# 2.11BSD#431 - /usr/src/ucb/ex/ex_cmdsub.c

##### Functions

    addmac            X
    append            X
    appendnone        X
    cmdmac            X
    delete            X
    deletenone        X
    getcopy           X
    getput            X
    join              X
    mapcmd            X
    move              X
    move1             X
    pargs             X
    plines            X
    pofix             X
    pragged           X
    put               X
    shift             X
    somechange        X
    squish            X
    tagfind           X
    undo              X
    yank              X
    zop               X
    zop2              X

##### Variables

    endline           X
    tad1              X
    zhadpr            X
    znoclear          X
    zweight           X

       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

- in line 1121, 1161
- in /usr/src/ucb/ex/ex_tty.c line 117-119(2)

append defined in line 32; used 11 times

- in line 237, 288, 364
- in /usr/src/ucb/ex/ex_cmds.c line 178, 242, 320
- in /usr/src/ucb/ex/ex_io.c line 432
- in /usr/src/ucb/ex/ex_re.c line 204
- in /usr/src/ucb/ex/ex_temp.c line 488
- in /usr/src/ucb/ex/ex_unix.c line 285
- in /usr/src/ucb/ex/ex_vops2.c line 857

appendnone defined in line 75; used 3 times

- in /usr/src/ucb/ex/ex_cmds.c line 259
- in /usr/src/ucb/ex/ex_temp.c line 466
- in /usr/src/ucb/ex/ex_vmain.c line 1152

cmdmac defined in line 1267; used 1 times

- in /usr/src/ucb/ex/ex_cmds.c line 759

delete defined in line 109; used 5 times

- in line 233
- in /usr/src/ucb/ex/ex.h line 360
- in /usr/src/ucb/ex/ex_cmds.c line 240, 258
- in /usr/src/ucb/ex/ex_unix.c line 272

deletenone defined in line 169; used 6 times

- in line 319
- in /usr/src/ucb/ex/ex_cmds.c line 175, 317
- in /usr/src/ucb/ex/ex_io.c line 422
- in /usr/src/ucb/ex/ex_temp.c line 465
- in /usr/src/ucb/ex/ex_vmain.c line 1150

getcopy defined in line 330; used 2 times

- in line 253, 288

getput defined in line 342; used 1 times

- in line 364

jnoop defined in line 242; used 3 times

- in line 25, 203, 237

join defined in line 205; used 2 times

- in /usr/src/ucb/ex/ex.h line 366
- in /usr/src/ucb/ex/ex_cmds.c line 337

mapcmd defined in line 1075; used 4 times

- in /usr/src/ucb/ex/ex_cmds.c line 155, 371, 647, 653

move defined in line 255; used 3 times

- in /usr/src/ucb/ex/ex_cmds.c line 190, 381, 636

move1 defined in line 276; used 2 times

- in line 272, 943

pargs defined in line 87; used 1 times

- in /usr/src/ucb/ex/ex_cmds.c line 163

plines defined in line 861; used 5 times

- in line 842-848(3)
- in /usr/src/ucb/ex/ex_cmds.c line 453, 794

pofix defined in line 879; used 9 times

- in line 868, 1095
- in /usr/src/ucb/ex/ex_cmds.c line 703, 815, 829
- in /usr/src/ucb/ex/ex_re.c line 377
- in /usr/src/ucb/ex/ex_set.c line 45
- in /usr/src/ucb/ex/ex_vmain.c line 927
- in /usr/src/ucb/ex/ex_vput.c line 1065

pragged defined in line 376; used 2 times

- in line 360
- in /usr/src/ucb/ex/ex_temp.c line 493

put defined in line 352; used 2 times

- in /usr/src/ucb/ex/ex.h line 357
- in /usr/src/ucb/ex/ex_cmds.c line 423

shift defined in line 413; used 3 times

- in /usr/src/ucb/ex/ex.h line 377
- in /usr/src/ucb/ex/ex_cmds.c line 846
- in /usr/src/ucb/ex/ex_vops.c line 855

somechange defined in line 1038; used 1 times

- in line 919

splitit defined in line 851; used 2 times

- in line 843-845(2)

squish defined in line 183; used 7 times

- in line 126, 174, 325, 987
- in /usr/src/ucb/ex/ex_temp.c line 485
- in /usr/src/ucb/ex/ex_v.c line 295
- in /usr/src/ucb/ex/ex_vops2.c line 47

tagfind defined in line 469; used 1 times

- in /usr/src/ucb/ex/ex_cmds.c line 627

undo defined in line 905; used 3 times

- in line 406
- in /usr/src/ucb/ex/ex_cmds.c line 665
- in /usr/src/ucb/ex/ex_vops.c line 109

yank defined in line 686; used 2 times

- in /usr/src/ucb/ex/ex.h line 385
- in /usr/src/ucb/ex/ex_cmds.c line 735

zop defined in line 710; used 2 times

- in /usr/src/ucb/ex/ex_cmds.c line 740, 878

zop2 defined in line 771; used 2 times

- in line 768
- in /usr/src/ucb/ex/ex_vmain.c line 1239

#### Defined variables

endline defined in line 23; used 9 times

- in /usr/src/ucb/ex/ex_cmds.c line 59, 79, 548, 764-769(2)
- in /usr/src/ucb/ex/ex_cmds2.c line 47-52(2), 193
- in /usr/src/ucb/ex/ex_re.c line 128

jcount defined in line 203; used 2 times

- in line 234, 246

sccsid defined in line 8; never used
tad1 defined in line 24; used 7 times

- in line 286, 333-335(2), 345-348(3), 363

zhadpr defined in line 706; used 2 times

- in line 716, 831

znoclear defined in line 707; used 5 times

- in line 718, 737, 754, 798, 835

zweight defined in line 708; used 5 times

- in line 719-729(3), 786, 815

Last modified: 1991-09-08 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 3713 |