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