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