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