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