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