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