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