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