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