xref: /netbsd-src/external/bsd/pdisk/dist/io.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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
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
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
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
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
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
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
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
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 *
324 get_string(int eos)
325 {
326     int c;
327     char *s;
328     char *ret_value;
329     char *limit;
330     int length;
331 
332     ret_value = (char *) malloc(STRING_CHUNK);
333     if (ret_value == NULL) {
334 	error(errno, "can't allocate memory for string buffer");
335 	return NULL;
336     }
337     length = STRING_CHUNK;
338     limit = ret_value + length;
339 
340     c = my_getch();
341     for (s = ret_value; ; c = my_getch()) {
342 	if (s >= limit) {
343 	    // expand string
344 	    limit = (char *) malloc(length+STRING_CHUNK);
345 	    if (limit == NULL) {
346 		error(errno, "can't allocate memory for string buffer");
347 		ret_value[length-1] = 0;
348 		break;
349 	    }
350 	    strncpy(limit, ret_value, length);
351 	    free(ret_value);
352 	    s = limit + (s - ret_value);
353 	    ret_value = limit;
354 	    length += STRING_CHUNK;
355 	    limit = ret_value + length;
356 	}
357 	if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
358 	    *s++ = 0;
359 	    break;
360 	} else if (c == '\n') {
361 	    *s++ = 0;
362 	    my_ungetch(c);
363 	    break;
364 	} else {
365 	    *s++ = c;
366 	}
367     }
368     return(ret_value);
369 }
370 
371 
372 uint32_t
373 get_multiplier(long divisor)
374 {
375     int c;
376     uint32_t result;
377     uint32_t extra;
378 
379     c = my_getch();
380 
381     extra = 1;
382     if (c <= 0 || divisor <= 0) {
383 	result = 0;
384     } else if (c == 't' || c == 'T') {
385 	result = 1024*1024;
386 	extra = 1024*1024;
387     } else if (c == 'g' || c == 'G') {
388 	result = 1024*1024*1024;
389     } else if (c == 'm' || c == 'M') {
390 	result = 1024*1024;
391     } else if (c == 'k' || c == 'K') {
392 	result = 1024;
393     } else {
394 	my_ungetch(c);
395 	result = 1;
396     }
397     if (result > 1) {
398 	if (extra > 1) {
399 	    result /= divisor;
400 	    if (result >= 4096) {
401 		/* overflow -> 20bits + >12bits */
402 		result = 0;
403 	    } else {
404 		result *= extra;
405 	    }
406 	} else if ((long long)result >= divisor) {
407 	    result /= divisor;
408 	} else {
409 	    result = 1;
410 	}
411     }
412     return result;
413 }
414 
415 
416 int
417 get_partition_modifier(void)
418 {
419     int c;
420     int result;
421 
422     result = 0;
423 
424     c = my_getch();
425 
426     if (c == 'p' || c == 'P') {
427     	result = 1;
428     } else if (c > 0) {
429 	my_ungetch(c);
430     }
431     return result;
432 }
433 
434 
435 int
436 number_of_digits(uint32_t value)
437 {
438     int j;
439 
440     j = 1;
441     while (value > 9) {
442 	j++;
443 	value = value / 10;
444     }
445     return j;
446 }
447 
448 
449 //
450 // Print a message on standard error & flush the input.
451 //
452 void
453 bad_input(const char *fmt, ...)
454 {
455     va_list ap;
456 
457     va_start(ap, fmt);
458     vfprintf(stderr, fmt, ap);
459     va_end(ap);
460     fprintf(stderr, "\n");
461     flush_to_newline(1);
462 }
463