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 // 62 const long kDefault = -1; 63 64 65 // 66 // Global Variables 67 // 68 short unget_buf[UNGET_MAX_COUNT+1]; 69 int unget_count; 70 char io_buffer[MAXIOSIZE]; 71 72 73 // 74 // Forward declarations 75 // 76 long get_number(int first_char); 77 char* get_string(int eos); 78 int my_getch(void); 79 void my_ungetch(int c); 80 81 82 // 83 // Routines 84 // 85 int 86 my_getch() 87 { 88 if (unget_count > 0) { 89 return (unget_buf[--unget_count]); 90 } else { 91 return (getc(stdin)); 92 } 93 } 94 95 96 void 97 my_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 110 void 111 flush_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 133 int 134 get_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 164 int 165 get_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 190 int 191 get_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 226 long 227 get_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 269 int 270 get_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 310 char * 311 get_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 359 unsigned long 360 get_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 403 int 404 get_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 422 int 423 number_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 // 439 void 440 bad_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