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