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