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