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

Defined functions

addmac defined in line 1171; used 4 times
cmdmac defined in line 1267; used 1 times
delete defined in line 109; used 5 times
getcopy defined in line 330; used 2 times
getput defined in line 342; used 1 times
jnoop defined in line 242; used 3 times
join defined in line 205; used 2 times
mapcmd defined in line 1075; used 4 times
move defined in line 255; used 3 times
move1 defined in line 276; used 2 times
pargs defined in line 87; used 1 times
plines defined in line 861; used 5 times
pragged defined in line 376; used 2 times
put defined in line 352; used 2 times
shift defined in line 413; used 3 times
somechange defined in line 1038; used 1 times
splitit defined in line 851; used 2 times
squish defined in line 183; used 7 times
tagfind defined in line 469; used 1 times
undo defined in line 905; used 3 times
yank defined in line 686; used 2 times
zop defined in line 710; used 2 times
zop2 defined in line 771; used 2 times

Defined variables

endline defined in line 23; used 9 times
jcount defined in line 203; used 2 times
sccsid defined in line 8; never used
tad1 defined in line 24; used 7 times
zhadpr defined in line 706; used 2 times
znoclear defined in line 707; used 5 times
zweight defined in line 708; used 5 times
Last modified: 1991-09-08
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 6402
Valid CSS Valid XHTML 1.0 Strict