1 /* $OpenBSD: io.c,v 1.13 2016/01/17 18:57:52 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 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <stdarg.h> 36 #include <errno.h> 37 38 #include "io.h" 39 40 #define BAD_DIGIT 17 /* must be greater than any base */ 41 #define STRING_CHUNK 16 42 #define UNGET_MAX_COUNT 10 43 44 const long kDefault = -1; 45 46 short unget_buf[UNGET_MAX_COUNT+1]; 47 int unget_count; 48 char io_buffer[MAXIOSIZE]; 49 50 long get_number(int first_char); 51 char* get_string(int eos); 52 int my_getch(void); 53 void my_ungetch(int c); 54 55 int 56 my_getch() 57 { 58 if (unget_count > 0) { 59 return (unget_buf[--unget_count]); 60 } else { 61 return (getc(stdin)); 62 } 63 } 64 65 66 void 67 my_ungetch(int c) 68 { 69 // In practice there is never more than one character in 70 // the unget_buf, but what's a little overkill among friends? 71 72 if (unget_count < UNGET_MAX_COUNT) { 73 unget_buf[unget_count++] = c; 74 } else { 75 errx(1, "Programmer error in my_ungetch()."); 76 } 77 } 78 79 void 80 flush_to_newline(int keep_newline) 81 { 82 int c; 83 84 for (;;) { 85 c = my_getch(); 86 87 if (c <= 0) { 88 break; 89 } else if (c == '\n') { 90 if (keep_newline) { 91 my_ungetch(c); 92 } 93 break; 94 } else { 95 // skip 96 } 97 } 98 return; 99 } 100 101 102 int 103 get_okay(const char *prompt, int default_value) 104 { 105 int c; 106 107 flush_to_newline(0); 108 printf(prompt); 109 110 for (;;) { 111 c = my_getch(); 112 113 if (c <= 0) { 114 break; 115 } else if (c == ' ' || c == '\t') { 116 // skip blanks and tabs 117 } else if (c == '\n') { 118 my_ungetch(c); 119 return default_value; 120 } else if (c == 'y' || c == 'Y') { 121 return 1; 122 } else if (c == 'n' || c == 'N') { 123 return 0; 124 } else { 125 flush_to_newline(0); 126 printf(prompt); 127 } 128 } 129 return -1; 130 } 131 132 int 133 get_command(const char *prompt, int promptBeforeGet, int *command) 134 { 135 int c; 136 137 if (promptBeforeGet) { 138 printf(prompt); 139 } 140 for (;;) { 141 c = my_getch(); 142 143 if (c <= 0) { 144 break; 145 } else if (c == ' ' || c == '\t') { 146 // skip blanks and tabs 147 } else if (c == '\n') { 148 printf(prompt); 149 } else { 150 *command = c; 151 return 1; 152 } 153 } 154 return 0; 155 } 156 157 int 158 get_number_argument(const char *prompt, long *number, long default_value) 159 { 160 int c; 161 int result = 0; 162 163 for (;;) { 164 c = my_getch(); 165 166 if (c <= 0) { 167 break; 168 } else if (c == ' ' || c == '\t') { 169 // skip blanks and tabs 170 } else if (c == '\n') { 171 if (default_value == kDefault) { 172 printf(prompt); 173 } else { 174 my_ungetch(c); 175 *number = default_value; 176 result = 1; 177 break; 178 } 179 } else if ('0' <= c && c <= '9') { 180 *number = get_number(c); 181 result = 1; 182 break; 183 } else { 184 my_ungetch(c); 185 *number = 0; 186 break; 187 } 188 } 189 return result; 190 } 191 192 193 long 194 get_number(int first_char) 195 { 196 int c; 197 int base; 198 int digit; 199 int ret_value; 200 201 if (first_char != '0') { 202 c = first_char; 203 base = 10; 204 digit = BAD_DIGIT; 205 } else if ((c=my_getch()) == 'x' || c == 'X') { 206 c = my_getch(); 207 base = 16; 208 digit = BAD_DIGIT; 209 } else { 210 my_ungetch(c); 211 c = first_char; 212 base = 8; 213 digit = 0; 214 } 215 ret_value = 0; 216 for (ret_value = 0; ; c = my_getch()) { 217 if (c >= '0' && c <= '9') { 218 digit = c - '0'; 219 } else if (c >='A' && c <= 'F') { 220 digit = 10 + (c - 'A'); 221 } else if (c >='a' && c <= 'f') { 222 digit = 10 + (c - 'a'); 223 } else { 224 digit = BAD_DIGIT; 225 } 226 if (digit >= base) { 227 break; 228 } 229 ret_value = ret_value * base + digit; 230 } 231 my_ungetch(c); 232 return(ret_value); 233 } 234 235 int 236 get_string_argument(const char *prompt, char **string, int reprompt) 237 { 238 int c; 239 int result = 0; 240 241 for (;;) { 242 c = my_getch(); 243 244 if (c <= 0) { 245 break; 246 } else if (c == ' ' || c == '\t') { 247 // skip blanks and tabs 248 } else if (c == '\n') { 249 if (reprompt) { 250 printf(prompt); 251 } else { 252 my_ungetch(c); 253 *string = NULL; 254 break; 255 } 256 } else if (c == '"' || c == '\'') { 257 *string = get_string(c); 258 result = 1; 259 break; 260 } else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') 261 || (c == '-' || c == '/' || c == '.' || c == ':')) { 262 my_ungetch(c); 263 *string = get_string(' '); 264 result = 1; 265 break; 266 } else { 267 my_ungetch(c); 268 *string = NULL; 269 break; 270 } 271 } 272 return result; 273 } 274 275 276 char * 277 get_string(int eos) 278 { 279 int c; 280 char *s; 281 char *ret_value; 282 char *limit; 283 int length; 284 285 ret_value = malloc(STRING_CHUNK); 286 if (ret_value == NULL) { 287 warn("can't allocate memory for string buffer"); 288 return NULL; 289 } 290 length = STRING_CHUNK; 291 limit = ret_value + length; 292 293 c = my_getch(); 294 for (s = ret_value; ; c = my_getch()) { 295 if (s >= limit) { 296 // expand string 297 limit = malloc(length+STRING_CHUNK); 298 if (limit == NULL) { 299 warn("can't allocate memory for string buffer"); 300 ret_value[length-1] = 0; 301 break; 302 } 303 strncpy(limit, ret_value, length); 304 free(ret_value); 305 s = limit + (s - ret_value); 306 ret_value = limit; 307 length += STRING_CHUNK; 308 limit = ret_value + length; 309 } 310 if (c <= 0 || c == eos || (eos == ' ' && c == '\t')) { 311 *s++ = 0; 312 break; 313 } else if (c == '\n') { 314 *s++ = 0; 315 my_ungetch(c); 316 break; 317 } else { 318 *s++ = c; 319 } 320 } 321 return(ret_value); 322 } 323 324 325 unsigned long 326 get_multiplier(long divisor) 327 { 328 int c; 329 unsigned long result; 330 unsigned long extra; 331 332 c = my_getch(); 333 334 extra = 1; 335 if (c <= 0 || divisor <= 0) { 336 result = 0; 337 } else if (c == 't' || c == 'T') { 338 result = 1024*1024; 339 extra = 1024*1024; 340 } else if (c == 'g' || c == 'G') { 341 result = 1024*1024*1024; 342 } else if (c == 'm' || c == 'M') { 343 result = 1024*1024; 344 } else if (c == 'k' || c == 'K') { 345 result = 1024; 346 } else { 347 my_ungetch(c); 348 result = 1; 349 } 350 if (result > 1) { 351 if (extra > 1) { 352 result /= divisor; 353 if (result >= 4096) { 354 /* overflow -> 20bits + >12bits */ 355 result = 0; 356 } else { 357 result *= extra; 358 } 359 } else if (result >= divisor) { 360 result /= divisor; 361 } else { 362 result = 1; 363 } 364 } 365 return result; 366 } 367 368 369 int 370 get_partition_modifier(void) 371 { 372 int c; 373 int result; 374 375 result = 0; 376 377 c = my_getch(); 378 379 if (c == 'p' || c == 'P') { 380 result = 1; 381 } else if (c > 0) { 382 my_ungetch(c); 383 } 384 return result; 385 } 386 387 388 int 389 number_of_digits(unsigned long value) 390 { 391 int j; 392 393 j = 1; 394 while (value > 9) { 395 j++; 396 value = value / 10; 397 } 398 return j; 399 } 400 401 402 // 403 // Print a message on standard error & flush the input. 404 // 405 void 406 bad_input(const char *fmt, ...) 407 { 408 va_list ap; 409 410 va_start(ap, fmt); 411 vfprintf(stderr, fmt, ap); 412 va_end(ap); 413 fprintf(stderr, "\n"); 414 flush_to_newline(1); 415 } 416