xref: /openbsd-src/sbin/pdisk/io.c (revision fb04e59a379471c29bf11cd0a1645ce90e80fe31)
1 /*	$OpenBSD: io.c,v 1.9 2016/01/11 07:54:07 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 
112 void
113 flush_to_newline(int keep_newline)
114 {
115     int		c;
116 
117     for (;;) {
118 	c = my_getch();
119 
120 	if (c <= 0) {
121 	    break;
122 	} else if (c == '\n') {
123 	    if (keep_newline) {
124 		my_ungetch(c);
125 	    }
126 	    break;
127 	} else {
128 	    // skip
129 	}
130     }
131     return;
132 }
133 
134 
135 int
136 get_okay(const char *prompt, int default_value)
137 {
138     int		c;
139 
140     flush_to_newline(0);
141     printf(prompt);
142 
143     for (;;) {
144 	c = my_getch();
145 
146 	if (c <= 0) {
147 	    break;
148 	} else if (c == ' ' || c == '\t') {
149 	    // skip blanks and tabs
150 	} else if (c == '\n') {
151 	    my_ungetch(c);
152 	    return default_value;
153 	} else if (c == 'y' || c == 'Y') {
154 	    return 1;
155 	} else if (c == 'n' || c == 'N') {
156 	    return 0;
157 	} else {
158 	    flush_to_newline(0);
159 	    printf(prompt);
160 	}
161     }
162     return -1;
163 }
164 
165 
166 int
167 get_command(const char *prompt, int promptBeforeGet, int *command)
168 {
169     int		c;
170 
171     if (promptBeforeGet) {
172 	printf(prompt);
173     }
174     for (;;) {
175 	c = my_getch();
176 
177 	if (c <= 0) {
178 	    break;
179 	} else if (c == ' ' || c == '\t') {
180 	    // skip blanks and tabs
181 	} else if (c == '\n') {
182 	    printf(prompt);
183 	} else {
184 	    *command = c;
185 	    return 1;
186 	}
187     }
188     return 0;
189 }
190 
191 
192 int
193 get_number_argument(const char *prompt, long *number, long default_value)
194 {
195     int c;
196     int result = 0;
197 
198     for (;;) {
199 	c = my_getch();
200 
201 	if (c <= 0) {
202 	    break;
203 	} else if (c == ' ' || c == '\t') {
204 	    // skip blanks and tabs
205 	} else if (c == '\n') {
206 	    if (default_value == kDefault) {
207 		printf(prompt);
208 	    } else {
209 		my_ungetch(c);
210 		*number = default_value;
211 		result = 1;
212 		break;
213 	    }
214 	} else if ('0' <= c && c <= '9') {
215 	    *number = get_number(c);
216 	    result = 1;
217 	    break;
218 	} else {
219 	    my_ungetch(c);
220 	    *number = 0;
221 	    break;
222 	}
223     }
224     return result;
225 }
226 
227 
228 long
229 get_number(int first_char)
230 {
231     int c;
232     int base;
233     int digit;
234     int ret_value;
235 
236     if (first_char != '0') {
237 	c = first_char;
238 	base = 10;
239 	digit = BAD_DIGIT;
240     } else if ((c=my_getch()) == 'x' || c == 'X') {
241 	c = my_getch();
242 	base = 16;
243 	digit = BAD_DIGIT;
244     } else {
245 	my_ungetch(c);
246 	c = first_char;
247 	base = 8;
248 	digit = 0;
249     }
250     ret_value = 0;
251     for (ret_value = 0; ; c = my_getch()) {
252 	if (c >= '0' && c <= '9') {
253 	    digit = c - '0';
254 	} else if (c >='A' && c <= 'F') {
255 	    digit = 10 + (c - 'A');
256 	} else if (c >='a' && c <= 'f') {
257 	    digit = 10 + (c - 'a');
258 	} else {
259 	    digit = BAD_DIGIT;
260 	}
261 	if (digit >= base) {
262 	    break;
263 	}
264 	ret_value = ret_value * base + digit;
265     }
266     my_ungetch(c);
267     return(ret_value);
268 }
269 
270 
271 int
272 get_string_argument(const char *prompt, char **string, int reprompt)
273 {
274     int c;
275     int result = 0;
276 
277     for (;;) {
278 	c = my_getch();
279 
280 	if (c <= 0) {
281 	    break;
282 	} else if (c == ' ' || c == '\t') {
283 	    // skip blanks and tabs
284 	} else if (c == '\n') {
285 	    if (reprompt) {
286 		printf(prompt);
287 	    } else {
288 		my_ungetch(c);
289 		*string = NULL;
290 		break;
291 	    }
292 	} else if (c == '"' || c == '\'') {
293 	    *string = get_string(c);
294 	    result = 1;
295 	    break;
296 	} else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
297 		|| (c == '-' || c == '/' || c == '.' || c == ':')) {
298 	    my_ungetch(c);
299 	    *string = get_string(' ');
300 	    result = 1;
301 	    break;
302 	} else {
303 	    my_ungetch(c);
304 	    *string = NULL;
305 	    break;
306 	}
307     }
308     return result;
309 }
310 
311 
312 char *
313 get_string(int eos)
314 {
315     int c;
316     char *s;
317     char *ret_value;
318     char *limit;
319     int length;
320 
321     ret_value = malloc(STRING_CHUNK);
322     if (ret_value == NULL) {
323 	error(errno, "can't allocate memory for string buffer");
324 	return NULL;
325     }
326     length = STRING_CHUNK;
327     limit = ret_value + length;
328 
329     c = my_getch();
330     for (s = ret_value; ; c = my_getch()) {
331 	if (s >= limit) {
332 	    // expand string
333 	    limit = malloc(length+STRING_CHUNK);
334 	    if (limit == NULL) {
335 		error(errno, "can't allocate memory for string buffer");
336 		ret_value[length-1] = 0;
337 		break;
338 	    }
339 	    strncpy(limit, ret_value, length);
340 	    free(ret_value);
341 	    s = limit + (s - ret_value);
342 	    ret_value = limit;
343 	    length += STRING_CHUNK;
344 	    limit = ret_value + length;
345 	}
346 	if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
347 	    *s++ = 0;
348 	    break;
349 	} else if (c == '\n') {
350 	    *s++ = 0;
351 	    my_ungetch(c);
352 	    break;
353 	} else {
354 	    *s++ = c;
355 	}
356     }
357     return(ret_value);
358 }
359 
360 
361 unsigned long
362 get_multiplier(long divisor)
363 {
364     int c;
365     unsigned long result;
366     unsigned long extra;
367 
368     c = my_getch();
369 
370     extra = 1;
371     if (c <= 0 || divisor <= 0) {
372 	result = 0;
373     } else if (c == 't' || c == 'T') {
374 	result = 1024*1024;
375 	extra = 1024*1024;
376     } else if (c == 'g' || c == 'G') {
377 	result = 1024*1024*1024;
378     } else if (c == 'm' || c == 'M') {
379 	result = 1024*1024;
380     } else if (c == 'k' || c == 'K') {
381 	result = 1024;
382     } else {
383 	my_ungetch(c);
384 	result = 1;
385     }
386     if (result > 1) {
387 	if (extra > 1) {
388 	    result /= divisor;
389 	    if (result >= 4096) {
390 		/* overflow -> 20bits + >12bits */
391 		result = 0;
392 	    } else {
393 		result *= extra;
394 	    }
395 	} else if (result >= divisor) {
396 	    result /= divisor;
397 	} else {
398 	    result = 1;
399 	}
400     }
401     return result;
402 }
403 
404 
405 int
406 get_partition_modifier(void)
407 {
408     int c;
409     int result;
410 
411     result = 0;
412 
413     c = my_getch();
414 
415     if (c == 'p' || c == 'P') {
416     	result = 1;
417     } else if (c > 0) {
418 	my_ungetch(c);
419     }
420     return result;
421 }
422 
423 
424 int
425 number_of_digits(unsigned long value)
426 {
427     int j;
428 
429     j = 1;
430     while (value > 9) {
431 	j++;
432 	value = value / 10;
433     }
434     return j;
435 }
436 
437 
438 //
439 // Print a message on standard error & flush the input.
440 //
441 void
442 bad_input(const char *fmt, ...)
443 {
444     va_list ap;
445 
446     va_start(ap, fmt);
447     vfprintf(stderr, fmt, ap);
448     va_end(ap);
449     fprintf(stderr, "\n");
450     flush_to_newline(1);
451 }
452