io.c revision 1.7
1//
2// io.c - simple io and input parsing routines
3//
4// Written by Eryk Vershen
5//
6
7/*
8 * Copyright 1996,1997,1998 by Apple Computer, Inc.
9 *              All Rights Reserved
10 *
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appears in all copies and
14 * that both the copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE.
20 *
21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 */
27
28// for *printf()
29#include <stdio.h>
30
31// for malloc() & free()
32#include <stdlib.h>
33// for strncpy()
34#include <string.h>
35// for va_start(), etc.
36#include <stdarg.h>
37// for errno
38#include <errno.h>
39
40#include "io.h"
41#include "errors.h"
42
43
44//
45// Defines
46//
47#define BAD_DIGIT 17	/* must be greater than any base */
48#define	STRING_CHUNK	16
49#define UNGET_MAX_COUNT 10
50#define loff_t long
51#define llseek lseek
52
53
54//
55// Types
56//
57
58
59//
60// Global Constants
61//
62const long kDefault = -1;
63
64
65//
66// Global Variables
67//
68short unget_buf[UNGET_MAX_COUNT+1];
69int unget_count;
70char io_buffer[MAXIOSIZE];
71
72
73//
74// Forward declarations
75//
76long get_number(int first_char);
77char* get_string(int eos);
78int my_getch(void);
79void my_ungetch(int c);
80
81
82//
83// Routines
84//
85int
86my_getch()
87{
88    if (unget_count > 0) {
89	return (unget_buf[--unget_count]);
90    } else {
91	return (getc(stdin));
92    }
93}
94
95
96void
97my_ungetch(int c)
98{
99    // In practice there is never more than one character in
100    // the unget_buf, but what's a little overkill among friends?
101
102    if (unget_count < UNGET_MAX_COUNT) {
103	unget_buf[unget_count++] = c;
104    } else {
105	fatal(-1, "Programmer error in my_ungetch().");
106    }
107}
108
109
110void
111flush_to_newline(int keep_newline)
112{
113    int		c;
114
115    for (;;) {
116	c = my_getch();
117
118	if (c <= 0) {
119	    break;
120	} else if (c == '\n') {
121	    if (keep_newline) {
122		my_ungetch(c);
123	    }
124	    break;
125	} else {
126	    // skip
127	}
128    }
129    return;
130}
131
132
133int
134get_okay(const char *prompt, int default_value)
135{
136    int		c;
137
138    flush_to_newline(0);
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	    my_ungetch(c);
150	    return default_value;
151	} else if (c == 'y' || c == 'Y') {
152	    return 1;
153	} else if (c == 'n' || c == 'N') {
154	    return 0;
155	} else {
156	    flush_to_newline(0);
157	    printf(prompt);
158	}
159    }
160    return -1;
161}
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
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
268
269int
270get_string_argument(const char *prompt, char **string, int reprompt)
271{
272    int c;
273    int result = 0;
274
275    for (;;) {
276	c = my_getch();
277
278	if (c <= 0) {
279	    break;
280	} else if (c == ' ' || c == '\t') {
281	    // skip blanks and tabs
282	} else if (c == '\n') {
283	    if (reprompt) {
284		printf(prompt);
285	    } else {
286		my_ungetch(c);
287		*string = NULL;
288		break;
289	    }
290	} else if (c == '"' || c == '\'') {
291	    *string = get_string(c);
292	    result = 1;
293	    break;
294	} else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
295		|| (c == '-' || c == '/' || c == '.' || c == ':')) {
296	    my_ungetch(c);
297	    *string = get_string(' ');
298	    result = 1;
299	    break;
300	} else {
301	    my_ungetch(c);
302	    *string = NULL;
303	    break;
304	}
305    }
306    return result;
307}
308
309
310char *
311get_string(int eos)
312{
313    int c;
314    char *s;
315    char *ret_value;
316    char *limit;
317    int length;
318
319    ret_value = (char *) malloc(STRING_CHUNK);
320    if (ret_value == NULL) {
321	error(errno, "can't allocate memory for string buffer");
322	return NULL;
323    }
324    length = STRING_CHUNK;
325    limit = ret_value + length;
326
327    c = my_getch();
328    for (s = ret_value; ; c = my_getch()) {
329	if (s >= limit) {
330	    // expand string
331	    limit = (char *) malloc(length+STRING_CHUNK);
332	    if (limit == NULL) {
333		error(errno, "can't allocate memory for string buffer");
334		ret_value[length-1] = 0;
335		break;
336	    }
337	    strncpy(limit, ret_value, length);
338	    free(ret_value);
339	    s = limit + (s - ret_value);
340	    ret_value = limit;
341	    length += STRING_CHUNK;
342	    limit = ret_value + length;
343	}
344	if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) {
345	    *s++ = 0;
346	    break;
347	} else if (c == '\n') {
348	    *s++ = 0;
349	    my_ungetch(c);
350	    break;
351	} else {
352	    *s++ = c;
353	}
354    }
355    return(ret_value);
356}
357
358
359unsigned long
360get_multiplier(long divisor)
361{
362    int c;
363    unsigned long result;
364    unsigned long extra;
365
366    c = my_getch();
367
368    extra = 1;
369    if (c <= 0 || divisor <= 0) {
370	result = 0;
371    } else if (c == 't' || c == 'T') {
372	result = 1024*1024;
373	extra = 1024*1024;
374    } else if (c == 'g' || c == 'G') {
375	result = 1024*1024*1024;
376    } else if (c == 'm' || c == 'M') {
377	result = 1024*1024;
378    } else if (c == 'k' || c == 'K') {
379	result = 1024;
380    } else {
381	my_ungetch(c);
382	result = 1;
383    }
384    if (result > 1) {
385	if (extra > 1) {
386	    result /= divisor;
387	    if (result >= 4096) {
388		/* overflow -> 20bits + >12bits */
389		result = 0;
390	    } else {
391		result *= extra;
392	    }
393	} else if (result >= divisor) {
394	    result /= divisor;
395	} else {
396	    result = 1;
397	}
398    }
399    return result;
400}
401
402
403int
404get_partition_modifier(void)
405{
406    int c;
407    int result;
408
409    result = 0;
410
411    c = my_getch();
412
413    if (c == 'p' || c == 'P') {
414    	result = 1;
415    } else if (c > 0) {
416	my_ungetch(c);
417    }
418    return result;
419}
420
421
422int
423number_of_digits(unsigned long value)
424{
425    int j;
426
427    j = 1;
428    while (value > 9) {
429	j++;
430	value = value / 10;
431    }
432    return j;
433}
434
435
436//
437// Print a message on standard error & flush the input.
438//
439void
440bad_input(const char *fmt, ...)
441{
442    va_list ap;
443
444    va_start(ap, fmt);
445    vfprintf(stderr, fmt, ap);
446    va_end(ap);
447    fprintf(stderr, "\n");
448    flush_to_newline(1);
449}
450