io.c revision 1.23
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
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
59void
60my_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
72void
73flush_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
95int
96get_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
125int
126get_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
150int
151get_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
179long
180get_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
218int
219get_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
253char *
254get_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
299unsigned long
300get_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
342int
343get_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
360int
361number_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 */
377void
378bad_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