1 /* $OpenBSD: io.c,v 1.12 2016/01/12 23:48:42 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 54 55 // 56 // Types 57 // 58 59 60 // 61 // Global Constants 62 // 63 const long kDefault = -1; 64 65 66 // 67 // Global Variables 68 // 69 short unget_buf[UNGET_MAX_COUNT+1]; 70 int unget_count; 71 char io_buffer[MAXIOSIZE]; 72 73 74 // 75 // Forward declarations 76 // 77 long get_number(int first_char); 78 char* get_string(int eos); 79 int my_getch(void); 80 void my_ungetch(int c); 81 82 83 // 84 // Routines 85 // 86 int 87 my_getch() 88 { 89 if (unget_count > 0) { 90 return (unget_buf[--unget_count]); 91 } else { 92 return (getc(stdin)); 93 } 94 } 95 96 97 void 98 my_ungetch(int c) 99 { 100 // In practice there is never more than one character in 101 // the unget_buf, but what's a little overkill among friends? 102 103 if (unget_count < UNGET_MAX_COUNT) { 104 unget_buf[unget_count++] = c; 105 } else { 106 errx(1, "Programmer error in my_ungetch()."); 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 int 164 get_command(const char *prompt, int promptBeforeGet, int *command) 165 { 166 int c; 167 168 if (promptBeforeGet) { 169 printf(prompt); 170 } 171 for (;;) { 172 c = my_getch(); 173 174 if (c <= 0) { 175 break; 176 } else if (c == ' ' || c == '\t') { 177 // skip blanks and tabs 178 } else if (c == '\n') { 179 printf(prompt); 180 } else { 181 *command = c; 182 return 1; 183 } 184 } 185 return 0; 186 } 187 188 int 189 get_number_argument(const char *prompt, long *number, long default_value) 190 { 191 int c; 192 int result = 0; 193 194 for (;;) { 195 c = my_getch(); 196 197 if (c <= 0) { 198 break; 199 } else if (c == ' ' || c == '\t') { 200 // skip blanks and tabs 201 } else if (c == '\n') { 202 if (default_value == kDefault) { 203 printf(prompt); 204 } else { 205 my_ungetch(c); 206 *number = default_value; 207 result = 1; 208 break; 209 } 210 } else if ('0' <= c && c <= '9') { 211 *number = get_number(c); 212 result = 1; 213 break; 214 } else { 215 my_ungetch(c); 216 *number = 0; 217 break; 218 } 219 } 220 return result; 221 } 222 223 224 long 225 get_number(int first_char) 226 { 227 int c; 228 int base; 229 int digit; 230 int ret_value; 231 232 if (first_char != '0') { 233 c = first_char; 234 base = 10; 235 digit = BAD_DIGIT; 236 } else if ((c=my_getch()) == 'x' || c == 'X') { 237 c = my_getch(); 238 base = 16; 239 digit = BAD_DIGIT; 240 } else { 241 my_ungetch(c); 242 c = first_char; 243 base = 8; 244 digit = 0; 245 } 246 ret_value = 0; 247 for (ret_value = 0; ; c = my_getch()) { 248 if (c >= '0' && c <= '9') { 249 digit = c - '0'; 250 } else if (c >='A' && c <= 'F') { 251 digit = 10 + (c - 'A'); 252 } else if (c >='a' && c <= 'f') { 253 digit = 10 + (c - 'a'); 254 } else { 255 digit = BAD_DIGIT; 256 } 257 if (digit >= base) { 258 break; 259 } 260 ret_value = ret_value * base + digit; 261 } 262 my_ungetch(c); 263 return(ret_value); 264 } 265 266 int 267 get_string_argument(const char *prompt, char **string, int reprompt) 268 { 269 int c; 270 int result = 0; 271 272 for (;;) { 273 c = my_getch(); 274 275 if (c <= 0) { 276 break; 277 } else if (c == ' ' || c == '\t') { 278 // skip blanks and tabs 279 } else if (c == '\n') { 280 if (reprompt) { 281 printf(prompt); 282 } else { 283 my_ungetch(c); 284 *string = NULL; 285 break; 286 } 287 } else if (c == '"' || c == '\'') { 288 *string = get_string(c); 289 result = 1; 290 break; 291 } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 292 || (c == '-' || c == '/' || c == '.' || c == ':')) { 293 my_ungetch(c); 294 *string = get_string(' '); 295 result = 1; 296 break; 297 } else { 298 my_ungetch(c); 299 *string = NULL; 300 break; 301 } 302 } 303 return result; 304 } 305 306 307 char * 308 get_string(int eos) 309 { 310 int c; 311 char *s; 312 char *ret_value; 313 char *limit; 314 int length; 315 316 ret_value = malloc(STRING_CHUNK); 317 if (ret_value == NULL) { 318 warn("can't allocate memory for string buffer"); 319 return NULL; 320 } 321 length = STRING_CHUNK; 322 limit = ret_value + length; 323 324 c = my_getch(); 325 for (s = ret_value; ; c = my_getch()) { 326 if (s >= limit) { 327 // expand string 328 limit = malloc(length+STRING_CHUNK); 329 if (limit == NULL) { 330 warn("can't allocate memory for string buffer"); 331 ret_value[length-1] = 0; 332 break; 333 } 334 strncpy(limit, ret_value, length); 335 free(ret_value); 336 s = limit + (s - ret_value); 337 ret_value = limit; 338 length += STRING_CHUNK; 339 limit = ret_value + length; 340 } 341 if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) { 342 *s++ = 0; 343 break; 344 } else if (c == '\n') { 345 *s++ = 0; 346 my_ungetch(c); 347 break; 348 } else { 349 *s++ = c; 350 } 351 } 352 return(ret_value); 353 } 354 355 356 unsigned long 357 get_multiplier(long divisor) 358 { 359 int c; 360 unsigned long result; 361 unsigned long extra; 362 363 c = my_getch(); 364 365 extra = 1; 366 if (c <= 0 || divisor <= 0) { 367 result = 0; 368 } else if (c == 't' || c == 'T') { 369 result = 1024*1024; 370 extra = 1024*1024; 371 } else if (c == 'g' || c == 'G') { 372 result = 1024*1024*1024; 373 } else if (c == 'm' || c == 'M') { 374 result = 1024*1024; 375 } else if (c == 'k' || c == 'K') { 376 result = 1024; 377 } else { 378 my_ungetch(c); 379 result = 1; 380 } 381 if (result > 1) { 382 if (extra > 1) { 383 result /= divisor; 384 if (result >= 4096) { 385 /* overflow -> 20bits + >12bits */ 386 result = 0; 387 } else { 388 result *= extra; 389 } 390 } else if (result >= divisor) { 391 result /= divisor; 392 } else { 393 result = 1; 394 } 395 } 396 return result; 397 } 398 399 400 int 401 get_partition_modifier(void) 402 { 403 int c; 404 int result; 405 406 result = 0; 407 408 c = my_getch(); 409 410 if (c == 'p' || c == 'P') { 411 result = 1; 412 } else if (c > 0) { 413 my_ungetch(c); 414 } 415 return result; 416 } 417 418 419 int 420 number_of_digits(unsigned long value) 421 { 422 int j; 423 424 j = 1; 425 while (value > 9) { 426 j++; 427 value = value / 10; 428 } 429 return j; 430 } 431 432 433 // 434 // Print a message on standard error & flush the input. 435 // 436 void 437 bad_input(const char *fmt, ...) 438 { 439 va_list ap; 440 441 va_start(ap, fmt); 442 vfprintf(stderr, fmt, ap); 443 va_end(ap); 444 fprintf(stderr, "\n"); 445 flush_to_newline(1); 446 } 447