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