1 //
2 // io.c - simple io and input parsing routines
3 //
4 // Written by Eryk Vershen
5 //
6
7 /*
8 * Copyright 1996,1997,1998 by Apple Computer, Inc.
9 * All Rights Reserved
10 *
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appears in all copies and
14 * that both the copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE.
20 *
21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 */
27
28 // for *printf()
29 #include <stdio.h>
30
31 // for malloc() & free()
32 #if !defined(__linux__)
33 #include <stdlib.h>
34 #else
35 #include <malloc.h>
36 #endif
37 // for strncpy()
38 #include <string.h>
39 // for va_start(), etc.
40 #include <stdarg.h>
41 // for errno
42 #include <errno.h>
43
44 #include "io.h"
45 #include "errors.h"
46
47
48 //
49 // Defines
50 //
51 #define BAD_DIGIT 17 /* must be greater than any base */
52 #define STRING_CHUNK 16
53 #define UNGET_MAX_COUNT 10
54 #ifndef __linux__
55 #ifndef __unix__
56 #define SCSI_FD 8
57 #endif
58 #ifdef NeXT
59 #define loff_t off_t
60 #define llseek lseek
61 #else
62 #define loff_t long
63 #define llseek lseek
64 #endif
65 #endif
66
67
68 //
69 // Types
70 //
71
72
73 //
74 // Global Constants
75 //
76 const long kDefault = -1;
77
78
79 //
80 // Global Variables
81 //
82 short unget_buf[UNGET_MAX_COUNT+1];
83 int unget_count;
84 char io_buffer[MAXIOSIZE];
85
86
87 //
88 // Forward declarations
89 //
90 long get_number(int first_char);
91 char* get_string(int eos);
92 int my_getch(void);
93 void my_ungetch(int c);
94
95 //
96 // Routines
97 //
98 int
my_getch(void)99 my_getch(void)
100 {
101 if (unget_count > 0) {
102 return (unget_buf[--unget_count]);
103 } else {
104 return (getc(stdin));
105 }
106 }
107
108
109 void
my_ungetch(int c)110 my_ungetch(int c)
111 {
112 // In practice there is never more than one character in
113 // the unget_buf, but what's a little overkill among friends?
114
115 if (unget_count < UNGET_MAX_COUNT) {
116 unget_buf[unget_count++] = c;
117 } else {
118 fatal(-1, "Programmer error in my_ungetch().");
119 }
120 }
121
122
123 void
flush_to_newline(int keep_newline)124 flush_to_newline(int keep_newline)
125 {
126 int c;
127
128 for (;;) {
129 c = my_getch();
130
131 if (c <= 0) {
132 break;
133 } else if (c == '\n') {
134 if (keep_newline) {
135 my_ungetch(c);
136 }
137 break;
138 } else {
139 // skip
140 }
141 }
142 return;
143 }
144
145
146 int
get_okay(const char * prompt,int default_value)147 get_okay(const char *prompt, int default_value)
148 {
149 int c;
150
151 flush_to_newline(0);
152 printf("%s", prompt);
153
154 for (;;) {
155 c = my_getch();
156
157 if (c <= 0) {
158 break;
159 } else if (c == ' ' || c == '\t') {
160 // skip blanks and tabs
161 } else if (c == '\n') {
162 my_ungetch(c);
163 return default_value;
164 } else if (c == 'y' || c == 'Y') {
165 return 1;
166 } else if (c == 'n' || c == 'N') {
167 return 0;
168 } else {
169 flush_to_newline(0);
170 printf("%s", prompt);
171 }
172 }
173 return -1;
174 }
175
176
177 int
get_command(const char * prompt,int promptBeforeGet,int * command)178 get_command(const char *prompt, int promptBeforeGet, int *command)
179 {
180 int c;
181
182 if (promptBeforeGet) {
183 printf("%s", prompt);
184 }
185 for (;;) {
186 c = my_getch();
187
188 if (c <= 0) {
189 break;
190 } else if (c == ' ' || c == '\t') {
191 // skip blanks and tabs
192 } else if (c == '\n') {
193 printf("%s", prompt);
194 } else {
195 *command = c;
196 return 1;
197 }
198 }
199 return 0;
200 }
201
202
203 int
get_number_argument(const char * prompt,long * number,long default_value)204 get_number_argument(const char *prompt, long *number, long default_value)
205 {
206 int c;
207 int result = 0;
208
209 for (;;) {
210 c = my_getch();
211
212 if (c <= 0) {
213 break;
214 } else if (c == ' ' || c == '\t') {
215 // skip blanks and tabs
216 } else if (c == '\n') {
217 if (default_value == kDefault) {
218 printf("%s", prompt);
219 } else {
220 my_ungetch(c);
221 *number = default_value;
222 result = 1;
223 break;
224 }
225 } else if ('0' <= c && c <= '9') {
226 *number = get_number(c);
227 result = 1;
228 break;
229 } else {
230 my_ungetch(c);
231 *number = 0;
232 break;
233 }
234 }
235 return result;
236 }
237
238
239 long
get_number(int first_char)240 get_number(int first_char)
241 {
242 register int c;
243 int base;
244 int digit;
245 int ret_value;
246
247 if (first_char != '0') {
248 c = first_char;
249 base = 10;
250 digit = BAD_DIGIT;
251 } else if ((c=my_getch()) == 'x' || c == 'X') {
252 c = my_getch();
253 base = 16;
254 digit = BAD_DIGIT;
255 } else {
256 my_ungetch(c);
257 c = first_char;
258 base = 8;
259 digit = 0;
260 }
261 ret_value = 0;
262 for (ret_value = 0; ; c = my_getch()) {
263 if (c >= '0' && c <= '9') {
264 digit = c - '0';
265 } else if (c >='A' && c <= 'F') {
266 digit = 10 + (c - 'A');
267 } else if (c >='a' && c <= 'f') {
268 digit = 10 + (c - 'a');
269 } else {
270 digit = BAD_DIGIT;
271 }
272 if (digit >= base) {
273 break;
274 }
275 ret_value = ret_value * base + digit;
276 }
277 my_ungetch(c);
278 return(ret_value);
279 }
280
281
282 int
get_string_argument(const char * prompt,char ** string,int reprompt)283 get_string_argument(const char *prompt, char **string, int reprompt)
284 {
285 int c;
286 int result = 0;
287
288 for (;;) {
289 c = my_getch();
290
291 if (c <= 0) {
292 break;
293 } else if (c == ' ' || c == '\t') {
294 // skip blanks and tabs
295 } else if (c == '\n') {
296 if (reprompt) {
297 printf("%s", prompt);
298 } else {
299 my_ungetch(c);
300 *string = NULL;
301 break;
302 }
303 } else if (c == '"' || c == '\'') {
304 *string = get_string(c);
305 result = 1;
306 break;
307 } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
308 || (c == '-' || c == '/' || c == '.' || c == ':')) {
309 my_ungetch(c);
310 *string = get_string(' ');
311 result = 1;
312 break;
313 } else {
314 my_ungetch(c);
315 *string = NULL;
316 break;
317 }
318 }
319 return result;
320 }
321
322
323 char *
get_string(int eos)324 get_string(int eos)
325 {
326 int c;
327 char *s;
328 char *ret_value;
329 char *limit;
330 int length;
331 ptrdiff_t off;
332
333 ret_value = (char *) malloc(STRING_CHUNK);
334 if (ret_value == NULL) {
335 error(errno, "can't allocate memory for string buffer");
336 return NULL;
337 }
338 length = STRING_CHUNK;
339 limit = ret_value + length;
340
341 c = my_getch();
342 for (s = ret_value; ; c = my_getch()) {
343 if (s >= limit) {
344 // expand string
345 limit = (char *) malloc(length+STRING_CHUNK);
346 if (limit == NULL) {
347 error(errno, "can't allocate memory for string buffer");
348 ret_value[length-1] = 0;
349 break;
350 }
351 strncpy(limit, ret_value, length);
352 off = s - ret_value;
353 free(ret_value);
354 s = limit + off;
355 ret_value = limit;
356 length += STRING_CHUNK;
357 limit = ret_value + length;
358 }
359 if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
360 *s++ = 0;
361 break;
362 } else if (c == '\n') {
363 *s++ = 0;
364 my_ungetch(c);
365 break;
366 } else {
367 *s++ = c;
368 }
369 }
370 return(ret_value);
371 }
372
373
374 uint32_t
get_multiplier(long divisor)375 get_multiplier(long divisor)
376 {
377 int c;
378 uint32_t result;
379 uint32_t extra;
380
381 c = my_getch();
382
383 extra = 1;
384 if (c <= 0 || divisor <= 0) {
385 result = 0;
386 } else if (c == 't' || c == 'T') {
387 result = 1024*1024;
388 extra = 1024*1024;
389 } else if (c == 'g' || c == 'G') {
390 result = 1024*1024*1024;
391 } else if (c == 'm' || c == 'M') {
392 result = 1024*1024;
393 } else if (c == 'k' || c == 'K') {
394 result = 1024;
395 } else {
396 my_ungetch(c);
397 result = 1;
398 }
399 if (result > 1) {
400 if (extra > 1) {
401 result /= divisor;
402 if (result >= 4096) {
403 /* overflow -> 20bits + >12bits */
404 result = 0;
405 } else {
406 result *= extra;
407 }
408 } else if ((long long)result >= divisor) {
409 result /= divisor;
410 } else {
411 result = 1;
412 }
413 }
414 return result;
415 }
416
417
418 int
get_partition_modifier(void)419 get_partition_modifier(void)
420 {
421 int c;
422 int result;
423
424 result = 0;
425
426 c = my_getch();
427
428 if (c == 'p' || c == 'P') {
429 result = 1;
430 } else if (c > 0) {
431 my_ungetch(c);
432 }
433 return result;
434 }
435
436
437 int
number_of_digits(uint32_t value)438 number_of_digits(uint32_t value)
439 {
440 int j;
441
442 j = 1;
443 while (value > 9) {
444 j++;
445 value = value / 10;
446 }
447 return j;
448 }
449
450
451 //
452 // Print a message on standard error & flush the input.
453 //
454 void
bad_input(const char * fmt,...)455 bad_input(const char *fmt, ...)
456 {
457 va_list ap;
458
459 va_start(ap, fmt);
460 vfprintf(stderr, fmt, ap);
461 va_end(ap);
462 fprintf(stderr, "\n");
463 flush_to_newline(1);
464 }
465