io.c revision 1.17
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 44const long kDefault = -1; 45 46short unget_buf[UNGET_MAX_COUNT + 1]; 47int unget_count; 48char io_buffer[MAXIOSIZE]; 49 50long get_number(int); 51char *get_string(int); 52int my_getch (void); 53void my_ungetch(int); 54 55int 56my_getch() 57{ 58 if (unget_count > 0) { 59 return (unget_buf[--unget_count]); 60 } else { 61 return (getc(stdin)); 62 } 63} 64 65 66void 67my_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 81void 82flush_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 104int 105get_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 134int 135get_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 159int 160get_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 195long 196get_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 234int 235get_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 275char * 276get_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 321unsigned long 322get_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 364int 365get_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 382int 383number_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 */ 399void 400bad_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