1: /* Copyright (c) 1981 Regents of the University of California */
   2: static char *sccsid = "@(#)ex_cmdsub.c	7.1	7/8/81";
   3: #include "ex.h"
   4: #include "ex_argv.h"
   5: #include "ex_temp.h"
   6: #include "ex_tty.h"
   7: #include "ex_vis.h"
   8: 
   9: /*
  10:  * Command mode subroutines implementing
  11:  *	append, args, copy, delete, join, move, put,
  12:  *	shift, tag, yank, z and undo
  13:  */
  14: 
  15: bool    endline = 1;
  16: line    *tad1;
  17: static  jnoop();
  18: 
  19: /*
  20:  * Append after line a lines returned by function f.
  21:  * Be careful about intermediate states to avoid scramble
  22:  * if an interrupt comes in.
  23:  */
  24: append(f, a)
  25:     int (*f)();
  26:     line *a;
  27: {
  28:     register line *a1, *a2, *rdot;
  29:     int nline;
  30: 
  31:     nline = 0;
  32:     dot = a;
  33:     if(FIXUNDO && !inopen && f!=getsub) {
  34:         undap1 = undap2 = dot + 1;
  35:         undkind = UNDCHANGE;
  36:     }
  37:     while ((*f)() == 0) {
  38:         if (truedol >= endcore) {
  39:             if (morelines() < 0) {
  40:                 if (FIXUNDO && f == getsub) {
  41:                     undap1 = addr1;
  42:                     undap2 = addr2 + 1;
  43:                 }
  44:                 error("Out of memory@- too many lines in file");
  45:             }
  46:         }
  47:         nline++;
  48:         a1 = truedol + 1;
  49:         a2 = a1 + 1;
  50:         dot++;
  51:         undap2++;
  52:         dol++;
  53:         unddol++;
  54:         truedol++;
  55:         for (rdot = dot; a1 > rdot;)
  56:             *--a2 = *--a1;
  57:         *rdot = 0;
  58:         putmark(rdot);
  59:         if (f == gettty) {
  60:             dirtcnt++;
  61:             TSYNC();
  62:         }
  63:     }
  64:     return (nline);
  65: }
  66: 
  67: appendnone()
  68: {
  69: 
  70:     if(FIXUNDO) {
  71:         undkind = UNDCHANGE;
  72:         undap1 = undap2 = addr1;
  73:     }
  74: }
  75: 
  76: /*
  77:  * Print out the argument list, with []'s around the current name.
  78:  */
  79: pargs()
  80: {
  81:     register char **av = argv0, *as = args0;
  82:     register int ac;
  83: 
  84:     for (ac = 0; ac < argc0; ac++) {
  85:         if (ac != 0)
  86:             putchar(' ');
  87:         if (ac + argc == argc0 - 1)
  88:             printf("[");
  89:         lprintf("%s", as);
  90:         if (ac + argc == argc0 - 1)
  91:             printf("]");
  92:         as = av ? *++av : strend(as) + 1;
  93:     }
  94:     noonl();
  95: }
  96: 
  97: /*
  98:  * Delete lines; two cases are if we are really deleting,
  99:  * more commonly we are just moving lines to the undo save area.
 100:  */
 101: delete(hush)
 102:     bool hush;
 103: {
 104:     register line *a1, *a2;
 105: 
 106:     nonzero();
 107:     if(FIXUNDO) {
 108:         register int (*dsavint)();
 109: 
 110: #ifdef TRACE
 111:         if (trace)
 112:             vudump("before delete");
 113: #endif
 114:         change();
 115:         dsavint = signal(SIGINT, SIG_IGN);
 116:         undkind = UNDCHANGE;
 117:         a1 = addr1;
 118:         squish();
 119:         a2 = addr2;
 120:         if (a2++ != dol) {
 121:             reverse(a1, a2);
 122:             reverse(a2, dol + 1);
 123:             reverse(a1, dol + 1);
 124:         }
 125:         dol -= a2 - a1;
 126:         unddel = a1 - 1;
 127:         if (a1 > dol)
 128:             a1 = dol;
 129:         dot = a1;
 130:         pkill[0] = pkill[1] = 0;
 131:         signal(SIGINT, dsavint);
 132: #ifdef TRACE
 133:         if (trace)
 134:             vudump("after delete");
 135: #endif
 136:     } else {
 137:         register line *a3;
 138:         register int i;
 139: 
 140:         change();
 141:         a1 = addr1;
 142:         a2 = addr2 + 1;
 143:         a3 = truedol;
 144:         i = a2 - a1;
 145:         unddol -= i;
 146:         undap2 -= i;
 147:         dol -= i;
 148:         truedol -= i;
 149:         do
 150:             *a1++ = *a2++;
 151:         while (a2 <= a3);
 152:         a1 = addr1;
 153:         if (a1 > dol)
 154:             a1 = dol;
 155:         dot = a1;
 156:     }
 157:     if (!hush)
 158:         killed();
 159: }
 160: 
 161: deletenone()
 162: {
 163: 
 164:     if(FIXUNDO) {
 165:         undkind = UNDCHANGE;
 166:         squish();
 167:         unddel = addr1;
 168:     }
 169: }
 170: 
 171: /*
 172:  * Crush out the undo save area, moving the open/visual
 173:  * save area down in its place.
 174:  */
 175: squish()
 176: {
 177:     register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
 178: 
 179:     if(FIXUNDO) {
 180:         if (inopen == -1)
 181:             return;
 182:         if (a1 < a2 && a2 < a3)
 183:             do
 184:                 *a1++ = *a2++;
 185:             while (a2 < a3);
 186:         truedol -= unddol - dol;
 187:         unddol = dol;
 188:     }
 189: }
 190: 
 191: /*
 192:  * Join lines.  Special hacks put in spaces, two spaces if
 193:  * preceding line ends with '.', or no spaces if next line starts with ).
 194:  */
 195: static  int jcount, jnoop();
 196: 
 197: join(c)
 198:     int c;
 199: {
 200:     register line *a1;
 201:     register char *cp, *cp1;
 202: 
 203:     cp = genbuf;
 204:     *cp = 0;
 205:     for (a1 = addr1; a1 <= addr2; a1++) {
 206:         getline(*a1);
 207:         cp1 = linebuf;
 208:         if (a1 != addr1 && c == 0) {
 209:             while (*cp1 == ' ' || *cp1 == '\t')
 210:                 cp1++;
 211:             if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
 212:                 if (*cp1 != ')') {
 213:                     *cp++ = ' ';
 214:                     if (cp[-2] == '.')
 215:                         *cp++ = ' ';
 216:                 }
 217:             }
 218:         }
 219:         while (*cp++ = *cp1++)
 220:             if (cp > &genbuf[LBSIZE-2])
 221:                 error("Line overflow|Result line of join would be too long");
 222:         cp--;
 223:     }
 224:     strcLIN(genbuf);
 225:     delete(0);
 226:     jcount = 1;
 227:     if (FIXUNDO)
 228:         undap1 = undap2 = addr1;
 229:     ignore(append(jnoop, --addr1));
 230:     if (FIXUNDO)
 231:         vundkind = VMANY;
 232: }
 233: 
 234: static
 235: jnoop()
 236: {
 237: 
 238:     return(--jcount);
 239: }
 240: 
 241: /*
 242:  * Move and copy lines.  Hard work is done by move1 which
 243:  * is also called by undo.
 244:  */
 245: int getcopy();
 246: 
 247: move()
 248: {
 249:     register line *adt;
 250:     bool iscopy = 0;
 251: 
 252:     if (Command[0] == 'm') {
 253:         setdot1();
 254:         markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
 255:     } else {
 256:         iscopy++;
 257:         setdot();
 258:     }
 259:     nonzero();
 260:     adt = address((char*)0);
 261:     if (adt == 0)
 262:         serror("%s where?|%s requires a trailing address", Command);
 263:     newline();
 264:     move1(iscopy, adt);
 265:     killed();
 266: }
 267: 
 268: move1(cflag, addrt)
 269:     int cflag;
 270:     line *addrt;
 271: {
 272:     register line *adt, *ad1, *ad2;
 273:     int lines;
 274: 
 275:     adt = addrt;
 276:     lines = (addr2 - addr1) + 1;
 277:     if (cflag) {
 278:         tad1 = addr1;
 279:         ad1 = dol;
 280:         ignore(append(getcopy, ad1++));
 281:         ad2 = dol;
 282:     } else {
 283:         ad2 = addr2;
 284:         for (ad1 = addr1; ad1 <= ad2;)
 285:             *ad1++ &= ~01;
 286:         ad1 = addr1;
 287:     }
 288:     ad2++;
 289:     if (adt < ad1) {
 290:         if (adt + 1 == ad1 && !cflag && !inglobal)
 291:             error("That move would do nothing!");
 292:         dot = adt + (ad2 - ad1);
 293:         if (++adt != ad1) {
 294:             reverse(adt, ad1);
 295:             reverse(ad1, ad2);
 296:             reverse(adt, ad2);
 297:         }
 298:     } else if (adt >= ad2) {
 299:         dot = adt++;
 300:         reverse(ad1, ad2);
 301:         reverse(ad2, adt);
 302:         reverse(ad1, adt);
 303:     } else
 304:         error("Move to a moved line");
 305:     change();
 306:     if (!inglobal)
 307:         if(FIXUNDO) {
 308:             if (cflag) {
 309:                 undap1 = addrt + 1;
 310:                 undap2 = undap1 + lines;
 311:                 deletenone();
 312:             } else {
 313:                 undkind = UNDMOVE;
 314:                 undap1 = addr1;
 315:                 undap2 = addr2;
 316:                 unddel = addrt;
 317:                 squish();
 318:             }
 319:         }
 320: }
 321: 
 322: getcopy()
 323: {
 324: 
 325:     if (tad1 > addr2)
 326:         return (EOF);
 327:     getline(*tad1++);
 328:     return (0);
 329: }
 330: 
 331: /*
 332:  * Put lines in the buffer from the undo save area.
 333:  */
 334: getput()
 335: {
 336: 
 337:     if (tad1 > unddol)
 338:         return (EOF);
 339:     getline(*tad1++);
 340:     tad1++;
 341:     return (0);
 342: }
 343: 
 344: put()
 345: {
 346:     register int cnt;
 347: 
 348:     if (!FIXUNDO)
 349:         error("Cannot put inside global/macro");
 350:     cnt = unddol - dol;
 351:     if (cnt && inopen && pkill[0] && pkill[1]) {
 352:         pragged(1);
 353:         return;
 354:     }
 355:     tad1 = dol + 1;
 356:     ignore(append(getput, addr2));
 357:     undkind = UNDPUT;
 358:     notecnt = cnt;
 359:     netchange(cnt);
 360: }
 361: 
 362: /*
 363:  * A tricky put, of a group of lines in the middle
 364:  * of an existing line.  Only from open/visual.
 365:  * Argument says pkills have meaning, e.g. called from
 366:  * put; it is 0 on calls from putreg.
 367:  */
 368: pragged(kill)
 369:     bool kill;
 370: {
 371:     extern char *cursor;
 372:     register char *gp = &genbuf[cursor - linebuf];
 373: 
 374:     /*
 375: 	 * This kind of stuff is TECO's forte.
 376: 	 * We just grunge along, since it cuts
 377: 	 * across our line-oriented model of the world
 378: 	 * almost scrambling our addled brain.
 379: 	 */
 380:     if (!kill)
 381:         getDOT();
 382:     strcpy(genbuf, linebuf);
 383:     getline(*unddol);
 384:     if (kill)
 385:         *pkill[1] = 0;
 386:     strcat(linebuf, gp);
 387:     putmark(unddol);
 388:     getline(dol[1]);
 389:     if (kill)
 390:         strcLIN(pkill[0]);
 391:     strcpy(gp, linebuf);
 392:     strcLIN(genbuf);
 393:     putmark(dol+1);
 394:     undkind = UNDCHANGE;
 395:     undap1 = dot;
 396:     undap2 = dot + 1;
 397:     unddel = dot - 1;
 398:     undo(1);
 399: }
 400: 
 401: /*
 402:  * Shift lines, based on c.
 403:  * If c is neither < nor >, then this is a lisp aligning =.
 404:  */
 405: shift(c, cnt)
 406:     int c;
 407:     int cnt;
 408: {
 409:     register line *addr;
 410:     register char *cp;
 411:     char *dp;
 412:     register int i;
 413: 
 414:     if(FIXUNDO)
 415:         save12(), undkind = UNDCHANGE;
 416:     cnt *= value(SHIFTWIDTH);
 417:     for (addr = addr1; addr <= addr2; addr++) {
 418:         dot = addr;
 419: #ifdef LISPCODE
 420:         if (c == '=' && addr == addr1 && addr != addr2)
 421:             continue;
 422: #endif
 423:         getDOT();
 424:         i = whitecnt(linebuf);
 425:         switch (c) {
 426: 
 427:         case '>':
 428:             if (linebuf[0] == 0)
 429:                 continue;
 430:             cp = genindent(i + cnt);
 431:             break;
 432: 
 433:         case '<':
 434:             if (i == 0)
 435:                 continue;
 436:             i -= cnt;
 437:             cp = i > 0 ? genindent(i) : genbuf;
 438:             break;
 439: 
 440: #ifdef LISPCODE
 441:         default:
 442:             i = lindent(addr);
 443:             getDOT();
 444:             cp = genindent(i);
 445:             break;
 446: #endif
 447:         }
 448:         if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
 449:             error("Line too long|Result line after shift would be too long");
 450:         CP(cp, dp);
 451:         strcLIN(genbuf);
 452:         putmark(addr);
 453:     }
 454:     killed();
 455: }
 456: 
 457: /*
 458:  * Find a tag in the tags file.
 459:  * Most work here is in parsing the tags file itself.
 460:  */
 461: tagfind(quick)
 462:     bool quick;
 463: {
 464:     char cmdbuf[BUFSIZ];
 465:     char filebuf[FNSIZE];
 466:     char tagfbuf[128];
 467:     register int c, d;
 468:     bool samef = 1;
 469:     int tfcount = 0;
 470:     int omagic;
 471:     char *fn, *fne;
 472: #ifdef STDIO        /* mjm: was VMUNIX */
 473:     /*
 474: 	 * We have lots of room so we bring in stdio and do
 475: 	 * a binary search on the tags file.
 476: 	 */
 477: # undef EOF
 478: # include <stdio.h>
 479: # undef getchar
 480: # undef putchar
 481:     FILE *iof;
 482:     char iofbuf[BUFSIZ];
 483:     long mid;   /* assumed byte offset */
 484:     long top, bot;  /* length of tag file */
 485:     struct stat sbuf;
 486: #endif
 487: 
 488:     omagic = value(MAGIC);
 489:     if (!skipend()) {
 490:         register char *lp = lasttag;
 491: 
 492:         while (!iswhite(peekchar()) && !endcmd(peekchar()))
 493:             if (lp < &lasttag[sizeof lasttag - 2])
 494:                 *lp++ = getchar();
 495:             else
 496:                 ignchar();
 497:         *lp++ = 0;
 498:         if (!endcmd(peekchar()))
 499: badtag:
 500:             error("Bad tag|Give one tag per line");
 501:     } else if (lasttag[0] == 0)
 502:         error("No previous tag");
 503:     c = getchar();
 504:     if (!endcmd(c))
 505:         goto badtag;
 506:     if (c == EOF)
 507:         ungetchar(c);
 508:     clrstats();
 509: 
 510:     /*
 511: 	 * Loop once for each file in tags "path".
 512: 	 */
 513:     CP(tagfbuf, svalue(TAGS));
 514:     fne = tagfbuf - 1;
 515:     while (fne) {
 516:         fn = ++fne;
 517:         while (*fne && *fne != ' ')
 518:             fne++;
 519:         if (*fne == 0)
 520:             fne = 0;    /* done, quit after this time */
 521:         else
 522:             *fne = 0;   /* null terminate filename */
 523: #ifdef STDIO        /* mjm: was VMUNIX */
 524:         iof = fopen(fn, "r");
 525:         if (iof == NULL)
 526:             continue;
 527:         tfcount++;
 528:         setbuf(iof, iofbuf);
 529:         fstat(fileno(iof), &sbuf);
 530:         top = sbuf.st_size;
 531:         if (top == 0L || iof == NULL)
 532:             top = -1L;
 533:         bot = 0L;
 534:         while (top >= bot) {
 535: #else
 536:         /*
 537: 		 * Avoid stdio and scan tag file linearly.
 538: 		 */
 539:         io = open(fn, 0);
 540:         if (io<0)
 541:             continue;
 542:         tfcount++;
 543:         while (getfile() == 0) {
 544: #endif
 545:             /* loop for each tags file entry */
 546:             register char *cp = linebuf;
 547:             register char *lp = lasttag;
 548:             char *oglobp;
 549: 
 550: #ifdef STDIO        /* mjm: was VMUNIX */
 551:             mid = (top + bot) / 2;
 552:             fseek(iof, mid, 0);
 553:             if (mid > 0)    /* to get first tag in file to work */
 554:                 /* scan to next \n */
 555:                 if(fgets(linebuf, sizeof linebuf, iof)==NULL)
 556:                     goto goleft;
 557:             /* get the line itself */
 558:             if(fgets(linebuf, sizeof linebuf, iof)==NULL)
 559:                 goto goleft;
 560:             linebuf[strlen(linebuf)-1] = 0; /* was '\n' */
 561: #endif
 562:             while (*cp && *lp == *cp)
 563:                 cp++, lp++;
 564:             if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || lp-lasttag < value(TAGLENGTH))) {
 565: #ifdef STDIO        /* mjm: was VMUNIX */
 566:                 if (*lp > *cp)
 567:                     bot = mid + 1;
 568:                 else
 569: goleft:
 570:                     top = mid - 1;
 571: #endif
 572:                 /* Not this tag.  Try the next */
 573:                 continue;
 574:             }
 575: 
 576:             /*
 577: 			 * We found the tag.  Decode the line in the file.
 578: 			 */
 579: #ifdef STDIO        /* mjm: was VMUNIX */
 580:             fclose(iof);
 581: #else
 582:             close(io);
 583: #endif
 584:             /* Rest of tag if abbreviated */
 585:             while (!iswhite(*cp))
 586:                 cp++;
 587: 
 588:             /* name of file */
 589:             while (*cp && iswhite(*cp))
 590:                 cp++;
 591:             if (!*cp)
 592: badtags:
 593:                 serror("%s: Bad tags file entry", lasttag);
 594:             lp = filebuf;
 595:             while (*cp && *cp != ' ' && *cp != '\t') {
 596:                 if (lp < &filebuf[sizeof filebuf - 2])
 597:                     *lp++ = *cp;
 598:                 cp++;
 599:             }
 600:             *lp++ = 0;
 601: 
 602:             if (*cp == 0)
 603:                 goto badtags;
 604:             if (dol != zero) {
 605:                 /*
 606: 				 * Save current position in 't for ^^ in visual.
 607: 				 */
 608:                 names['t'-'a'] = *dot &~ 01;
 609:                 if (inopen) {
 610:                     extern char *ncols['z'-'a'+2];
 611:                     extern char *cursor;
 612: 
 613:                     ncols['t'-'a'] = cursor;
 614:                 }
 615:             }
 616:             strcpy(cmdbuf, cp);
 617:             if (strcmp(filebuf, savedfile) || !edited) {
 618:                 char cmdbuf2[sizeof filebuf + 10];
 619: 
 620:                 /* Different file.  Do autowrite & get it. */
 621:                 if (!quick) {
 622:                     ckaw();
 623:                     if (chng && dol > zero)
 624:                         error("No write@since last change (:tag! overrides)");
 625:                 }
 626:                 oglobp = globp;
 627:                 strcpy(cmdbuf2, "e! ");
 628:                 strcat(cmdbuf2, filebuf);
 629:                 globp = cmdbuf2;
 630:                 d = peekc; ungetchar(0);
 631:                 commands(1, 1);
 632:                 peekc = d;
 633:                 globp = oglobp;
 634:                 value(MAGIC) = omagic;
 635:                 samef = 0;
 636:             }
 637: 
 638:             /*
 639: 			 * Look for pattern in the current file.
 640: 			 */
 641:             oglobp = globp;
 642:             globp = cmdbuf;
 643:             d = peekc; ungetchar(0);
 644:             if (samef)
 645:                 markpr(dot);
 646:             /*
 647: 			 * BUG: if it isn't found (user edited header
 648: 			 * line) we get left in nomagic mode.
 649: 			 */
 650:             value(MAGIC) = 0;
 651:             commands(1, 1);
 652:             peekc = d;
 653:             globp = oglobp;
 654:             value(MAGIC) = omagic;
 655:             return;
 656:         }   /* end of "for each tag in file" */
 657: 
 658:         /*
 659: 		 * No such tag in this file.  Close it and try the next.
 660: 		 */
 661: #ifdef STDIO        /* mjm: was VMUNIX */
 662:         fclose(iof);
 663: #else
 664:         close(io);
 665: #endif
 666:     }   /* end of "for each file in path" */
 667:     if (tfcount <= 0)
 668:         error("No tags file");
 669:     else
 670:         serror("%s: No such tag@in tags file", lasttag);
 671: }
 672: 
 673: /*
 674:  * Save lines from addr1 thru addr2 as though
 675:  * they had been deleted.
 676:  */
 677: yank()
 678: {
 679: 
 680:     if (!FIXUNDO)
 681:         error("Can't yank inside global/macro");
 682:     save12();
 683:     undkind = UNDNONE;
 684:     killcnt(addr2 - addr1 + 1);
 685: }
 686: 
 687: /*
 688:  * z command; print windows of text in the file.
 689:  *
 690:  * If this seems unreasonably arcane, the reasons
 691:  * are historical.  This is one of the first commands
 692:  * added to the first ex (then called en) and the
 693:  * number of facilities here were the major advantage
 694:  * of en over ed since they allowed more use to be
 695:  * made of fast terminals w/o typing .,.22p all the time.
 696:  */
 697: bool    zhadpr;
 698: bool    znoclear;
 699: short   zweight;
 700: 
 701: zop(hadpr)
 702:     int hadpr;
 703: {
 704:     register int c, lines, op;
 705:     bool excl;
 706: 
 707:     zhadpr = hadpr;
 708:     notempty();
 709:     znoclear = 0;
 710:     zweight = 0;
 711:     excl = exclam();
 712:     switch (c = op = getchar()) {
 713: 
 714:     case '^':
 715:         zweight = 1;
 716:     case '-':
 717:     case '+':
 718:         while (peekchar() == op) {
 719:             ignchar();
 720:             zweight++;
 721:         }
 722:     case '=':
 723:     case '.':
 724:         c = getchar();
 725:         break;
 726: 
 727:     case EOF:
 728:         znoclear++;
 729:         break;
 730: 
 731:     default:
 732:         op = 0;
 733:         break;
 734:     }
 735:     if (isdigit(c)) {
 736:         lines = c - '0';
 737:         for(;;) {
 738:             c = getchar();
 739:             if (!isdigit(c))
 740:                 break;
 741:             lines *= 10;
 742:             lines += c - '0';
 743:         }
 744:         if (lines < LINES)
 745:             znoclear++;
 746:         value(WINDOW) = lines;
 747:         if (op == '=')
 748:             lines += 2;
 749:     } else
 750:         lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
 751:     if (inopen || c != EOF) {
 752:         ungetchar(c);
 753:         newline();
 754:     }
 755:     addr1 = addr2;
 756:     if (addr2 == 0 && dot < dol && op == 0)
 757:         addr1 = addr2 = dot+1;
 758:     setdot();
 759:     zop2(lines, op);
 760: }
 761: 
 762: zop2(lines, op)
 763:     register int lines;
 764:     register int op;
 765: {
 766:     register line *split;
 767: 
 768:     split = NULL;
 769:     switch (op) {
 770: 
 771:     case EOF:
 772:         if (addr2 == dol)
 773:             error("\nAt EOF");
 774:     case '+':
 775:         if (addr2 == dol)
 776:             error("At EOF");
 777:         addr2 += lines * zweight;
 778:         if (addr2 > dol)
 779:             error("Hit BOTTOM");
 780:         addr2++;
 781:     default:
 782:         addr1 = addr2;
 783:         addr2 += lines-1;
 784:         dot = addr2;
 785:         break;
 786: 
 787:     case '=':
 788:     case '.':
 789:         znoclear = 0;
 790:         lines--;
 791:         lines >>= 1;
 792:         if (op == '=')
 793:             lines--;
 794:         addr1 = addr2 - lines;
 795:         if (op == '=')
 796:             dot = split = addr2;
 797:         addr2 += lines;
 798:         if (op == '.') {
 799:             markDOT();
 800:             dot = addr2;
 801:         }
 802:         break;
 803: 
 804:     case '^':
 805:     case '-':
 806:         addr2 -= lines * zweight;
 807:         if (addr2 < one)
 808:             error("Hit TOP");
 809:         lines--;
 810:         addr1 = addr2 - lines;
 811:         dot = addr2;
 812:         break;
 813:     }
 814:     if (addr1 <= zero)
 815:         addr1 = one;
 816:     if (addr2 > dol)
 817:         addr2 = dol;
 818:     if (dot > dol)
 819:         dot = dol;
 820:     if (addr1 > addr2)
 821:         return;
 822:     if (op == EOF && zhadpr) {
 823:         getline(*addr1);
 824:         putchar('\r' | QUOTE);
 825:         shudclob = 1;
 826:     } else if (znoclear == 0 && CL != NOSTR && !inopen) {
 827:         flush1();
 828:         vclear();
 829:     }
 830:     if (addr2 - addr1 > 1)
 831:         pstart();
 832:     if (split) {
 833:         plines(addr1, split - 1, 0);
 834:         splitit();
 835:         plines(split, split, 0);
 836:         splitit();
 837:         addr1 = split + 1;
 838:     }
 839:     plines(addr1, addr2, 0);
 840: }
 841: 
 842: static
 843: splitit()
 844: {
 845:     register int l;
 846: 
 847:     for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
 848:         putchar('-');
 849:     putnl();
 850: }
 851: 
 852: plines(adr1, adr2, movedot)
 853:     line *adr1;
 854:     register line *adr2;
 855:     bool movedot;
 856: {
 857:     register line *addr;
 858: 
 859:     pofix();
 860:     for (addr = adr1; addr <= adr2; addr++) {
 861:         getline(*addr);
 862:         pline(lineno(addr));
 863:         if (inopen)
 864:             putchar('\n' | QUOTE);
 865:         if (movedot)
 866:             dot = addr;
 867:     }
 868: }
 869: 
 870: pofix()
 871: {
 872: 
 873:     if (inopen && Outchar != termchar) {
 874:         vnfl();
 875:         setoutt();
 876:     }
 877: }
 878: 
 879: /*
 880:  * Dudley doright to the rescue.
 881:  * Undo saves the day again.
 882:  * A tip of the hatlo hat to Warren Teitleman
 883:  * who made undo as useful as do.
 884:  *
 885:  * Command level undo works easily because
 886:  * the editor has a unique temporary file
 887:  * index for every line which ever existed.
 888:  * We don't have to save large blocks of text,
 889:  * only the indices which are small.  We do this
 890:  * by moving them to after the last line in the
 891:  * line buffer array, and marking down info
 892:  * about whence they came.
 893:  *
 894:  * Undo is its own inverse.
 895:  */
 896: undo(c)
 897:     bool c;
 898: {
 899:     register int i;
 900:     register line *jp, *kp;
 901:     line *dolp1, *newdol, *newadot;
 902: 
 903: #ifdef TRACE
 904:     if (trace)
 905:         vudump("before undo");
 906: #endif
 907:     if (inglobal && inopen <= 0)
 908:         error("Can't undo in global@commands");
 909:     if (!c)
 910:         somechange();
 911:     pkill[0] = pkill[1] = 0;
 912:     change();
 913:     if (undkind == UNDMOVE) {
 914:         /*
 915: 		 * Command to be undone is a move command.
 916: 		 * This is handled as a special case by noting that
 917: 		 * a move "a,b m c" can be inverted by another move.
 918: 		 */
 919:         if ((i = (jp = unddel) - undap2) > 0) {
 920:             /*
 921: 			 * when c > b inverse is a+(c-b),c m a-1
 922: 			 */
 923:             addr2 = jp;
 924:             addr1 = (jp = undap1) + i;
 925:             unddel = jp-1;
 926:         } else {
 927:             /*
 928: 			 * when b > c inverse is  c+1,c+1+(b-a) m b
 929: 			 */
 930:             addr1 = ++jp;
 931:             addr2 = jp + ((unddel = undap2) - undap1);
 932:         }
 933:         kp = undap1;
 934:         move1(0, unddel);
 935:         dot = kp;
 936:         Command = "move";
 937:         killed();
 938:     } else {
 939:         int cnt;
 940: 
 941:         newadot = dot;
 942:         cnt = lineDOL();
 943:         newdol = dol;
 944:         dolp1 = dol + 1;
 945:         /*
 946: 		 * Command to be undone is a non-move.
 947: 		 * All such commands are treated as a combination of
 948: 		 * a delete command and a append command.
 949: 		 * We first move the lines appended by the last command
 950: 		 * from undap1 to undap2-1 so that they are just before the
 951: 		 * saved deleted lines.
 952: 		 */
 953:         if ((i = (kp = undap2) - (jp = undap1)) > 0) {
 954:             if (kp != dolp1) {
 955:                 reverse(jp, kp);
 956:                 reverse(kp, dolp1);
 957:                 reverse(jp, dolp1);
 958:             }
 959:             /*
 960: 			 * Account for possible backward motion of target
 961: 			 * for restoration of saved deleted lines.
 962: 			 */
 963:             if (unddel >= jp)
 964:                 unddel -= i;
 965:             newdol -= i;
 966:             /*
 967: 			 * For the case where no lines are restored, dot
 968: 			 * is the line before the first line deleted.
 969: 			 */
 970:             dot = jp-1;
 971:         }
 972:         /*
 973: 		 * Now put the deleted lines, if any, back where they were.
 974: 		 * Basic operation is: dol+1,unddol m unddel
 975: 		 */
 976:         if (undkind == UNDPUT) {
 977:             unddel = undap1 - 1;
 978:             squish();
 979:         }
 980:         jp = unddel + 1;
 981:         if ((i = (kp = unddol) - dol) > 0) {
 982:             if (jp != dolp1) {
 983:                 reverse(jp, dolp1);
 984:                 reverse(dolp1, ++kp);
 985:                 reverse(jp, kp);
 986:             }
 987:             /*
 988: 			 * Account for possible forward motion of the target
 989: 			 * for restoration of the deleted lines.
 990: 			 */
 991:             if (undap1 >= jp)
 992:                 undap1 += i;
 993:             /*
 994: 			 * Dot is the first resurrected line.
 995: 			 */
 996:             dot = jp;
 997:             newdol += i;
 998:         }
 999:         /*
1000: 		 * Clean up so we are invertible
1001: 		 */
1002:         unddel = undap1 - 1;
1003:         undap1 = jp;
1004:         undap2 = jp + i;
1005:         dol = newdol;
1006:         netchHAD(cnt);
1007:         if (undkind == UNDALL) {
1008:             dot = undadot;
1009:             undadot = newadot;
1010:         } else
1011:             undkind = UNDCHANGE;
1012:     }
1013:     /*
1014: 	 * Defensive programming - after a munged undadot.
1015: 	 * Also handle empty buffer case.
1016: 	 */
1017:     if ((dot <= zero || dot > dol) && dot != dol)
1018:         dot = one;
1019: #ifdef TRACE
1020:     if (trace)
1021:         vudump("after undo");
1022: #endif
1023: }
1024: 
1025: /*
1026:  * Be (almost completely) sure there really
1027:  * was a change, before claiming to undo.
1028:  */
1029: somechange()
1030: {
1031:     register line *ip, *jp;
1032: 
1033:     switch (undkind) {
1034: 
1035:     case UNDMOVE:
1036:         return;
1037: 
1038:     case UNDCHANGE:
1039:         if (undap1 == undap2 && dol == unddol)
1040:             break;
1041:         return;
1042: 
1043:     case UNDPUT:
1044:         if (undap1 != undap2)
1045:             return;
1046:         break;
1047: 
1048:     case UNDALL:
1049:         if (unddol - dol != lineDOL())
1050:             return;
1051:         for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
1052:             if ((*ip &~ 01) != (*jp &~ 01))
1053:                 return;
1054:         break;
1055: 
1056:     case UNDNONE:
1057:         error("Nothing to undo");
1058:     }
1059:     error("Nothing changed|Last undoable command didn't change anything");
1060: }
1061: 
1062: /*
1063:  * Map command:
1064:  * map src dest
1065:  */
1066: mapcmd(un, ab)
1067:     int un; /* true if this is unmap command */
1068:     int ab; /* true if this is abbr command */
1069: {
1070:     char lhs[100], rhs[100];    /* max sizes resp. */
1071:     register char *p;
1072:     register int c;     /* mjm: char --> int */
1073:     char *dname;
1074:     struct maps *mp;    /* the map structure we are working on */
1075: 
1076:     mp = ab ? abbrevs : exclam() ? immacs : arrows;
1077:     if (skipend()) {
1078:         int i;
1079: 
1080:         /* print current mapping values */
1081:         if (peekchar() != EOF)
1082:             ignchar();
1083:         if (un)
1084:             error("Missing lhs");
1085:         if (inopen)
1086:             pofix();
1087:         for (i=0; mp[i].mapto; i++)
1088:             if (mp[i].cap) {
1089:                 lprintf("%s", mp[i].descr);
1090:                 putchar('\t');
1091:                 lprintf("%s", mp[i].cap);
1092:                 putchar('\t');
1093:                 lprintf("%s", mp[i].mapto);
1094:                 putNFL();
1095:             }
1096:         return;
1097:     }
1098: 
1099:     ignore(skipwh());
1100:     for (p=lhs; ; ) {
1101:         c = getchar();
1102:         if (c == CTRL(v)) {
1103:             c = getchar();
1104:         } else if (!un && any(c, " \t")) {
1105:             /* End of lhs */
1106:             break;
1107:         } else if (endcmd(c) && c!='"') {
1108:             ungetchar(c);
1109:             if (un) {
1110:                 newline();
1111:                 *p = 0;
1112:                 addmac(lhs, NOSTR, NOSTR, mp);
1113:                 return;
1114:             } else
1115:                 error("Missing rhs");
1116:         }
1117:         *p++ = c;
1118:     }
1119:     *p = 0;
1120: 
1121:     if (skipend())
1122:         error("Missing rhs");
1123:     for (p=rhs; ; ) {
1124:         c = getchar();
1125:         if (c == CTRL(v)) {
1126:             c = getchar();
1127:         } else if (endcmd(c) && c!='"') {
1128:             ungetchar(c);
1129:             break;
1130:         }
1131:         *p++ = c;
1132:     }
1133:     *p = 0;
1134:     newline();
1135:     /*
1136: 	 * Special hack for function keys: #1 means key f1, etc.
1137: 	 * If the terminal doesn't have function keys, we just use #1.
1138: 	 */
1139:     if (lhs[0] == '#') {
1140:         char *fnkey;
1141:         char *fkey();
1142:         char funkey[3];
1143: 
1144:         fnkey = fkey(lhs[1] - '0');
1145:         funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
1146:         if (fnkey)
1147:             strcpy(lhs, fnkey);
1148:         dname = funkey;
1149:     } else {
1150:         dname = lhs;
1151:     }
1152:     addmac(lhs,rhs,dname,mp);
1153: }
1154: 
1155: /*
1156:  * Add a macro definition to those that already exist. The sequence of
1157:  * chars "src" is mapped into "dest". If src is already mapped into something
1158:  * this overrides the mapping. There is no recursion. Unmap is done by
1159:  * using NOSTR for dest.  Dname is what to show in listings.  mp is
1160:  * the structure to affect (arrows, etc).
1161:  */
1162: addmac(src,dest,dname,mp)
1163:     register char *src, *dest, *dname;
1164:     register struct maps *mp;
1165: {
1166:     register int slot, zer;
1167: 
1168: #ifdef TRACE
1169:     if (trace)
1170:         fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1171: #endif
1172:     if (dest && mp==arrows) {
1173:         /* Make sure user doesn't screw himself */
1174:         /*
1175: 		 * Prevent tail recursion. We really should be
1176: 		 * checking to see if src is a suffix of dest
1177: 		 * but this makes mapping involving escapes that
1178: 		 * is reasonable mess up.
1179: 		 */
1180:         if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1181:             error("No tail recursion");
1182:         /*
1183: 		 * We don't let the user rob himself of ":", and making
1184: 		 * multi char words is a bad idea so we don't allow it.
1185: 		 * Note that if user sets mapinput and maps all of return,
1186: 		 * linefeed, and escape, he can screw himself. This is
1187: 		 * so weird I don't bother to check for it.
1188: 		 */
1189:         if (isalpha(src[0]) && src[1] || any(src[0],":"))
1190:             error("Too dangerous to map that");
1191:     }
1192:     else if (dest) {
1193:         /* check for tail recursion in input mode: fussier */
1194:         if (eq(src, dest+strlen(dest)-strlen(src)))
1195:             error("No tail recursion");
1196:     }
1197:     /*
1198: 	 * If the src were null it would cause the dest to
1199: 	 * be mapped always forever. This is not good.
1200: 	 */
1201:     if (src == NOSTR || src[0] == 0)
1202:         error("Missing lhs");
1203: 
1204:     /* see if we already have a def for src */
1205:     zer = -1;
1206:     for (slot=0; mp[slot].mapto; slot++) {
1207:         if (mp[slot].cap) {
1208:             if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
1209:                 break;  /* if so, reuse slot */
1210:         } else {
1211:             zer = slot; /* remember an empty slot */
1212:         }
1213:     }
1214: 
1215:     if (dest == NOSTR) {
1216:         /* unmap */
1217:         if (mp[slot].cap) {
1218:             mp[slot].cap = NOSTR;
1219:             mp[slot].descr = NOSTR;
1220:         } else {
1221:             error("Not mapped|That macro wasn't mapped");
1222:         }
1223:         return;
1224:     }
1225: 
1226:     /* reuse empty slot, if we found one and src isn't already defined */
1227:     if (zer >= 0 && mp[slot].mapto == 0)
1228:         slot = zer;
1229: 
1230:     /* if not, append to end */
1231:     if (slot >= MAXNOMACS)
1232:         error("Too many macros");
1233:     if (msnext == 0)    /* first time */
1234:         msnext = mapspace;
1235:     /* Check is a bit conservative, we charge for dname even if reusing src */
1236:     if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
1237:         error("Too much macro text");
1238:     CP(msnext, src);
1239:     mp[slot].cap = msnext;
1240:     msnext += strlen(src) + 1;  /* plus 1 for null on the end */
1241:     CP(msnext, dest);
1242:     mp[slot].mapto = msnext;
1243:     msnext += strlen(dest) + 1;
1244:     if (dname) {
1245:         CP(msnext, dname);
1246:         mp[slot].descr = msnext;
1247:         msnext += strlen(dname) + 1;
1248:     } else {
1249:         /* default descr to string user enters */
1250:         mp[slot].descr = src;
1251:     }
1252: }
1253: 
1254: /*
1255:  * Implements macros from command mode. c is the buffer to
1256:  * get the macro from.
1257:  */
1258: cmdmac(c)
1259: char c;
1260: {
1261:     char macbuf[BUFSIZ];
1262:     line *ad, *a1, *a2;
1263:     char *oglobp;
1264:     char pk;
1265:     bool oinglobal;
1266: 
1267:     lastmac = c;
1268:     oglobp = globp;
1269:     oinglobal = inglobal;
1270:     pk = peekc; peekc = 0;
1271:     if (inglobal < 2)
1272:         inglobal = 1;
1273:     regbuf(c, macbuf, sizeof(macbuf));
1274:     a1 = addr1; a2 = addr2;
1275:     for (ad=a1; ad<=a2; ad++) {
1276:         globp = macbuf;
1277:         dot = ad;
1278:         commands(1,1);
1279:     }
1280:     globp = oglobp;
1281:     inglobal = oinglobal;
1282:     peekc = pk;
1283: }

Defined functions

addmac defined in line 1162; used 4 times
cmdmac defined in line 1258; used 1 times
delete defined in line 101; used 5 times
getcopy defined in line 322; used 2 times
getput defined in line 334; used 1 times
jnoop defined in line 234; used 3 times
join defined in line 197; used 2 times
mapcmd defined in line 1066; used 4 times
move defined in line 247; used 3 times
move1 defined in line 268; used 2 times
pargs defined in line 79; used 1 times
plines defined in line 852; used 5 times
pragged defined in line 368; used 2 times
put defined in line 344; used 2 times
somechange defined in line 1029; used 1 times
splitit defined in line 842; used 2 times
squish defined in line 175; used 7 times
tagfind defined in line 461; used 1 times
undo defined in line 896; used 3 times
yank defined in line 677; used 2 times
zop defined in line 701; used 2 times
zop2 defined in line 762; used 2 times

Defined variables

jcount defined in line 195; used 2 times
sccsid defined in line 2; never used
tad1 defined in line 16; used 7 times
zhadpr defined in line 697; used 2 times
znoclear defined in line 698; used 5 times
zweight defined in line 699; used 5 times
Last modified: 1981-07-09
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3407
Valid CSS Valid XHTML 1.0 Strict