1 /* $NetBSD: subr_userconf.c,v 1.10 2002/10/22 03:27:47 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Mats O Jansson. 18 * 4. The name of the author may not be used to endorse or promote 19 * products derived from this software without specific prior written 20 * permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 26 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * OpenBSD: subr_userconf.c,v 1.19 2000/01/08 23:23:37 d Exp 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: subr_userconf.c,v 1.10 2002/10/22 03:27:47 simonb Exp $"); 39 40 #include "opt_userconf.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 #include <sys/malloc.h> 46 #include <sys/time.h> 47 48 #include <dev/cons.h> 49 50 extern struct cfdata cfdata[]; 51 52 int userconf_base = 16; /* Base for "large" numbers */ 53 int userconf_maxdev = -1; /* # of used device slots */ 54 int userconf_totdev = -1; /* # of device slots */ 55 int userconf_maxlocnames = -1; /* # of locnames */ 56 int userconf_cnt = -1; /* Line counter for ... */ 57 int userconf_lines = 12; /* ... # of lines per page */ 58 int userconf_histlen = 0; 59 int userconf_histcur = 0; 60 char userconf_history[1024]; 61 int userconf_histsz = sizeof(userconf_history); 62 char userconf_argbuf[40]; /* Additional input */ 63 char userconf_cmdbuf[40]; /* Command line */ 64 char userconf_histbuf[40]; 65 66 void userconf_init __P((void)); 67 int userconf_more __P((void)); 68 void userconf_modify __P((const char *, int*)); 69 void userconf_hist_cmd __P((char)); 70 void userconf_hist_int __P((int)); 71 void userconf_hist_eoc __P((void)); 72 void userconf_pnum __P((int)); 73 void userconf_pdevnam __P((short)); 74 void userconf_pdev __P((short)); 75 int userconf_number __P((char *, int *)); 76 int userconf_device __P((char *, int *, short *, short *)); 77 void userconf_change __P((int)); 78 void userconf_disable __P((int)); 79 void userconf_enable __P((int)); 80 void userconf_help __P((void)); 81 void userconf_list __P((void)); 82 void userconf_common_dev __P((char *, int, short, short, char)); 83 void userconf_add_read __P((char *, char, char *, int, int *)); 84 int userconf_parse __P((char *)); 85 86 static int getsn __P((char *, int)); 87 88 #define UC_CHANGE 'c' 89 #define UC_DISABLE 'd' 90 #define UC_ENABLE 'e' 91 #define UC_FIND 'f' 92 #define UC_SHOW 's' 93 94 char *userconf_cmds[] = { 95 "base", "b", 96 "change", "c", 97 "disable", "d", 98 "enable", "e", 99 "exit", "q", 100 "find", "f", 101 "help", "h", 102 "list", "l", 103 "lines", "L", 104 "quit", "q", 105 "?", "h", 106 "", "", 107 }; 108 109 void 110 userconf_init() 111 { 112 int i; 113 struct cfdata *cf; 114 115 i = 0; 116 for (cf = cfdata; cf->cf_name; cf++) 117 i++; 118 119 userconf_maxdev = i - 1; 120 userconf_totdev = i - 1; 121 } 122 123 int 124 userconf_more() 125 { 126 int quit = 0; 127 char c = '\0'; 128 129 if (userconf_cnt != -1) { 130 if (userconf_cnt == userconf_lines) { 131 printf("-- more --"); 132 c = cngetc(); 133 userconf_cnt = 0; 134 printf("\r \r"); 135 } 136 userconf_cnt++; 137 if (c == 'q' || c == 'Q') 138 quit = 1; 139 } 140 return (quit); 141 } 142 143 void 144 userconf_hist_cmd(cmd) 145 char cmd; 146 { 147 userconf_histcur = userconf_histlen; 148 if (userconf_histcur < userconf_histsz) { 149 userconf_history[userconf_histcur] = cmd; 150 userconf_histcur++; 151 } 152 } 153 154 void 155 userconf_hist_int(val) 156 int val; 157 { 158 sprintf(userconf_histbuf," %d",val); 159 if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) { 160 memcpy(&userconf_history[userconf_histcur], 161 userconf_histbuf, 162 strlen(userconf_histbuf)); 163 userconf_histcur = userconf_histcur + strlen(userconf_histbuf); 164 } 165 } 166 167 void 168 userconf_hist_eoc() 169 { 170 if (userconf_histcur < userconf_histsz) { 171 userconf_history[userconf_histcur] = '\n'; 172 userconf_histcur++; 173 userconf_histlen = userconf_histcur; 174 } 175 } 176 177 void 178 userconf_pnum(val) 179 int val; 180 { 181 if (val > -2 && val < 16) { 182 printf("%d",val); 183 } else { 184 switch (userconf_base) { 185 case 8: 186 printf("0%o",val); 187 break; 188 case 10: 189 printf("%d",val); 190 break; 191 case 16: 192 default: 193 printf("0x%x",val); 194 break; 195 } 196 } 197 } 198 199 void 200 userconf_pdevnam(dev) 201 short dev; 202 { 203 struct cfdata *cd; 204 205 cd = &cfdata[dev]; 206 printf("%s", cd->cf_name); 207 switch (cd->cf_fstate) { 208 case FSTATE_NOTFOUND: 209 case FSTATE_DNOTFOUND: 210 printf("%d", cd->cf_unit); 211 break; 212 case FSTATE_FOUND: 213 printf("*FOUND*"); 214 break; 215 case FSTATE_STAR: 216 case FSTATE_DSTAR: 217 printf("*"); 218 break; 219 default: 220 printf("*UNKNOWN*"); 221 break; 222 } 223 } 224 225 void 226 userconf_pdev(devno) 227 short devno; 228 { 229 struct cfdata *cd; 230 const struct cfparent *cfp; 231 int *l; 232 const char * const *ln; 233 234 if (devno > userconf_maxdev) { 235 printf("Unknown devno (max is %d)\n", userconf_maxdev); 236 return; 237 } 238 239 cd = &cfdata[devno]; 240 241 printf("[%3d] ", devno); 242 userconf_pdevnam(devno); 243 printf(" at"); 244 cfp = cd->cf_pspec; 245 if (cfp == NULL) 246 printf(" root"); 247 else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1) 248 printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit); 249 else 250 printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent 251 : cfp->cfp_iattr); 252 switch (cd->cf_fstate) { 253 case FSTATE_NOTFOUND: 254 case FSTATE_FOUND: 255 case FSTATE_STAR: 256 break; 257 case FSTATE_DNOTFOUND: 258 case FSTATE_DSTAR: 259 printf(" disable"); 260 break; 261 default: 262 printf(" ???"); 263 break; 264 } 265 l = cd->cf_loc; 266 ln = cd->cf_locnames; 267 while (ln && *ln) { 268 printf(" %s ", *ln++); 269 userconf_pnum(*l++); 270 } 271 printf("\n"); 272 } 273 274 int 275 userconf_number(c, val) 276 char *c; 277 int *val; 278 { 279 u_int num = 0; 280 int neg = 0; 281 int base = 10; 282 283 if (*c == '-') { 284 neg = 1; 285 c++; 286 } 287 if (*c == '0') { 288 base = 8; 289 c++; 290 if (*c == 'x' || *c == 'X') { 291 base = 16; 292 c++; 293 } 294 } 295 while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') { 296 u_char cc = *c; 297 298 if (cc >= '0' && cc <= '9') 299 cc = cc - '0'; 300 else if (cc >= 'a' && cc <= 'f') 301 cc = cc - 'a' + 10; 302 else if (cc >= 'A' && cc <= 'F') 303 cc = cc - 'A' + 10; 304 else 305 return (-1); 306 307 if (cc > base) 308 return (-1); 309 num = num * base + cc; 310 c++; 311 } 312 313 if (neg && num > INT_MAX) /* overflow */ 314 return (1); 315 *val = neg ? - num : num; 316 return (0); 317 } 318 319 int 320 userconf_device(cmd, len, unit, state) 321 char *cmd; 322 int *len; 323 short *unit, *state; 324 { 325 short u = 0, s = FSTATE_FOUND; 326 int l = 0; 327 char *c; 328 329 c = cmd; 330 while (*c >= 'a' && *c <= 'z') { 331 l++; 332 c++; 333 } 334 if (*c == '*') { 335 s = FSTATE_STAR; 336 c++; 337 } else { 338 while (*c >= '0' && *c <= '9') { 339 s = FSTATE_NOTFOUND; 340 u = u*10 + *c - '0'; 341 c++; 342 } 343 } 344 while (*c == ' ' || *c == '\t' || *c == '\n') 345 c++; 346 347 if (*c == '\0') { 348 *len = l; 349 *unit = u; 350 *state = s; 351 return(0); 352 } 353 354 return(-1); 355 } 356 357 void 358 userconf_modify(item, val) 359 const char *item; 360 int *val; 361 { 362 int ok = 0; 363 int a; 364 char *c; 365 366 while (!ok) { 367 printf("%s [", item); 368 userconf_pnum(*val); 369 printf("] ? "); 370 371 getsn(userconf_argbuf, sizeof(userconf_argbuf)); 372 373 c = userconf_argbuf; 374 while (*c == ' ' || *c == '\t' || *c == '\n') c++; 375 376 if (*c != '\0') { 377 if (userconf_number(c, &a) == 0) { 378 *val = a; 379 ok = 1; 380 } else { 381 printf("Unknown argument\n"); 382 } 383 } else { 384 ok = 1; 385 } 386 } 387 } 388 389 void 390 userconf_change(devno) 391 int devno; 392 { 393 struct cfdata *cd; 394 char c = '\0'; 395 int *l; 396 int ln; 397 const char * const *locnames; 398 399 if (devno <= userconf_maxdev) { 400 401 userconf_pdev(devno); 402 403 while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') { 404 printf("change (y/n) ?"); 405 c = cngetc(); 406 printf("\n"); 407 } 408 409 if (c == 'y' || c == 'Y') { 410 411 /* XXX add cmd 'c' <devno> */ 412 userconf_hist_cmd('c'); 413 userconf_hist_int(devno); 414 415 cd = &cfdata[devno]; 416 l = cd->cf_loc; 417 locnames = cd->cf_locnames; 418 ln = 0; 419 420 while (locnames[ln]) 421 { 422 userconf_modify(locnames[ln], l); 423 424 /* XXX add *l */ 425 userconf_hist_int(*l); 426 427 ln++; 428 l++; 429 } 430 431 printf("[%3d] ", devno); 432 userconf_pdevnam(devno); 433 printf(" changed\n"); 434 userconf_pdev(devno); 435 436 /* XXX add eoc */ 437 userconf_hist_eoc(); 438 439 } 440 } else { 441 printf("Unknown devno (max is %d)\n", userconf_maxdev); 442 } 443 } 444 445 void 446 userconf_disable(devno) 447 int devno; 448 { 449 int done = 0; 450 451 if (devno <= userconf_maxdev) { 452 switch (cfdata[devno].cf_fstate) { 453 case FSTATE_NOTFOUND: 454 cfdata[devno].cf_fstate = FSTATE_DNOTFOUND; 455 break; 456 case FSTATE_STAR: 457 cfdata[devno].cf_fstate = FSTATE_DSTAR; 458 break; 459 case FSTATE_DNOTFOUND: 460 case FSTATE_DSTAR: 461 done = 1; 462 break; 463 default: 464 printf("Error unknown state\n"); 465 break; 466 } 467 468 printf("[%3d] ", devno); 469 userconf_pdevnam(devno); 470 if (done) { 471 printf(" already"); 472 } else { 473 /* XXX add cmd 'd' <devno> eoc */ 474 userconf_hist_cmd('d'); 475 userconf_hist_int(devno); 476 userconf_hist_eoc(); 477 } 478 printf(" disabled\n"); 479 } else { 480 printf("Unknown devno (max is %d)\n", userconf_maxdev); 481 } 482 } 483 484 void 485 userconf_enable(devno) 486 int devno; 487 { 488 int done = 0; 489 490 if (devno <= userconf_maxdev) { 491 switch (cfdata[devno].cf_fstate) { 492 case FSTATE_DNOTFOUND: 493 cfdata[devno].cf_fstate = FSTATE_NOTFOUND; 494 break; 495 case FSTATE_DSTAR: 496 cfdata[devno].cf_fstate = FSTATE_STAR; 497 break; 498 case FSTATE_NOTFOUND: 499 case FSTATE_STAR: 500 done = 1; 501 break; 502 default: 503 printf("Error unknown state\n"); 504 break; 505 } 506 507 printf("[%3d] ", devno); 508 userconf_pdevnam(devno); 509 if (done) { 510 printf(" already"); 511 } else { 512 /* XXX add cmd 'e' <devno> eoc */ 513 userconf_hist_cmd('d'); 514 userconf_hist_int(devno); 515 userconf_hist_eoc(); 516 } 517 printf(" enabled\n"); 518 } else { 519 printf("Unknown devno (max is %d)\n", userconf_maxdev); 520 } 521 } 522 523 void 524 userconf_help() 525 { 526 int j = 0, k; 527 528 printf("command args description\n"); 529 while (*userconf_cmds[j] != '\0') { 530 printf(userconf_cmds[j]); 531 k = strlen(userconf_cmds[j]); 532 while (k < 10) { 533 printf(" "); 534 k++; 535 } 536 switch (*userconf_cmds[j+1]) { 537 case 'L': 538 printf("[count] number of lines before more"); 539 break; 540 case 'b': 541 printf("8|10|16 base on large numbers"); 542 break; 543 case 'c': 544 printf("devno|dev change devices"); 545 break; 546 case 'd': 547 printf("devno|dev disable devices"); 548 break; 549 case 'e': 550 printf("devno|dev enable devices"); 551 break; 552 case 'f': 553 printf("devno|dev find devices"); 554 break; 555 case 'h': 556 printf(" this message"); 557 break; 558 case 'l': 559 printf(" list configuration"); 560 break; 561 case 'q': 562 printf(" leave userconf"); 563 break; 564 default: 565 printf(" don't know"); 566 break; 567 } 568 printf("\n"); 569 j += 2; 570 } 571 } 572 573 void 574 userconf_list() 575 { 576 int i = 0; 577 578 userconf_cnt = 0; 579 580 while (cfdata[i].cf_name != NULL) { 581 if (userconf_more()) 582 break; 583 userconf_pdev(i++); 584 } 585 586 userconf_cnt = -1; 587 } 588 589 void 590 userconf_common_dev(dev, len, unit, state, routine) 591 char *dev; 592 int len; 593 short unit, state; 594 char routine; 595 { 596 int i = 0; 597 598 switch (routine) { 599 case UC_CHANGE: 600 break; 601 default: 602 userconf_cnt = 0; 603 break; 604 } 605 606 while (cfdata[i].cf_name != NULL) { 607 if (strlen(cfdata[i].cf_name) == len) { 608 609 /* 610 * Ok, if device name is correct 611 * If state == FSTATE_FOUND, look for "dev" 612 * If state == FSTATE_STAR, look for "dev*" 613 * If state == FSTATE_NOTFOUND, look for "dev0" 614 */ 615 if (strncasecmp(dev, cfdata[i].cf_name, 616 len) == 0 && 617 (state == FSTATE_FOUND || 618 (state == FSTATE_STAR && 619 (cfdata[i].cf_fstate == FSTATE_STAR || 620 cfdata[i].cf_fstate == FSTATE_DSTAR)) || 621 (state == FSTATE_NOTFOUND && 622 cfdata[i].cf_unit == unit && 623 (cfdata[i].cf_fstate == FSTATE_NOTFOUND || 624 cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) { 625 if (userconf_more()) 626 break; 627 switch (routine) { 628 case UC_CHANGE: 629 userconf_change(i); 630 break; 631 case UC_ENABLE: 632 userconf_enable(i); 633 break; 634 case UC_DISABLE: 635 userconf_disable(i); 636 break; 637 case UC_FIND: 638 userconf_pdev(i); 639 break; 640 default: 641 printf("Unknown routine /%c/\n", 642 routine); 643 break; 644 } 645 } 646 } 647 i++; 648 } 649 650 switch (routine) { 651 case UC_CHANGE: 652 break; 653 default: 654 userconf_cnt = -1; 655 break; 656 } 657 } 658 659 void 660 userconf_add_read(prompt, field, dev, len, val) 661 char *prompt; 662 char field; 663 char *dev; 664 int len; 665 int *val; 666 { 667 int ok = 0; 668 int a; 669 char *c; 670 671 *val = -1; 672 673 while (!ok) { 674 printf("%s ? ", prompt); 675 676 getsn(userconf_argbuf, sizeof(userconf_argbuf)); 677 678 c = userconf_argbuf; 679 while (*c == ' ' || *c == '\t' || *c == '\n') c++; 680 681 if (*c != '\0') { 682 if (userconf_number(c, &a) == 0) { 683 if (a > userconf_maxdev) { 684 printf("Unknown devno (max is %d)\n", 685 userconf_maxdev); 686 } else if (strncasecmp(dev, 687 cfdata[a].cf_name, len) != 0 && 688 field == 'a') { 689 printf("Not same device type\n"); 690 } else { 691 *val = a; 692 ok = 1; 693 } 694 } else if (*c == '?') { 695 userconf_common_dev(dev, len, 0, 696 FSTATE_FOUND, UC_FIND); 697 } else if (*c == 'q' || *c == 'Q') { 698 ok = 1; 699 } else { 700 printf("Unknown argument\n"); 701 } 702 } else { 703 ok = 1; 704 } 705 } 706 } 707 708 int 709 userconf_parse(cmd) 710 char *cmd; 711 { 712 char *c, *v; 713 int i = 0, j = 0, k, a; 714 short unit, state; 715 716 c = cmd; 717 while (*c == ' ' || *c == '\t') 718 c++; 719 v = c; 720 while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') { 721 c++; 722 i++; 723 } 724 725 k = -1; 726 while (*userconf_cmds[j] != '\0') { 727 if (strlen(userconf_cmds[j]) == i) { 728 if (strncasecmp(v, userconf_cmds[j], i) == 0) 729 k = j; 730 } 731 j += 2; 732 } 733 734 while (*c == ' ' || *c == '\t' || *c == '\n') 735 c++; 736 737 if (k == -1) { 738 if (*v != '\n') 739 printf("Unknown command, try help\n"); 740 } else { 741 switch (*userconf_cmds[k+1]) { 742 case 'L': 743 if (*c == '\0') 744 printf("Argument expected\n"); 745 else if (userconf_number(c, &a) == 0) 746 userconf_lines = a; 747 else 748 printf("Unknown argument\n"); 749 break; 750 case 'b': 751 if (*c == '\0') 752 printf("8|10|16 expected\n"); 753 else if (userconf_number(c, &a) == 0) { 754 if (a == 8 || a == 10 || a == 16) { 755 userconf_base = a; 756 } else { 757 printf("8|10|16 expected\n"); 758 } 759 } else 760 printf("Unknown argument\n"); 761 break; 762 case 'c': 763 if (*c == '\0') 764 printf("DevNo or Dev expected\n"); 765 else if (userconf_number(c, &a) == 0) 766 userconf_change(a); 767 else if (userconf_device(c, &a, &unit, &state) == 0) 768 userconf_common_dev(c, a, unit, state, UC_CHANGE); 769 else 770 printf("Unknown argument\n"); 771 break; 772 case 'd': 773 if (*c == '\0') 774 printf("Attr, DevNo or Dev expected\n"); 775 else if (userconf_number(c, &a) == 0) 776 userconf_disable(a); 777 else if (userconf_device(c, &a, &unit, &state) == 0) 778 userconf_common_dev(c, a, unit, state, UC_DISABLE); 779 else 780 printf("Unknown argument\n"); 781 break; 782 case 'e': 783 if (*c == '\0') 784 printf("Attr, DevNo or Dev expected\n"); 785 else if (userconf_number(c, &a) == 0) 786 userconf_enable(a); 787 else if (userconf_device(c, &a, &unit, &state) == 0) 788 userconf_common_dev(c, a, unit, state, UC_ENABLE); 789 else 790 printf("Unknown argument\n"); 791 break; 792 case 'f': 793 if (*c == '\0') 794 printf("DevNo or Dev expected\n"); 795 else if (userconf_number(c, &a) == 0) 796 userconf_pdev(a); 797 else if (userconf_device(c, &a, &unit, &state) == 0) 798 userconf_common_dev(c, a, unit, state, UC_FIND); 799 else 800 printf("Unknown argument\n"); 801 break; 802 case 'h': 803 userconf_help(); 804 break; 805 case 'l': 806 if (*c == '\0') 807 userconf_list(); 808 else 809 printf("Unknown argument\n"); 810 break; 811 case 'q': 812 /* XXX add cmd 'q' eoc */ 813 userconf_hist_cmd('q'); 814 userconf_hist_eoc(); 815 return(-1); 816 case 's': 817 default: 818 printf("Unknown command\n"); 819 break; 820 } 821 } 822 return(0); 823 } 824 825 extern void user_config __P((void)); 826 827 void 828 user_config() 829 { 830 char prompt[] = "uc> "; 831 832 userconf_init(); 833 printf("userconf: configure system autoconfiguration:\n"); 834 835 while (1) { 836 printf(prompt); 837 if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 && 838 userconf_parse(userconf_cmdbuf)) 839 break; 840 } 841 printf("Continuing...\n"); 842 } 843 844 /* 845 * XXX shouldn't this be a common function? 846 */ 847 static int 848 getsn(cp, size) 849 char *cp; 850 int size; 851 { 852 char *lp; 853 int c, len; 854 855 cnpollc(1); 856 857 lp = cp; 858 len = 0; 859 for (;;) { 860 c = cngetc(); 861 switch (c) { 862 case '\n': 863 case '\r': 864 printf("\n"); 865 *lp++ = '\0'; 866 cnpollc(0); 867 return (len); 868 case '\b': 869 case '\177': 870 case '#': 871 if (len) { 872 --len; 873 --lp; 874 printf("\b \b"); 875 } 876 continue; 877 case '@': 878 case 'u'&037: 879 len = 0; 880 lp = cp; 881 printf("\n"); 882 continue; 883 default: 884 if (len + 1 >= size || c < ' ') { 885 printf("\007"); 886 continue; 887 } 888 printf("%c", c); 889 ++len; 890 *lp++ = c; 891 } 892 } 893 } 894