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