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