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