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