io.c revision 1.22
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
42short	unget_buf[UNGET_MAX_COUNT + 1];
43int	unget_count;
44
45long	get_number(int);
46char   *get_string(int);
47int	my_getch (void);
48
49int
50my_getch()
51{
52	if (unget_count > 0) {
53		return (unget_buf[--unget_count]);
54	} else {
55		return (getc(stdin));
56	}
57}
58
59
60void
61my_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
75void
76flush_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
98int
99get_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
128int
129get_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
153int
154get_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
182long
183get_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
221int
222get_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
256char           *
257get_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
302unsigned long
303get_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
345int
346get_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
363int
364number_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 */
380void
381bad_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