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