io.c revision 1.11
1/* $OpenBSD: io.c,v 1.11 2016/01/12 20:09:39 krw 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#include <err.h> 31 32// for *printf() 33#include <stdio.h> 34 35// for malloc() & free() 36#include <stdlib.h> 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 46 47// 48// Defines 49// 50#define BAD_DIGIT 17 /* must be greater than any base */ 51#define STRING_CHUNK 16 52#define UNGET_MAX_COUNT 10 53#define loff_t long 54#define llseek lseek 55 56 57// 58// Types 59// 60 61 62// 63// Global Constants 64// 65const long kDefault = -1; 66 67 68// 69// Global Variables 70// 71short unget_buf[UNGET_MAX_COUNT+1]; 72int unget_count; 73char io_buffer[MAXIOSIZE]; 74 75 76// 77// Forward declarations 78// 79long get_number(int first_char); 80char* get_string(int eos); 81int my_getch(void); 82void my_ungetch(int c); 83 84 85// 86// Routines 87// 88int 89my_getch() 90{ 91 if (unget_count > 0) { 92 return (unget_buf[--unget_count]); 93 } else { 94 return (getc(stdin)); 95 } 96} 97 98 99void 100my_ungetch(int c) 101{ 102 // In practice there is never more than one character in 103 // the unget_buf, but what's a little overkill among friends? 104 105 if (unget_count < UNGET_MAX_COUNT) { 106 unget_buf[unget_count++] = c; 107 } else { 108 errx(1, "Programmer error in my_ungetch()."); 109 } 110} 111 112void 113flush_to_newline(int keep_newline) 114{ 115 int c; 116 117 for (;;) { 118 c = my_getch(); 119 120 if (c <= 0) { 121 break; 122 } else if (c == '\n') { 123 if (keep_newline) { 124 my_ungetch(c); 125 } 126 break; 127 } else { 128 // skip 129 } 130 } 131 return; 132} 133 134 135int 136get_okay(const char *prompt, int default_value) 137{ 138 int c; 139 140 flush_to_newline(0); 141 printf(prompt); 142 143 for (;;) { 144 c = my_getch(); 145 146 if (c <= 0) { 147 break; 148 } else if (c == ' ' || c == '\t') { 149 // skip blanks and tabs 150 } else if (c == '\n') { 151 my_ungetch(c); 152 return default_value; 153 } else if (c == 'y' || c == 'Y') { 154 return 1; 155 } else if (c == 'n' || c == 'N') { 156 return 0; 157 } else { 158 flush_to_newline(0); 159 printf(prompt); 160 } 161 } 162 return -1; 163} 164 165int 166get_command(const char *prompt, int promptBeforeGet, int *command) 167{ 168 int c; 169 170 if (promptBeforeGet) { 171 printf(prompt); 172 } 173 for (;;) { 174 c = my_getch(); 175 176 if (c <= 0) { 177 break; 178 } else if (c == ' ' || c == '\t') { 179 // skip blanks and tabs 180 } else if (c == '\n') { 181 printf(prompt); 182 } else { 183 *command = c; 184 return 1; 185 } 186 } 187 return 0; 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 268int 269get_string_argument(const char *prompt, char **string, int reprompt) 270{ 271 int c; 272 int result = 0; 273 274 for (;;) { 275 c = my_getch(); 276 277 if (c <= 0) { 278 break; 279 } else if (c == ' ' || c == '\t') { 280 // skip blanks and tabs 281 } else if (c == '\n') { 282 if (reprompt) { 283 printf(prompt); 284 } else { 285 my_ungetch(c); 286 *string = NULL; 287 break; 288 } 289 } else if (c == '"' || c == '\'') { 290 *string = get_string(c); 291 result = 1; 292 break; 293 } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 294 || (c == '-' || c == '/' || c == '.' || c == ':')) { 295 my_ungetch(c); 296 *string = get_string(' '); 297 result = 1; 298 break; 299 } else { 300 my_ungetch(c); 301 *string = NULL; 302 break; 303 } 304 } 305 return result; 306} 307 308 309char * 310get_string(int eos) 311{ 312 int c; 313 char *s; 314 char *ret_value; 315 char *limit; 316 int length; 317 318 ret_value = malloc(STRING_CHUNK); 319 if (ret_value == NULL) { 320 warn("can't allocate memory for string buffer"); 321 return NULL; 322 } 323 length = STRING_CHUNK; 324 limit = ret_value + length; 325 326 c = my_getch(); 327 for (s = ret_value; ; c = my_getch()) { 328 if (s >= limit) { 329 // expand string 330 limit = malloc(length+STRING_CHUNK); 331 if (limit == NULL) { 332 warn("can't allocate memory for string buffer"); 333 ret_value[length-1] = 0; 334 break; 335 } 336 strncpy(limit, ret_value, length); 337 free(ret_value); 338 s = limit + (s - ret_value); 339 ret_value = limit; 340 length += STRING_CHUNK; 341 limit = ret_value + length; 342 } 343 if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) { 344 *s++ = 0; 345 break; 346 } else if (c == '\n') { 347 *s++ = 0; 348 my_ungetch(c); 349 break; 350 } else { 351 *s++ = c; 352 } 353 } 354 return(ret_value); 355} 356 357 358unsigned long 359get_multiplier(long divisor) 360{ 361 int c; 362 unsigned long result; 363 unsigned long extra; 364 365 c = my_getch(); 366 367 extra = 1; 368 if (c <= 0 || divisor <= 0) { 369 result = 0; 370 } else if (c == 't' || c == 'T') { 371 result = 1024*1024; 372 extra = 1024*1024; 373 } else if (c == 'g' || c == 'G') { 374 result = 1024*1024*1024; 375 } else if (c == 'm' || c == 'M') { 376 result = 1024*1024; 377 } else if (c == 'k' || c == 'K') { 378 result = 1024; 379 } else { 380 my_ungetch(c); 381 result = 1; 382 } 383 if (result > 1) { 384 if (extra > 1) { 385 result /= divisor; 386 if (result >= 4096) { 387 /* overflow -> 20bits + >12bits */ 388 result = 0; 389 } else { 390 result *= extra; 391 } 392 } else if (result >= divisor) { 393 result /= divisor; 394 } else { 395 result = 1; 396 } 397 } 398 return result; 399} 400 401 402int 403get_partition_modifier(void) 404{ 405 int c; 406 int result; 407 408 result = 0; 409 410 c = my_getch(); 411 412 if (c == 'p' || c == 'P') { 413 result = 1; 414 } else if (c > 0) { 415 my_ungetch(c); 416 } 417 return result; 418} 419 420 421int 422number_of_digits(unsigned long value) 423{ 424 int j; 425 426 j = 1; 427 while (value > 9) { 428 j++; 429 value = value / 10; 430 } 431 return j; 432} 433 434 435// 436// Print a message on standard error & flush the input. 437// 438void 439bad_input(const char *fmt, ...) 440{ 441 va_list ap; 442 443 va_start(ap, fmt); 444 vfprintf(stderr, fmt, ap); 445 va_end(ap); 446 fprintf(stderr, "\n"); 447 flush_to_newline(1); 448} 449