xref: /openbsd-src/sbin/pdisk/io.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 //
2 // io.c - simple io and input parsing routines
3 //
4 // Written by Eryk Vershen (eryk@apple.com)
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__) && !defined(__unix__) || defined(__OpenBSD__)
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();
93 void my_ungetch(int c);
94 
95 
96 //
97 // Routines
98 //
99 int
100 my_getch()
101 {
102     if (unget_count > 0) {
103 	return (unget_buf[--unget_count]);
104     } else {
105 	return (getc(stdin));
106     }
107 }
108 
109 
110 void
111 my_ungetch(int c)
112 {
113     // In practice there is never more than one character in
114     // the unget_buf, but what's a little overkill among friends?
115 
116     if (unget_count < UNGET_MAX_COUNT) {
117 	unget_buf[unget_count++] = c;
118     } else {
119 	fatal(-1, "Programmer error in my_ungetch().");
120     }
121 }
122 
123 
124 void
125 flush_to_newline(int keep_newline)
126 {
127     int		c;
128 
129     for (;;) {
130 	c = my_getch();
131 
132 	if (c <= 0) {
133 	    break;
134 	} else if (c == '\n') {
135 	    if (keep_newline) {
136 		my_ungetch(c);
137 	    }
138 	    break;
139 	} else {
140 	    // skip
141 	}
142     }
143     return;
144 }
145 
146 
147 int
148 get_okay(char *prompt, int default_value)
149 {
150     int		c;
151 
152     flush_to_newline(0);
153     printf(prompt);
154 
155     for (;;) {
156 	c = my_getch();
157 
158 	if (c <= 0) {
159 	    break;
160 	} else if (c == ' ' || c == '\t') {
161 	    // skip blanks and tabs
162 	} else if (c == '\n') {
163 	    my_ungetch(c);
164 	    return default_value;
165 	} else if (c == 'y' || c == 'Y') {
166 	    return 1;
167 	} else if (c == 'n' || c == 'N') {
168 	    return 0;
169 	} else {
170 	    flush_to_newline(0);
171 	    printf(prompt);
172 	}
173     }
174     return -1;
175 }
176 
177 
178 int
179 get_command(char *prompt, int promptBeforeGet, int *command)
180 {
181     int		c;
182 
183     if (promptBeforeGet) {
184 	printf(prompt);
185     }
186     for (;;) {
187 	c = my_getch();
188 
189 	if (c <= 0) {
190 	    break;
191 	} else if (c == ' ' || c == '\t') {
192 	    // skip blanks and tabs
193 	} else if (c == '\n') {
194 	    printf(prompt);
195 	} else {
196 	    *command = c;
197 	    return 1;
198 	}
199     }
200     return 0;
201 }
202 
203 
204 int
205 get_number_argument(char *prompt, long *number, long default_value)
206 {
207     int c;
208     int result = 0;
209 
210     for (;;) {
211 	c = my_getch();
212 
213 	if (c <= 0) {
214 	    break;
215 	} else if (c == ' ' || c == '\t') {
216 	    // skip blanks and tabs
217 	} else if (c == '\n') {
218 	    if (default_value == kDefault) {
219 		printf(prompt);
220 	    } else {
221 		my_ungetch(c);
222 		*number = default_value;
223 		result = 1;
224 		break;
225 	    }
226 	} else if ('0' <= c && c <= '9') {
227 	    *number = get_number(c);
228 	    result = 1;
229 	    break;
230 	} else {
231 	    my_ungetch(c);
232 	    *number = 0;
233 	    break;
234 	}
235     }
236     return result;
237 }
238 
239 
240 long
241 get_number(int first_char)
242 {
243     register int c;
244     int base;
245     int digit;
246     int ret_value;
247 
248     if (first_char != '0') {
249 	c = first_char;
250 	base = 10;
251 	digit = BAD_DIGIT;
252     } else if ((c=my_getch()) == 'x' || c == 'X') {
253 	c = my_getch();
254 	base = 16;
255 	digit = BAD_DIGIT;
256     } else {
257 	my_ungetch(c);
258 	c = first_char;
259 	base = 8;
260 	digit = 0;
261     }
262     ret_value = 0;
263     for (ret_value = 0; ; c = my_getch()) {
264 	if (c >= '0' && c <= '9') {
265 	    digit = c - '0';
266 	} else if (c >='A' && c <= 'F') {
267 	    digit = 10 + (c - 'A');
268 	} else if (c >='a' && c <= 'f') {
269 	    digit = 10 + (c - 'a');
270 	} else {
271 	    digit = BAD_DIGIT;
272 	}
273 	if (digit >= base) {
274 	    break;
275 	}
276 	ret_value = ret_value * base + digit;
277     }
278     my_ungetch(c);
279     return(ret_value);
280 }
281 
282 
283 int
284 get_string_argument(char *prompt, char **string, int reprompt)
285 {
286     int c;
287     int result = 0;
288 
289     for (;;) {
290 	c = my_getch();
291 
292 	if (c <= 0) {
293 	    break;
294 	} else if (c == ' ' || c == '\t') {
295 	    // skip blanks and tabs
296 	} else if (c == '\n') {
297 	    if (reprompt) {
298 		printf(prompt);
299 	    } else {
300 		my_ungetch(c);
301 		*string = NULL;
302 		break;
303 	    }
304 	} else if (c == '"' || c == '\'') {
305 	    *string = get_string(c);
306 	    result = 1;
307 	    break;
308 	} else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
309 		|| (c == '-' || c == '/' || c == '.' || c == ':')) {
310 	    my_ungetch(c);
311 	    *string = get_string(' ');
312 	    result = 1;
313 	    break;
314 	} else {
315 	    my_ungetch(c);
316 	    *string = NULL;
317 	    break;
318 	}
319     }
320     return result;
321 }
322 
323 
324 char *
325 get_string(int eos)
326 {
327     int c;
328     char *s;
329     char *ret_value;
330     char *limit;
331     int length;
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 	    free(ret_value);
353 	    s = limit + (s - ret_value);
354 	    ret_value = limit;
355 	    length += STRING_CHUNK;
356 	    limit = ret_value + length;
357 	}
358 	if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
359 	    *s++ = 0;
360 	    break;
361 	} else if (c == '\n') {
362 	    *s++ = 0;
363 	    my_ungetch(c);
364 	    break;
365 	} else {
366 	    *s++ = c;
367 	}
368     }
369     return(ret_value);
370 }
371 
372 
373 unsigned long
374 get_multiplier(long divisor)
375 {
376     int c;
377     unsigned long result;
378     unsigned long extra;
379 
380     c = my_getch();
381 
382     extra = 1;
383     if (c <= 0 || divisor <= 0) {
384 	result = 0;
385     } else if (c == 't' || c == 'T') {
386 	result = 1024*1024;
387 	extra = 1024*1024;
388     } else if (c == 'g' || c == 'G') {
389 	result = 1024*1024*1024;
390     } else if (c == 'm' || c == 'M') {
391 	result = 1024*1024;
392     } else if (c == 'k' || c == 'K') {
393 	result = 1024;
394     } else {
395 	my_ungetch(c);
396 	result = 1;
397     }
398     if (result > 1) {
399 	if (extra > 1) {
400 	    result /= divisor;
401 	    if (result >= 4096) {
402 		/* overflow -> 20bits + >12bits */
403 		result = 0;
404 	    } else {
405 		result *= extra;
406 	    }
407 	} else if (result >= divisor) {
408 	    result /= divisor;
409 	} else {
410 	    result = 1;
411 	}
412     }
413     return result;
414 }
415 
416 
417 int
418 get_partition_modifier(void)
419 {
420     int c;
421     int result;
422 
423     result = 0;
424 
425     c = my_getch();
426 
427     if (c == 'p' || c == 'P') {
428     	result = 1;
429     } else if (c > 0) {
430 	my_ungetch(c);
431     }
432     return result;
433 }
434 
435 
436 int
437 number_of_digits(unsigned long value)
438 {
439     int j;
440 
441     j = 1;
442     while (value > 9) {
443 	j++;
444 	value = value / 10;
445     }
446     return j;
447 }
448 
449 
450 //
451 // Print a message on standard error & flush the input.
452 //
453 void
454 bad_input(char *fmt, ...)
455 {
456     va_list ap;
457 
458     va_start(ap, fmt);
459     vfprintf(stderr, fmt, ap);
460     va_end(ap);
461     fprintf(stderr, "\n");
462     flush_to_newline(1);
463 }
464