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