1 /* $OpenBSD: echo.c,v 1.50 2012/04/12 04:47:59 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Echo line reading and writing. 7 * 8 * Common routines for reading and writing characters in the echo line area 9 * of the display screen. Used by the entire known universe. 10 */ 11 12 #include "def.h" 13 #include "key.h" 14 #include "macro.h" 15 16 #include "funmap.h" 17 18 #include <stdarg.h> 19 #include <term.h> 20 21 static char *veread(const char *, char *, size_t, int, va_list); 22 static int complt(int, int, char *, size_t, int, int *); 23 static int complt_list(int, char *, int); 24 static void eformat(const char *, va_list); 25 static void eputi(int, int); 26 static void eputl(long, int); 27 static void eputs(const char *); 28 static void eputc(char); 29 static struct list *copy_list(struct list *); 30 31 int epresf = FALSE; /* stuff in echo line flag */ 32 33 /* 34 * Erase the echo line. 35 */ 36 void 37 eerase(void) 38 { 39 ttcolor(CTEXT); 40 ttmove(nrow - 1, 0); 41 tteeol(); 42 ttflush(); 43 epresf = FALSE; 44 } 45 46 /* 47 * Ask a "yes" or "no" question. Return ABORT if the user answers the 48 * question with the abort ("^G") character. Return FALSE for "no" and 49 * TRUE for "yes". No formatting services are available. No newline 50 * required. 51 */ 52 int 53 eyorn(const char *sp) 54 { 55 int s; 56 57 if (inmacro) 58 return (TRUE); 59 60 ewprintf("%s? (y or n) ", sp); 61 for (;;) { 62 s = getkey(FALSE); 63 if (s == 'y' || s == 'Y' || s == ' ') 64 return (TRUE); 65 if (s == 'n' || s == 'N' || s == CCHR('M')) 66 return (FALSE); 67 if (s == CCHR('G')) 68 return (ctrlg(FFRAND, 1)); 69 ewprintf("Please answer y or n. %s? (y or n) ", sp); 70 } 71 /* NOTREACHED */ 72 } 73 74 /* 75 * Like eyorn, but for more important questions. User must type all of 76 * "yes" or "no" and the trailing newline. 77 */ 78 int 79 eyesno(const char *sp) 80 { 81 char buf[64], *rep; 82 83 if (inmacro) 84 return (TRUE); 85 86 rep = eread("%s? (yes or no) ", buf, sizeof(buf), 87 EFNUL | EFNEW | EFCR, sp); 88 for (;;) { 89 if (rep == NULL) 90 return (ABORT); 91 if (rep[0] != '\0') { 92 if (macrodef) { 93 struct line *lp = maclcur; 94 95 maclcur = lp->l_bp; 96 maclcur->l_fp = lp->l_fp; 97 free(lp); 98 } 99 if ((rep[0] == 'y' || rep[0] == 'Y') && 100 (rep[1] == 'e' || rep[1] == 'E') && 101 (rep[2] == 's' || rep[2] == 'S') && 102 (rep[3] == '\0')) 103 return (TRUE); 104 if ((rep[0] == 'n' || rep[0] == 'N') && 105 (rep[1] == 'o' || rep[0] == 'O') && 106 (rep[2] == '\0')) 107 return (FALSE); 108 } 109 rep = eread("Please answer yes or no. %s? (yes or no) ", 110 buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp); 111 } 112 /* NOTREACHED */ 113 } 114 115 /* 116 * This is the general "read input from the echo line" routine. The basic 117 * idea is that the prompt string "prompt" is written to the echo line, and 118 * a one line reply is read back into the supplied "buf" (with maximum 119 * length "len"). 120 * XXX: When checking for an empty return value, always check rep, *not* buf 121 * as buf may be freed in pathological cases. 122 */ 123 /* VARARGS */ 124 char * 125 eread(const char *fmt, char *buf, size_t nbuf, int flag, ...) 126 { 127 va_list ap; 128 char *rep; 129 130 va_start(ap, flag); 131 rep = veread(fmt, buf, nbuf, flag, ap); 132 va_end(ap); 133 return (rep); 134 } 135 136 static char * 137 veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap) 138 { 139 int dynbuf = (buf == NULL); 140 int cpos, epos; /* cursor, end position in buf */ 141 int c, i, y; 142 int cplflag = FALSE; /* display completion list */ 143 int cwin = FALSE; /* completion list created */ 144 int mr = 0; /* match left arrow */ 145 int ml = 0; /* match right arrow */ 146 int esc = 0; /* position in esc pattern */ 147 struct buffer *bp; /* completion list buffer */ 148 struct mgwin *wp; /* window for compl list */ 149 int match; /* esc match found */ 150 int cc, rr; /* saved ttcol, ttrow */ 151 char *ret; /* return value */ 152 153 static char emptyval[] = ""; /* XXX hackish way to return err msg*/ 154 155 if (inmacro) { 156 if (dynbuf) { 157 if ((buf = malloc(maclcur->l_used + 1)) == NULL) 158 return (NULL); 159 } else if (maclcur->l_used >= nbuf) 160 return (NULL); 161 bcopy(maclcur->l_text, buf, maclcur->l_used); 162 buf[maclcur->l_used] = '\0'; 163 maclcur = maclcur->l_fp; 164 return (buf); 165 } 166 epos = cpos = 0; 167 ml = mr = esc = 0; 168 cplflag = FALSE; 169 170 if ((flag & EFNEW) != 0 || ttrow != nrow - 1) { 171 ttcolor(CTEXT); 172 ttmove(nrow - 1, 0); 173 epresf = TRUE; 174 } else 175 eputc(' '); 176 eformat(fp, ap); 177 if ((flag & EFDEF) != 0) { 178 if (buf == NULL) 179 return (NULL); 180 eputs(buf); 181 epos = cpos += strlen(buf); 182 } 183 tteeol(); 184 ttflush(); 185 for (;;) { 186 c = getkey(FALSE); 187 if ((flag & EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) { 188 if (cplflag == TRUE) { 189 complt_list(flag, buf, cpos); 190 cwin = TRUE; 191 } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) { 192 cplflag = TRUE; 193 epos += i; 194 cpos = epos; 195 } 196 continue; 197 } 198 cplflag = FALSE; 199 200 if (esc > 0) { /* ESC sequence started */ 201 match = 0; 202 if (ml == esc && key_left[ml] && c == key_left[ml]) { 203 match++; 204 if (key_left[++ml] == '\0') { 205 c = CCHR('B'); 206 esc = 0; 207 } 208 } 209 if (mr == esc && key_right[mr] && c == key_right[mr]) { 210 match++; 211 if (key_right[++mr] == '\0') { 212 c = CCHR('F'); 213 esc = 0; 214 } 215 } 216 if (match == 0) { 217 esc = 0; 218 continue; 219 /* hack. how do we know esc pattern is done? */ 220 } 221 if (esc > 0) { 222 esc++; 223 continue; 224 } 225 } 226 switch (c) { 227 case CCHR('A'): /* start of line */ 228 while (cpos > 0) { 229 if (ISCTRL(buf[--cpos]) != FALSE) { 230 ttputc('\b'); 231 --ttcol; 232 } 233 ttputc('\b'); 234 --ttcol; 235 } 236 ttflush(); 237 break; 238 case CCHR('D'): 239 if (cpos != epos) { 240 tteeol(); 241 y = buf[cpos]; 242 epos--; 243 rr = ttrow; 244 cc = ttcol; 245 for (i = cpos; i < epos; i++) { 246 buf[i] = buf[i + 1]; 247 eputc(buf[i]); 248 } 249 ttmove(rr, cc); 250 ttflush(); 251 } 252 break; 253 case CCHR('E'): /* end of line */ 254 while (cpos < epos) { 255 eputc(buf[cpos++]); 256 } 257 ttflush(); 258 break; 259 case CCHR('B'): /* back */ 260 if (cpos > 0) { 261 if (ISCTRL(buf[--cpos]) != FALSE) { 262 ttputc('\b'); 263 --ttcol; 264 } 265 ttputc('\b'); 266 --ttcol; 267 ttflush(); 268 } 269 break; 270 case CCHR('F'): /* forw */ 271 if (cpos < epos) { 272 eputc(buf[cpos++]); 273 ttflush(); 274 } 275 break; 276 case CCHR('Y'): /* yank from kill buffer */ 277 i = 0; 278 while ((y = kremove(i++)) >= 0 && y != '\n') { 279 int t; 280 if (dynbuf && epos + 1 >= nbuf) { 281 void *newp; 282 size_t newsize = epos + epos + 16; 283 if ((newp = realloc(buf, newsize)) 284 == NULL) 285 goto memfail; 286 buf = newp; 287 nbuf = newsize; 288 } 289 if (!dynbuf && epos + 1 >= nbuf) { 290 ewprintf("Line too long"); 291 return (emptyval); 292 } 293 for (t = epos; t > cpos; t--) 294 buf[t] = buf[t - 1]; 295 buf[cpos++] = (char)y; 296 epos++; 297 eputc((char)y); 298 cc = ttcol; 299 rr = ttrow; 300 for (t = cpos; t < epos; t++) 301 eputc(buf[t]); 302 ttmove(rr, cc); 303 } 304 ttflush(); 305 break; 306 case CCHR('K'): /* copy here-EOL to kill buffer */ 307 kdelete(); 308 for (i = cpos; i < epos; i++) 309 kinsert(buf[i], KFORW); 310 tteeol(); 311 epos = cpos; 312 ttflush(); 313 break; 314 case CCHR('['): 315 ml = mr = esc = 1; 316 break; 317 case CCHR('J'): 318 c = CCHR('M'); 319 /* FALLTHROUGH */ 320 case CCHR('M'): /* return, done */ 321 /* if there's nothing in the minibuffer, abort */ 322 if (epos == 0 && !(flag & EFNUL)) { 323 (void)ctrlg(FFRAND, 0); 324 ttflush(); 325 return (NULL); 326 } 327 if ((flag & EFFUNC) != 0) { 328 if (complt(flag, c, buf, nbuf, epos, &i) 329 == FALSE) 330 continue; 331 if (i > 0) 332 epos += i; 333 } 334 buf[epos] = '\0'; 335 if ((flag & EFCR) != 0) { 336 ttputc(CCHR('M')); 337 ttflush(); 338 } 339 if (macrodef) { 340 struct line *lp; 341 342 if ((lp = lalloc(cpos)) == NULL) 343 goto memfail; 344 lp->l_fp = maclcur->l_fp; 345 maclcur->l_fp = lp; 346 lp->l_bp = maclcur; 347 maclcur = lp; 348 bcopy(buf, lp->l_text, cpos); 349 } 350 ret = buf; 351 goto done; 352 case CCHR('G'): /* bell, abort */ 353 eputc(CCHR('G')); 354 (void)ctrlg(FFRAND, 0); 355 ttflush(); 356 ret = NULL; 357 goto done; 358 case CCHR('H'): /* rubout, erase */ 359 case CCHR('?'): 360 if (cpos != 0) { 361 y = buf[--cpos]; 362 epos--; 363 ttputc('\b'); 364 ttcol--; 365 if (ISCTRL(y) != FALSE) { 366 ttputc('\b'); 367 ttcol--; 368 } 369 rr = ttrow; 370 cc = ttcol; 371 for (i = cpos; i < epos; i++) { 372 buf[i] = buf[i + 1]; 373 eputc(buf[i]); 374 } 375 ttputc(' '); 376 if (ISCTRL(y) != FALSE) { 377 ttputc(' '); 378 ttputc('\b'); 379 } 380 ttputc('\b'); 381 ttmove(rr, cc); 382 ttflush(); 383 } 384 break; 385 case CCHR('X'): /* kill line */ 386 case CCHR('U'): 387 while (cpos != 0) { 388 ttputc('\b'); 389 ttputc(' '); 390 ttputc('\b'); 391 --ttcol; 392 if (ISCTRL(buf[--cpos]) != FALSE) { 393 ttputc('\b'); 394 ttputc(' '); 395 ttputc('\b'); 396 --ttcol; 397 } 398 epos--; 399 } 400 ttflush(); 401 break; 402 case CCHR('W'): /* kill to beginning of word */ 403 while ((cpos > 0) && !ISWORD(buf[cpos - 1])) { 404 ttputc('\b'); 405 ttputc(' '); 406 ttputc('\b'); 407 --ttcol; 408 if (ISCTRL(buf[--cpos]) != FALSE) { 409 ttputc('\b'); 410 ttputc(' '); 411 ttputc('\b'); 412 --ttcol; 413 } 414 epos--; 415 } 416 while ((cpos > 0) && ISWORD(buf[cpos - 1])) { 417 ttputc('\b'); 418 ttputc(' '); 419 ttputc('\b'); 420 --ttcol; 421 if (ISCTRL(buf[--cpos]) != FALSE) { 422 ttputc('\b'); 423 ttputc(' '); 424 ttputc('\b'); 425 --ttcol; 426 } 427 epos--; 428 } 429 ttflush(); 430 break; 431 case CCHR('\\'): 432 case CCHR('Q'): /* quote next */ 433 c = getkey(FALSE); 434 /* FALLTHROUGH */ 435 default: 436 if (dynbuf && epos + 1 >= nbuf) { 437 void *newp; 438 size_t newsize = epos + epos + 16; 439 if ((newp = realloc(buf, newsize)) == NULL) 440 goto memfail; 441 buf = newp; 442 nbuf = newsize; 443 } 444 if (!dynbuf && epos + 1 >= nbuf) { 445 ewprintf("Line too long"); 446 return (emptyval); 447 } 448 for (i = epos; i > cpos; i--) 449 buf[i] = buf[i - 1]; 450 buf[cpos++] = (char)c; 451 epos++; 452 eputc((char)c); 453 cc = ttcol; 454 rr = ttrow; 455 for (i = cpos; i < epos; i++) 456 eputc(buf[i]); 457 ttmove(rr, cc); 458 ttflush(); 459 } 460 } 461 done: 462 if (cwin == TRUE) { 463 /* blow away cpltion window */ 464 bp = bfind("*Completions*", TRUE); 465 if ((wp = popbuf(bp, WEPHEM)) != NULL) { 466 if (wp->w_flag & WEPHEM) { 467 curwp = wp; 468 delwind(FFRAND, 1); 469 } else { 470 killbuffer(bp); 471 } 472 } 473 } 474 return (ret); 475 memfail: 476 if (dynbuf && buf) 477 free(buf); 478 ewprintf("Out of memory"); 479 return (emptyval); 480 } 481 482 /* 483 * Do completion on a list of objects. 484 * c is SPACE, TAB, or CR 485 * return TRUE if matched (or partially matched) 486 * FALSE is result is ambiguous, 487 * ABORT on error. 488 */ 489 static int 490 complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx) 491 { 492 struct list *lh, *lh2; 493 struct list *wholelist = NULL; 494 int i, nxtra, nhits, bxtra, msglen, nshown; 495 int wflag = FALSE; 496 char *msg; 497 498 lh = lh2 = NULL; 499 500 if ((flags & EFFUNC) != 0) { 501 buf[cpos] = '\0'; 502 wholelist = lh = complete_function_list(buf); 503 } else if ((flags & EFBUF) != 0) { 504 lh = &(bheadp->b_list); 505 } else if ((flags & EFFILE) != 0) { 506 buf[cpos] = '\0'; 507 wholelist = lh = make_file_list(buf); 508 } else 509 panic("broken complt call: flags"); 510 511 if (c == ' ') 512 wflag = TRUE; 513 else if (c != '\t' && c != CCHR('M')) 514 panic("broken complt call: c"); 515 516 nhits = 0; 517 nxtra = HUGE; 518 519 for (; lh != NULL; lh = lh->l_next) { 520 if (memcmp(buf, lh->l_name, cpos) != 0) 521 continue; 522 if (nhits == 0) 523 lh2 = lh; 524 ++nhits; 525 if (lh->l_name[cpos] == '\0') 526 nxtra = -1; /* exact match */ 527 else { 528 bxtra = getxtra(lh, lh2, cpos, wflag); 529 if (bxtra < nxtra) 530 nxtra = bxtra; 531 lh2 = lh; 532 } 533 } 534 if (nhits == 0) 535 msg = " [No match]"; 536 else if (nhits > 1 && nxtra == 0) 537 msg = " [Ambiguous. Ctrl-G to cancel]"; 538 else { 539 /* 540 * Being lazy - ought to check length, but all things 541 * autocompleted have known types/lengths. 542 */ 543 if (nxtra < 0 && nhits > 1 && c == ' ') 544 nxtra = 1; /* ??? */ 545 for (i = 0; i < nxtra && cpos < nbuf; ++i) { 546 buf[cpos] = lh2->l_name[cpos]; 547 eputc(buf[cpos++]); 548 } 549 /* XXX should grow nbuf */ 550 ttflush(); 551 free_file_list(wholelist); 552 *nx = nxtra; 553 if (nxtra < 0 && c != CCHR('M')) /* exact */ 554 *nx = 0; 555 return (TRUE); 556 } 557 558 /* 559 * wholelist is NULL if we are doing buffers. Want to free lists 560 * that were created for us, but not the buffer list! 561 */ 562 free_file_list(wholelist); 563 564 /* Set up backspaces, etc., being mindful of echo line limit. */ 565 msglen = strlen(msg); 566 nshown = (ttcol + msglen + 2 > ncol) ? 567 ncol - ttcol - 2 : msglen; 568 eputs(msg); 569 ttcol -= (i = nshown); /* update ttcol! */ 570 while (i--) /* move back before msg */ 571 ttputc('\b'); 572 ttflush(); /* display to user */ 573 i = nshown; 574 while (i--) /* blank out on next flush */ 575 eputc(' '); 576 ttcol -= (i = nshown); /* update ttcol on BS's */ 577 while (i--) 578 ttputc('\b'); /* update ttcol again! */ 579 *nx = nxtra; 580 return ((nhits > 0) ? TRUE : FALSE); 581 } 582 583 /* 584 * Do completion on a list of objects, listing instead of completing. 585 */ 586 static int 587 complt_list(int flags, char *buf, int cpos) 588 { 589 struct list *lh, *lh2, *lh3; 590 struct list *wholelist = NULL; 591 struct buffer *bp; 592 int i, maxwidth, width; 593 int preflen = 0; 594 int oldrow = ttrow; 595 int oldcol = ttcol; 596 int oldhue = tthue; 597 char *linebuf; 598 size_t linesize, len; 599 char *cp; 600 601 lh = NULL; 602 603 ttflush(); 604 605 /* The results are put into a completion buffer. */ 606 bp = bfind("*Completions*", TRUE); 607 if (bclear(bp) == FALSE) 608 return (FALSE); 609 610 /* 611 * First get the list of objects. This list may contain only 612 * the ones that complete what has been typed, or may be the 613 * whole list of all objects of this type. They are filtered 614 * later in any case. Set wholelist if the list has been 615 * cons'ed up just for us, so we can free it later. We have 616 * to copy the buffer list for this function even though we 617 * didn't for complt. The sorting code does destructive 618 * changes to the list, which we don't want to happen to the 619 * main buffer list! 620 */ 621 if ((flags & EFBUF) != 0) 622 wholelist = lh = copy_list(&(bheadp->b_list)); 623 else if ((flags & EFFUNC) != 0) { 624 buf[cpos] = '\0'; 625 wholelist = lh = complete_function_list(buf); 626 } else if ((flags & EFFILE) != 0) { 627 buf[cpos] = '\0'; 628 wholelist = lh = make_file_list(buf); 629 /* 630 * We don't want to display stuff up to the / for file 631 * names preflen is the list of a prefix of what the 632 * user typed that should not be displayed. 633 */ 634 cp = strrchr(buf, '/'); 635 if (cp) 636 preflen = cp - buf + 1; 637 } else 638 panic("broken complt call: flags"); 639 640 /* 641 * Sort the list, since users expect to see it in alphabetic 642 * order. 643 */ 644 lh2 = lh; 645 while (lh2 != NULL) { 646 lh3 = lh2->l_next; 647 while (lh3 != NULL) { 648 if (strcmp(lh2->l_name, lh3->l_name) > 0) { 649 cp = lh2->l_name; 650 lh2->l_name = lh3->l_name; 651 lh3->l_name = cp; 652 } 653 lh3 = lh3->l_next; 654 } 655 lh2 = lh2->l_next; 656 } 657 658 /* 659 * First find max width of object to be displayed, so we can 660 * put several on a line. 661 */ 662 maxwidth = 0; 663 lh2 = lh; 664 while (lh2 != NULL) { 665 for (i = 0; i < cpos; ++i) { 666 if (buf[i] != lh2->l_name[i]) 667 break; 668 } 669 if (i == cpos) { 670 width = strlen(lh2->l_name); 671 if (width > maxwidth) 672 maxwidth = width; 673 } 674 lh2 = lh2->l_next; 675 } 676 maxwidth += 1 - preflen; 677 678 /* 679 * Now do the display. Objects are written into linebuf until 680 * it fills, and then put into the help buffer. 681 */ 682 linesize = MAX(ncol, maxwidth) + 1; 683 if ((linebuf = malloc(linesize)) == NULL) 684 return (FALSE); 685 width = 0; 686 687 /* 688 * We're going to strlcat() into the buffer, so it has to be 689 * NUL terminated. 690 */ 691 linebuf[0] = '\0'; 692 for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) { 693 for (i = 0; i < cpos; ++i) { 694 if (buf[i] != lh2->l_name[i]) 695 break; 696 } 697 /* if we have a match */ 698 if (i == cpos) { 699 /* if it wraps */ 700 if ((width + maxwidth) > ncol) { 701 addline(bp, linebuf); 702 linebuf[0] = '\0'; 703 width = 0; 704 } 705 len = strlcat(linebuf, lh2->l_name + preflen, 706 linesize); 707 width += maxwidth; 708 if (len < width && width < linesize) { 709 /* pad so the objects nicely line up */ 710 memset(linebuf + len, ' ', 711 maxwidth - strlen(lh2->l_name + preflen)); 712 linebuf[width] = '\0'; 713 } 714 } 715 } 716 if (width > 0) 717 addline(bp, linebuf); 718 free(linebuf); 719 720 /* 721 * Note that we free lists only if they are put in wholelist lists 722 * that were built just for us should be freed. However when we use 723 * the buffer list, obviously we don't want it freed. 724 */ 725 free_file_list(wholelist); 726 popbuftop(bp, WEPHEM); /* split the screen and put up the help 727 * buffer */ 728 update(); /* needed to make the new stuff actually 729 * appear */ 730 ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */ 731 ttcolor(oldhue); /* with arbitrary color */ 732 ttflush(); 733 return (0); 734 } 735 736 /* 737 * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal 738 * position in the name. Return the longest block of characters that can be 739 * autocompleted at this point. Sometimes the two symbols are the same, but 740 * this is normal. 741 */ 742 int 743 getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag) 744 { 745 int i; 746 747 i = cpos; 748 for (;;) { 749 if (lp1->l_name[i] != lp2->l_name[i]) 750 break; 751 if (lp1->l_name[i] == '\0') 752 break; 753 ++i; 754 if (wflag && !ISWORD(lp1->l_name[i - 1])) 755 break; 756 } 757 return (i - cpos); 758 } 759 760 /* 761 * Special "printf" for the echo line. Each call to "ewprintf" starts a 762 * new line in the echo area, and ends with an erase to end of the echo 763 * line. The formatting is done by a call to the standard formatting 764 * routine. 765 */ 766 /* VARARGS */ 767 void 768 ewprintf(const char *fmt, ...) 769 { 770 va_list ap; 771 772 if (inmacro) 773 return; 774 775 va_start(ap, fmt); 776 ttcolor(CTEXT); 777 ttmove(nrow - 1, 0); 778 eformat(fmt, ap); 779 va_end(ap); 780 tteeol(); 781 ttflush(); 782 epresf = TRUE; 783 } 784 785 /* 786 * Printf style formatting. This is called by both "ewprintf" and "ereply" 787 * to provide formatting services to their clients. The move to the start 788 * of the echo line, and the erase to the end of the echo line, is done by 789 * the caller. 790 * %c prints the "name" of the supplied character. 791 * %k prints the name of the current key (and takes no arguments). 792 * %d prints a decimal integer 793 * %o prints an octal integer 794 * %p prints a pointer 795 * %s prints a string 796 * %ld prints a long word 797 * Anything else is echoed verbatim 798 */ 799 static void 800 eformat(const char *fp, va_list ap) 801 { 802 char kname[NKNAME], tmp[100], *cp; 803 int c; 804 805 while ((c = *fp++) != '\0') { 806 if (c != '%') 807 eputc(c); 808 else { 809 c = *fp++; 810 switch (c) { 811 case 'c': 812 getkeyname(kname, sizeof(kname), 813 va_arg(ap, int)); 814 eputs(kname); 815 break; 816 817 case 'k': 818 for (cp = kname, c = 0; c < key.k_count; c++) { 819 if (c) 820 *cp++ = ' '; 821 cp = getkeyname(cp, sizeof(kname) - 822 (cp - kname) - 1, key.k_chars[c]); 823 } 824 eputs(kname); 825 break; 826 827 case 'd': 828 eputi(va_arg(ap, int), 10); 829 break; 830 831 case 'o': 832 eputi(va_arg(ap, int), 8); 833 break; 834 835 case 'p': 836 snprintf(tmp, sizeof(tmp), "%p", 837 va_arg(ap, void *)); 838 eputs(tmp); 839 break; 840 841 case 's': 842 eputs(va_arg(ap, char *)); 843 break; 844 845 case 'l': 846 /* explicit longword */ 847 c = *fp++; 848 switch (c) { 849 case 'd': 850 eputl(va_arg(ap, long), 10); 851 break; 852 default: 853 eputc(c); 854 break; 855 } 856 break; 857 858 default: 859 eputc(c); 860 } 861 } 862 } 863 } 864 865 /* 866 * Put integer, in radix "r". 867 */ 868 static void 869 eputi(int i, int r) 870 { 871 int q; 872 873 if (i < 0) { 874 eputc('-'); 875 i = -i; 876 } 877 if ((q = i / r) != 0) 878 eputi(q, r); 879 eputc(i % r + '0'); 880 } 881 882 /* 883 * Put long, in radix "r". 884 */ 885 static void 886 eputl(long l, int r) 887 { 888 long q; 889 890 if (l < 0) { 891 eputc('-'); 892 l = -l; 893 } 894 if ((q = l / r) != 0) 895 eputl(q, r); 896 eputc((int)(l % r) + '0'); 897 } 898 899 /* 900 * Put string. 901 */ 902 static void 903 eputs(const char *s) 904 { 905 int c; 906 907 while ((c = *s++) != '\0') 908 eputc(c); 909 } 910 911 /* 912 * Put character. Watch for control characters, and for the line getting 913 * too long. 914 */ 915 static void 916 eputc(char c) 917 { 918 if (ttcol + 2 < ncol) { 919 if (ISCTRL(c)) { 920 eputc('^'); 921 c = CCHR(c); 922 } 923 ttputc(c); 924 ++ttcol; 925 } 926 } 927 928 void 929 free_file_list(struct list *lp) 930 { 931 struct list *next; 932 933 while (lp) { 934 next = lp->l_next; 935 free(lp->l_name); 936 free(lp); 937 lp = next; 938 } 939 } 940 941 static struct list * 942 copy_list(struct list *lp) 943 { 944 struct list *current, *last, *nxt; 945 946 last = NULL; 947 while (lp) { 948 current = malloc(sizeof(struct list)); 949 if (current == NULL) { 950 /* Free what we have allocated so far */ 951 for (current = last; current; current = nxt) { 952 nxt = current->l_next; 953 free(current->l_name); 954 free(current); 955 } 956 return (NULL); 957 } 958 current->l_next = last; 959 current->l_name = strdup(lp->l_name); 960 last = current; 961 lp = lp->l_next; 962 } 963 return (last); 964 } 965