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