io.c revision 1.22
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 42short unget_buf[UNGET_MAX_COUNT + 1]; 43int unget_count; 44 45long get_number(int); 46char *get_string(int); 47int my_getch (void); 48 49int 50my_getch() 51{ 52 if (unget_count > 0) { 53 return (unget_buf[--unget_count]); 54 } else { 55 return (getc(stdin)); 56 } 57} 58 59 60void 61my_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 75void 76flush_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 98int 99get_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 128int 129get_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 153int 154get_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 182long 183get_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 221int 222get_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 256char * 257get_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 302unsigned long 303get_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 345int 346get_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 363int 364number_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 */ 380void 381bad_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