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