xref: /openbsd-src/sbin/pdisk/io.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
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 #include <stdlib.h>
33 // for strncpy()
34 #include <string.h>
35 // for va_start(), etc.
36 #include <stdarg.h>
37 // for errno
38 #include <errno.h>
39 
40 #include "io.h"
41 #include "errors.h"
42 
43 
44 //
45 // Defines
46 //
47 #define BAD_DIGIT 17	/* must be greater than any base */
48 #define	STRING_CHUNK	16
49 #define UNGET_MAX_COUNT 10
50 #define loff_t long
51 #define llseek lseek
52 
53 
54 //
55 // Types
56 //
57 
58 
59 //
60 // Global Constants
61 //
62 const long kDefault = -1;
63 
64 
65 //
66 // Global Variables
67 //
68 short unget_buf[UNGET_MAX_COUNT+1];
69 int unget_count;
70 char io_buffer[MAXIOSIZE];
71 
72 
73 //
74 // Forward declarations
75 //
76 long get_number(int first_char);
77 char* get_string(int eos);
78 int my_getch(void);
79 void my_ungetch(int c);
80 
81 
82 //
83 // Routines
84 //
85 int
86 my_getch()
87 {
88     if (unget_count > 0) {
89 	return (unget_buf[--unget_count]);
90     } else {
91 	return (getc(stdin));
92     }
93 }
94 
95 
96 void
97 my_ungetch(int c)
98 {
99     // In practice there is never more than one character in
100     // the unget_buf, but what's a little overkill among friends?
101 
102     if (unget_count < UNGET_MAX_COUNT) {
103 	unget_buf[unget_count++] = c;
104     } else {
105 	fatal(-1, "Programmer error in my_ungetch().");
106     }
107 }
108 
109 
110 void
111 flush_to_newline(int keep_newline)
112 {
113     int		c;
114 
115     for (;;) {
116 	c = my_getch();
117 
118 	if (c <= 0) {
119 	    break;
120 	} else if (c == '\n') {
121 	    if (keep_newline) {
122 		my_ungetch(c);
123 	    }
124 	    break;
125 	} else {
126 	    // skip
127 	}
128     }
129     return;
130 }
131 
132 
133 int
134 get_okay(const char *prompt, int default_value)
135 {
136     int		c;
137 
138     flush_to_newline(0);
139     printf(prompt);
140 
141     for (;;) {
142 	c = my_getch();
143 
144 	if (c <= 0) {
145 	    break;
146 	} else if (c == ' ' || c == '\t') {
147 	    // skip blanks and tabs
148 	} else if (c == '\n') {
149 	    my_ungetch(c);
150 	    return default_value;
151 	} else if (c == 'y' || c == 'Y') {
152 	    return 1;
153 	} else if (c == 'n' || c == 'N') {
154 	    return 0;
155 	} else {
156 	    flush_to_newline(0);
157 	    printf(prompt);
158 	}
159     }
160     return -1;
161 }
162 
163 
164 int
165 get_command(const char *prompt, int promptBeforeGet, int *command)
166 {
167     int		c;
168 
169     if (promptBeforeGet) {
170 	printf(prompt);
171     }
172     for (;;) {
173 	c = my_getch();
174 
175 	if (c <= 0) {
176 	    break;
177 	} else if (c == ' ' || c == '\t') {
178 	    // skip blanks and tabs
179 	} else if (c == '\n') {
180 	    printf(prompt);
181 	} else {
182 	    *command = c;
183 	    return 1;
184 	}
185     }
186     return 0;
187 }
188 
189 
190 int
191 get_number_argument(const char *prompt, long *number, long default_value)
192 {
193     int c;
194     int result = 0;
195 
196     for (;;) {
197 	c = my_getch();
198 
199 	if (c <= 0) {
200 	    break;
201 	} else if (c == ' ' || c == '\t') {
202 	    // skip blanks and tabs
203 	} else if (c == '\n') {
204 	    if (default_value == kDefault) {
205 		printf(prompt);
206 	    } else {
207 		my_ungetch(c);
208 		*number = default_value;
209 		result = 1;
210 		break;
211 	    }
212 	} else if ('0' <= c && c <= '9') {
213 	    *number = get_number(c);
214 	    result = 1;
215 	    break;
216 	} else {
217 	    my_ungetch(c);
218 	    *number = 0;
219 	    break;
220 	}
221     }
222     return result;
223 }
224 
225 
226 long
227 get_number(int first_char)
228 {
229     int c;
230     int base;
231     int digit;
232     int ret_value;
233 
234     if (first_char != '0') {
235 	c = first_char;
236 	base = 10;
237 	digit = BAD_DIGIT;
238     } else if ((c=my_getch()) == 'x' || c == 'X') {
239 	c = my_getch();
240 	base = 16;
241 	digit = BAD_DIGIT;
242     } else {
243 	my_ungetch(c);
244 	c = first_char;
245 	base = 8;
246 	digit = 0;
247     }
248     ret_value = 0;
249     for (ret_value = 0; ; c = my_getch()) {
250 	if (c >= '0' && c <= '9') {
251 	    digit = c - '0';
252 	} else if (c >='A' && c <= 'F') {
253 	    digit = 10 + (c - 'A');
254 	} else if (c >='a' && c <= 'f') {
255 	    digit = 10 + (c - 'a');
256 	} else {
257 	    digit = BAD_DIGIT;
258 	}
259 	if (digit >= base) {
260 	    break;
261 	}
262 	ret_value = ret_value * base + digit;
263     }
264     my_ungetch(c);
265     return(ret_value);
266 }
267 
268 
269 int
270 get_string_argument(const char *prompt, char **string, int reprompt)
271 {
272     int c;
273     int result = 0;
274 
275     for (;;) {
276 	c = my_getch();
277 
278 	if (c <= 0) {
279 	    break;
280 	} else if (c == ' ' || c == '\t') {
281 	    // skip blanks and tabs
282 	} else if (c == '\n') {
283 	    if (reprompt) {
284 		printf(prompt);
285 	    } else {
286 		my_ungetch(c);
287 		*string = NULL;
288 		break;
289 	    }
290 	} else if (c == '"' || c == '\'') {
291 	    *string = get_string(c);
292 	    result = 1;
293 	    break;
294 	} else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
295 		|| (c == '-' || c == '/' || c == '.' || c == ':')) {
296 	    my_ungetch(c);
297 	    *string = get_string(' ');
298 	    result = 1;
299 	    break;
300 	} else {
301 	    my_ungetch(c);
302 	    *string = NULL;
303 	    break;
304 	}
305     }
306     return result;
307 }
308 
309 
310 char *
311 get_string(int eos)
312 {
313     int c;
314     char *s;
315     char *ret_value;
316     char *limit;
317     int length;
318 
319     ret_value = (char *) malloc(STRING_CHUNK);
320     if (ret_value == NULL) {
321 	error(errno, "can't allocate memory for string buffer");
322 	return NULL;
323     }
324     length = STRING_CHUNK;
325     limit = ret_value + length;
326 
327     c = my_getch();
328     for (s = ret_value; ; c = my_getch()) {
329 	if (s >= limit) {
330 	    // expand string
331 	    limit = (char *) malloc(length+STRING_CHUNK);
332 	    if (limit == NULL) {
333 		error(errno, "can't allocate memory for string buffer");
334 		ret_value[length-1] = 0;
335 		break;
336 	    }
337 	    strncpy(limit, ret_value, length);
338 	    free(ret_value);
339 	    s = limit + (s - ret_value);
340 	    ret_value = limit;
341 	    length += STRING_CHUNK;
342 	    limit = ret_value + length;
343 	}
344 	if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
345 	    *s++ = 0;
346 	    break;
347 	} else if (c == '\n') {
348 	    *s++ = 0;
349 	    my_ungetch(c);
350 	    break;
351 	} else {
352 	    *s++ = c;
353 	}
354     }
355     return(ret_value);
356 }
357 
358 
359 unsigned long
360 get_multiplier(long divisor)
361 {
362     int c;
363     unsigned long result;
364     unsigned long extra;
365 
366     c = my_getch();
367 
368     extra = 1;
369     if (c <= 0 || divisor <= 0) {
370 	result = 0;
371     } else if (c == 't' || c == 'T') {
372 	result = 1024*1024;
373 	extra = 1024*1024;
374     } else if (c == 'g' || c == 'G') {
375 	result = 1024*1024*1024;
376     } else if (c == 'm' || c == 'M') {
377 	result = 1024*1024;
378     } else if (c == 'k' || c == 'K') {
379 	result = 1024;
380     } else {
381 	my_ungetch(c);
382 	result = 1;
383     }
384     if (result > 1) {
385 	if (extra > 1) {
386 	    result /= divisor;
387 	    if (result >= 4096) {
388 		/* overflow -> 20bits + >12bits */
389 		result = 0;
390 	    } else {
391 		result *= extra;
392 	    }
393 	} else if (result >= divisor) {
394 	    result /= divisor;
395 	} else {
396 	    result = 1;
397 	}
398     }
399     return result;
400 }
401 
402 
403 int
404 get_partition_modifier(void)
405 {
406     int c;
407     int result;
408 
409     result = 0;
410 
411     c = my_getch();
412 
413     if (c == 'p' || c == 'P') {
414     	result = 1;
415     } else if (c > 0) {
416 	my_ungetch(c);
417     }
418     return result;
419 }
420 
421 
422 int
423 number_of_digits(unsigned long value)
424 {
425     int j;
426 
427     j = 1;
428     while (value > 9) {
429 	j++;
430 	value = value / 10;
431     }
432     return j;
433 }
434 
435 
436 //
437 // Print a message on standard error & flush the input.
438 //
439 void
440 bad_input(const char *fmt, ...)
441 {
442     va_list ap;
443 
444     va_start(ap, fmt);
445     vfprintf(stderr, fmt, ap);
446     va_end(ap);
447     fprintf(stderr, "\n");
448     flush_to_newline(1);
449 }
450