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