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