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