106fb5685Smark /* Copyright (c) 1979 Regents of the University of California */ 206fb5685Smark #include "ex.h" 306fb5685Smark #include "ex_argv.h" 406fb5685Smark #include "ex_temp.h" 506fb5685Smark #include "ex_tty.h" 6*d4cba322Smark #include "ex_vis.h" 706fb5685Smark 806fb5685Smark /* 906fb5685Smark * Command mode subroutines implementing 1006fb5685Smark * append, args, copy, delete, join, move, put, 1106fb5685Smark * shift, tag, yank, z and undo 1206fb5685Smark */ 1306fb5685Smark 1406fb5685Smark bool endline = 1; 1506fb5685Smark line *tad1; 16*d4cba322Smark static jnoop(); 1706fb5685Smark 1806fb5685Smark /* 1906fb5685Smark * Append after line a lines returned by function f. 2006fb5685Smark * Be careful about intermediate states to avoid scramble 2106fb5685Smark * if an interrupt comes in. 2206fb5685Smark */ 2306fb5685Smark append(f, a) 2406fb5685Smark int (*f)(); 2506fb5685Smark line *a; 2606fb5685Smark { 2706fb5685Smark register line *a1, *a2, *rdot; 2806fb5685Smark int nline; 2906fb5685Smark 3006fb5685Smark nline = 0; 3106fb5685Smark dot = a; 32aecfa433Smark if(FIXUNDO && !inopen && f!=getsub) { 3306fb5685Smark undap1 = undap2 = dot + 1; 3406fb5685Smark undkind = UNDCHANGE; 3506fb5685Smark } 3606fb5685Smark while ((*f)() == 0) { 3706fb5685Smark if (truedol >= endcore) { 3806fb5685Smark if (morelines() < 0) { 39aecfa433Smark if (FIXUNDO && f == getsub) { 4006fb5685Smark undap1 = addr1; 4106fb5685Smark undap2 = addr2 + 1; 4206fb5685Smark } 4306fb5685Smark error("Out of memory@- too many lines in file"); 4406fb5685Smark } 4506fb5685Smark } 4606fb5685Smark nline++; 4706fb5685Smark a1 = truedol + 1; 4806fb5685Smark a2 = a1 + 1; 4906fb5685Smark dot++; 5006fb5685Smark undap2++; 5106fb5685Smark dol++; 5206fb5685Smark unddol++; 5306fb5685Smark truedol++; 5406fb5685Smark for (rdot = dot; a1 > rdot;) 5506fb5685Smark *--a2 = *--a1; 5606fb5685Smark *rdot = 0; 5706fb5685Smark putmark(rdot); 5806fb5685Smark if (f == gettty) { 5906fb5685Smark dirtcnt++; 6006fb5685Smark TSYNC(); 6106fb5685Smark } 6206fb5685Smark } 6306fb5685Smark return (nline); 6406fb5685Smark } 6506fb5685Smark 6606fb5685Smark appendnone() 6706fb5685Smark { 6806fb5685Smark 69aecfa433Smark if(FIXUNDO) { 7006fb5685Smark undkind = UNDCHANGE; 7106fb5685Smark undap1 = undap2 = addr1; 7206fb5685Smark } 7306fb5685Smark } 7406fb5685Smark 7506fb5685Smark /* 7606fb5685Smark * Print out the argument list, with []'s around the current name. 7706fb5685Smark */ 7806fb5685Smark pargs() 7906fb5685Smark { 8006fb5685Smark register char **av = argv0, *as = args0; 8106fb5685Smark register int ac; 8206fb5685Smark 8306fb5685Smark for (ac = 0; ac < argc0; ac++) { 8406fb5685Smark if (ac != 0) 8506fb5685Smark putchar(' '); 8606fb5685Smark if (ac + argc == argc0 - 1) 8706fb5685Smark printf("["); 8806fb5685Smark lprintf("%s", as); 8906fb5685Smark if (ac + argc == argc0 - 1) 9006fb5685Smark printf("]"); 9106fb5685Smark as = av ? *++av : strend(as) + 1; 9206fb5685Smark } 9306fb5685Smark noonl(); 9406fb5685Smark } 9506fb5685Smark 9606fb5685Smark /* 9706fb5685Smark * Delete lines; two cases are if we are really deleting, 9806fb5685Smark * more commonly we are just moving lines to the undo save area. 9906fb5685Smark */ 10006fb5685Smark delete(hush) 10106fb5685Smark bool hush; 10206fb5685Smark { 10306fb5685Smark register line *a1, *a2; 10406fb5685Smark 10506fb5685Smark nonzero(); 106aecfa433Smark if(FIXUNDO) { 10706fb5685Smark register int (*dsavint)(); 10806fb5685Smark 10906fb5685Smark change(); 11006fb5685Smark dsavint = signal(SIGINT, SIG_IGN); 11106fb5685Smark undkind = UNDCHANGE; 11206fb5685Smark a1 = addr1; 11306fb5685Smark squish(); 11406fb5685Smark a2 = addr2; 11506fb5685Smark if (a2++ != dol) { 11606fb5685Smark reverse(a1, a2); 11706fb5685Smark reverse(a2, dol + 1); 11806fb5685Smark reverse(a1, dol + 1); 11906fb5685Smark } 12006fb5685Smark dol -= a2 - a1; 12106fb5685Smark unddel = a1 - 1; 12206fb5685Smark if (a1 > dol) 12306fb5685Smark a1 = dol; 12406fb5685Smark dot = a1; 12506fb5685Smark pkill[0] = pkill[1] = 0; 12606fb5685Smark signal(SIGINT, dsavint); 12706fb5685Smark } else { 12806fb5685Smark register line *a3; 12906fb5685Smark register int i; 13006fb5685Smark 13106fb5685Smark change(); 13206fb5685Smark a1 = addr1; 13306fb5685Smark a2 = addr2 + 1; 13406fb5685Smark a3 = truedol; 13506fb5685Smark i = a2 - a1; 13606fb5685Smark unddol -= i; 13706fb5685Smark undap2 -= i; 13806fb5685Smark dol -= i; 13906fb5685Smark truedol -= i; 14006fb5685Smark do 14106fb5685Smark *a1++ = *a2++; 14206fb5685Smark while (a2 <= a3); 14306fb5685Smark a1 = addr1; 14406fb5685Smark if (a1 > dol) 14506fb5685Smark a1 = dol; 14606fb5685Smark dot = a1; 14706fb5685Smark } 14806fb5685Smark if (!hush) 14906fb5685Smark killed(); 15006fb5685Smark } 15106fb5685Smark 15206fb5685Smark deletenone() 15306fb5685Smark { 15406fb5685Smark 155aecfa433Smark if(FIXUNDO) { 15606fb5685Smark undkind = UNDCHANGE; 15706fb5685Smark squish(); 15806fb5685Smark unddel = addr1; 15906fb5685Smark } 16006fb5685Smark } 16106fb5685Smark 16206fb5685Smark /* 16306fb5685Smark * Crush out the undo save area, moving the open/visual 16406fb5685Smark * save area down in its place. 16506fb5685Smark */ 16606fb5685Smark squish() 16706fb5685Smark { 16806fb5685Smark register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1; 16906fb5685Smark 170aecfa433Smark if(FIXUNDO) { 17106fb5685Smark if (inopen == -1) 17206fb5685Smark return; 17306fb5685Smark if (a1 < a2 && a2 < a3) 17406fb5685Smark do 17506fb5685Smark *a1++ = *a2++; 17606fb5685Smark while (a2 < a3); 17706fb5685Smark truedol -= unddol - dol; 17806fb5685Smark unddol = dol; 17906fb5685Smark } 180aecfa433Smark } 18106fb5685Smark 18206fb5685Smark /* 18306fb5685Smark * Join lines. Special hacks put in spaces, two spaces if 18406fb5685Smark * preceding line ends with '.', or no spaces if next line starts with ). 18506fb5685Smark */ 18606fb5685Smark static int jcount, jnoop(); 18706fb5685Smark 18806fb5685Smark join(c) 18906fb5685Smark int c; 19006fb5685Smark { 19106fb5685Smark register line *a1; 19206fb5685Smark register char *cp, *cp1; 19306fb5685Smark 19406fb5685Smark cp = genbuf; 19506fb5685Smark *cp = 0; 19606fb5685Smark for (a1 = addr1; a1 <= addr2; a1++) { 19706fb5685Smark getline(*a1); 19806fb5685Smark cp1 = linebuf; 19906fb5685Smark if (a1 != addr1 && c == 0) { 20006fb5685Smark while (*cp1 == ' ' || *cp1 == '\t') 20106fb5685Smark cp1++; 20206fb5685Smark if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') { 20306fb5685Smark if (*cp1 != ')') { 20406fb5685Smark *cp++ = ' '; 20506fb5685Smark if (cp[-2] == '.') 20606fb5685Smark *cp++ = ' '; 20706fb5685Smark } 20806fb5685Smark } 20906fb5685Smark } 21006fb5685Smark while (*cp++ = *cp1++) 21106fb5685Smark if (cp > &genbuf[LBSIZE-2]) 21206fb5685Smark error("Line overflow|Result line of join would be too long"); 21306fb5685Smark cp--; 21406fb5685Smark } 21506fb5685Smark strcLIN(genbuf); 21606fb5685Smark delete(0); 21706fb5685Smark jcount = 1; 218*d4cba322Smark if (FIXUNDO) 219*d4cba322Smark undap1 = undap2 = addr1; 22006fb5685Smark ignore(append(jnoop, --addr1)); 221*d4cba322Smark if (FIXUNDO) 222*d4cba322Smark vundkind = VMANY; 22306fb5685Smark } 22406fb5685Smark 22506fb5685Smark static 22606fb5685Smark jnoop() 22706fb5685Smark { 22806fb5685Smark 22906fb5685Smark return(--jcount); 23006fb5685Smark } 23106fb5685Smark 23206fb5685Smark /* 23306fb5685Smark * Move and copy lines. Hard work is done by move1 which 23406fb5685Smark * is also called by undo. 23506fb5685Smark */ 23606fb5685Smark int getcopy(); 23706fb5685Smark 23806fb5685Smark move() 23906fb5685Smark { 24006fb5685Smark register line *adt; 24106fb5685Smark bool iscopy = 0; 24206fb5685Smark 24306fb5685Smark if (Command[0] == 'm') { 24406fb5685Smark setdot1(); 24506fb5685Smark markpr(addr2 == dot ? addr1 - 1 : addr2 + 1); 24606fb5685Smark } else { 24706fb5685Smark iscopy++; 24806fb5685Smark setdot(); 24906fb5685Smark } 25006fb5685Smark nonzero(); 25106fb5685Smark adt = address(0); 25206fb5685Smark if (adt == 0) 25306fb5685Smark serror("%s where?|%s requires a trailing address", Command); 25406fb5685Smark newline(); 25506fb5685Smark move1(iscopy, adt); 25606fb5685Smark killed(); 25706fb5685Smark } 25806fb5685Smark 25906fb5685Smark move1(cflag, addrt) 26006fb5685Smark int cflag; 26106fb5685Smark line *addrt; 26206fb5685Smark { 26306fb5685Smark register line *adt, *ad1, *ad2; 26406fb5685Smark int lines; 26506fb5685Smark 26606fb5685Smark adt = addrt; 26706fb5685Smark lines = (addr2 - addr1) + 1; 26806fb5685Smark if (cflag) { 26906fb5685Smark tad1 = addr1; 27006fb5685Smark ad1 = dol; 27106fb5685Smark ignore(append(getcopy, ad1++)); 27206fb5685Smark ad2 = dol; 27306fb5685Smark } else { 27406fb5685Smark ad2 = addr2; 27506fb5685Smark for (ad1 = addr1; ad1 <= ad2;) 27606fb5685Smark *ad1++ &= ~01; 27706fb5685Smark ad1 = addr1; 27806fb5685Smark } 27906fb5685Smark ad2++; 28006fb5685Smark if (adt < ad1) { 28106fb5685Smark if (adt + 1 == ad1 && !cflag && !inglobal) 28206fb5685Smark error("That move would do nothing!"); 28306fb5685Smark dot = adt + (ad2 - ad1); 28406fb5685Smark if (++adt != ad1) { 28506fb5685Smark reverse(adt, ad1); 28606fb5685Smark reverse(ad1, ad2); 28706fb5685Smark reverse(adt, ad2); 28806fb5685Smark } 28906fb5685Smark } else if (adt >= ad2) { 29006fb5685Smark dot = adt++; 29106fb5685Smark reverse(ad1, ad2); 29206fb5685Smark reverse(ad2, adt); 29306fb5685Smark reverse(ad1, adt); 29406fb5685Smark } else 29506fb5685Smark error("Move to a moved line"); 29606fb5685Smark change(); 29706fb5685Smark if (!inglobal) 298aecfa433Smark if(FIXUNDO) { 29906fb5685Smark if (cflag) { 30006fb5685Smark undap1 = addrt + 1; 30106fb5685Smark undap2 = undap1 + lines; 30206fb5685Smark deletenone(); 30306fb5685Smark } else { 30406fb5685Smark undkind = UNDMOVE; 30506fb5685Smark undap1 = addr1; 30606fb5685Smark undap2 = addr2; 30706fb5685Smark unddel = addrt; 30806fb5685Smark squish(); 30906fb5685Smark } 31006fb5685Smark } 311aecfa433Smark } 31206fb5685Smark 31306fb5685Smark getcopy() 31406fb5685Smark { 31506fb5685Smark 31606fb5685Smark if (tad1 > addr2) 31706fb5685Smark return (EOF); 31806fb5685Smark getline(*tad1++); 31906fb5685Smark return (0); 32006fb5685Smark } 32106fb5685Smark 32206fb5685Smark /* 32306fb5685Smark * Put lines in the buffer from the undo save area. 32406fb5685Smark */ 32506fb5685Smark getput() 32606fb5685Smark { 32706fb5685Smark 32806fb5685Smark if (tad1 > unddol) 32906fb5685Smark return (EOF); 33006fb5685Smark getline(*tad1++); 33106fb5685Smark tad1++; 33206fb5685Smark return (0); 33306fb5685Smark } 33406fb5685Smark 33506fb5685Smark put() 33606fb5685Smark { 33706fb5685Smark register int cnt; 33806fb5685Smark 339aecfa433Smark if (!FIXUNDO) 340aecfa433Smark error("Cannot put inside global/macro"); 34106fb5685Smark cnt = unddol - dol; 34206fb5685Smark if (cnt && inopen && pkill[0] && pkill[1]) { 34306fb5685Smark pragged(1); 34406fb5685Smark return; 34506fb5685Smark } 34606fb5685Smark tad1 = dol + 1; 34706fb5685Smark ignore(append(getput, addr2)); 34806fb5685Smark undkind = UNDPUT; 34906fb5685Smark notecnt = cnt; 35006fb5685Smark netchange(cnt); 35106fb5685Smark } 35206fb5685Smark 35306fb5685Smark /* 35406fb5685Smark * A tricky put, of a group of lines in the middle 35506fb5685Smark * of an existing line. Only from open/visual. 35606fb5685Smark * Argument says pkills have meaning, e.g. called from 35706fb5685Smark * put; it is 0 on calls from putreg. 35806fb5685Smark */ 35906fb5685Smark pragged(kill) 36006fb5685Smark bool kill; 36106fb5685Smark { 36206fb5685Smark extern char *cursor; 36306fb5685Smark register char *gp = &genbuf[cursor - linebuf]; 36406fb5685Smark 36506fb5685Smark /* 36606fb5685Smark * This kind of stuff is TECO's forte. 36706fb5685Smark * We just grunge along, since it cuts 36806fb5685Smark * across our line-oriented model of the world 36906fb5685Smark * almost scrambling our addled brain. 37006fb5685Smark */ 37106fb5685Smark if (!kill) 37206fb5685Smark getDOT(); 37306fb5685Smark strcpy(genbuf, linebuf); 37406fb5685Smark getline(*unddol); 37506fb5685Smark if (kill) 37606fb5685Smark *pkill[1] = 0; 37706fb5685Smark strcat(linebuf, gp); 37806fb5685Smark putmark(unddol); 37906fb5685Smark getline(dol[1]); 38006fb5685Smark if (kill) 38106fb5685Smark strcLIN(pkill[0]); 38206fb5685Smark strcpy(gp, linebuf); 38306fb5685Smark strcLIN(genbuf); 38406fb5685Smark putmark(dol+1); 38506fb5685Smark undkind = UNDCHANGE; 38606fb5685Smark undap1 = dot; 38706fb5685Smark undap2 = dot + 1; 38806fb5685Smark unddel = dot - 1; 38906fb5685Smark undo(1); 39006fb5685Smark } 39106fb5685Smark 39206fb5685Smark /* 39306fb5685Smark * Shift lines, based on c. 39406fb5685Smark * If c is neither < nor >, then this is a lisp aligning =. 39506fb5685Smark */ 39606fb5685Smark shift(c, cnt) 39706fb5685Smark int c; 39806fb5685Smark int cnt; 39906fb5685Smark { 40006fb5685Smark register line *addr; 40106fb5685Smark register char *cp; 40206fb5685Smark char *dp; 40306fb5685Smark register int i; 40406fb5685Smark 405aecfa433Smark if(FIXUNDO) 40606fb5685Smark save12(), undkind = UNDCHANGE; 40706fb5685Smark cnt *= value(SHIFTWIDTH); 40806fb5685Smark for (addr = addr1; addr <= addr2; addr++) { 40906fb5685Smark dot = addr; 41006fb5685Smark #ifdef LISPCODE 41106fb5685Smark if (c == '=' && addr == addr1 && addr != addr2) 41206fb5685Smark continue; 41306fb5685Smark #endif 41406fb5685Smark getDOT(); 41506fb5685Smark i = whitecnt(linebuf); 41606fb5685Smark switch (c) { 41706fb5685Smark 41806fb5685Smark case '>': 41906fb5685Smark if (linebuf[0] == 0) 42006fb5685Smark continue; 42106fb5685Smark cp = genindent(i + cnt); 42206fb5685Smark break; 42306fb5685Smark 42406fb5685Smark case '<': 42506fb5685Smark if (i == 0) 42606fb5685Smark continue; 42706fb5685Smark i -= cnt; 42806fb5685Smark cp = i > 0 ? genindent(i) : genbuf; 42906fb5685Smark break; 43006fb5685Smark 43106fb5685Smark #ifdef LISPCODE 43206fb5685Smark default: 43306fb5685Smark i = lindent(addr); 43406fb5685Smark getDOT(); 43506fb5685Smark cp = genindent(i); 43606fb5685Smark break; 43706fb5685Smark #endif 43806fb5685Smark } 43906fb5685Smark if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) 44006fb5685Smark error("Line too long|Result line after shift would be too long"); 44106fb5685Smark CP(cp, dp); 44206fb5685Smark strcLIN(genbuf); 44306fb5685Smark putmark(addr); 44406fb5685Smark } 44506fb5685Smark killed(); 44606fb5685Smark } 44706fb5685Smark 44806fb5685Smark /* 44906fb5685Smark * Find a tag in the tags file. 45006fb5685Smark * Most work here is in parsing the tags file itself. 45106fb5685Smark */ 45206fb5685Smark tagfind(quick) 45306fb5685Smark bool quick; 45406fb5685Smark { 45506fb5685Smark char cmdbuf[BUFSIZ]; 45606fb5685Smark char filebuf[FNSIZE]; 457aecfa433Smark char tagfbuf[128]; 45806fb5685Smark register int c, d; 45906fb5685Smark bool samef = 1; 460aecfa433Smark int tfcount = 0; 461aecfa433Smark int omagic; 462aecfa433Smark char *fn, *fne; 463aecfa433Smark #ifdef VMUNIX 464aecfa433Smark /* 465aecfa433Smark * We have lots of room so we bring in stdio and do 466aecfa433Smark * a binary search on the tags file. 467aecfa433Smark */ 468aecfa433Smark # undef EOF 469aecfa433Smark # include <stdio.h> 470aecfa433Smark # undef getchar 471aecfa433Smark # undef putchar 472aecfa433Smark FILE *iof; 473aecfa433Smark char iofbuf[BUFSIZ]; 474aecfa433Smark long mid; /* assumed byte offset */ 475aecfa433Smark long top, bot; /* length of tag file */ 476aecfa433Smark struct stat sbuf; 477aecfa433Smark #endif 47806fb5685Smark 47906fb5685Smark omagic = value(MAGIC); 48006fb5685Smark if (!skipend()) { 48106fb5685Smark register char *lp = lasttag; 48206fb5685Smark 48306fb5685Smark while (!iswhite(peekchar()) && !endcmd(peekchar())) 48406fb5685Smark if (lp < &lasttag[sizeof lasttag - 2]) 48506fb5685Smark *lp++ = getchar(); 48606fb5685Smark else 48706fb5685Smark ignchar(); 48806fb5685Smark *lp++ = 0; 48906fb5685Smark if (!endcmd(peekchar())) 49006fb5685Smark badtag: 49106fb5685Smark error("Bad tag|Give one tag per line"); 49206fb5685Smark } else if (lasttag[0] == 0) 49306fb5685Smark error("No previous tag"); 49406fb5685Smark c = getchar(); 49506fb5685Smark if (!endcmd(c)) 49606fb5685Smark goto badtag; 49706fb5685Smark if (c == EOF) 49806fb5685Smark ungetchar(c); 49906fb5685Smark clrstats(); 500aecfa433Smark 501aecfa433Smark /* 502aecfa433Smark * Loop once for each file in tags "path". 503aecfa433Smark */ 504aecfa433Smark CP(tagfbuf, svalue(TAGS)); 505aecfa433Smark fne = tagfbuf - 1; 506aecfa433Smark while (fne) { 507aecfa433Smark fn = ++fne; 508aecfa433Smark while (*fne && *fne != ' ') 509aecfa433Smark fne++; 510aecfa433Smark if (*fne == 0) 511aecfa433Smark fne = 0; /* done, quit after this time */ 512aecfa433Smark else 513aecfa433Smark *fne = 0; /* null terminate filename */ 514aecfa433Smark #ifdef VMUNIX 515aecfa433Smark iof = fopen(fn, "r"); 516aecfa433Smark if (iof == NULL) 517aecfa433Smark continue; 518aecfa433Smark tfcount++; 519aecfa433Smark setbuf(iof, iofbuf); 520aecfa433Smark fstat(fileno(iof), &sbuf); 521aecfa433Smark top = sbuf.st_size; 522aecfa433Smark if (top == 0L || iof == NULL) 523aecfa433Smark top = -1L; 524aecfa433Smark bot = 0L; 525aecfa433Smark while (top >= bot) { 526aecfa433Smark #else 527aecfa433Smark /* 528aecfa433Smark * Avoid stdio and scan tag file linearly. 529aecfa433Smark */ 530aecfa433Smark io = open(fn, 0); 531aecfa433Smark if (io<0) 532aecfa433Smark continue; 533*d4cba322Smark tfcount++; 53406fb5685Smark while (getfile() == 0) { 535aecfa433Smark #endif 536aecfa433Smark /* loop for each tags file entry */ 53706fb5685Smark register char *cp = linebuf; 53806fb5685Smark register char *lp = lasttag; 53906fb5685Smark char *oglobp; 54006fb5685Smark 541aecfa433Smark #ifdef VMUNIX 542aecfa433Smark mid = (top + bot) / 2; 543aecfa433Smark fseek(iof, mid, 0); 544aecfa433Smark if (mid > 0) /* to get first tag in file to work */ 545aecfa433Smark fgets(linebuf, sizeof linebuf, iof); /* scan to next \n */ 546aecfa433Smark fgets(linebuf, sizeof linebuf, iof); /* get a line */ 547aecfa433Smark linebuf[strlen(linebuf)-1] = 0; /* was '\n' */ 548aecfa433Smark #endif 54906fb5685Smark while (*cp && *lp == *cp) 55006fb5685Smark cp++, lp++; 551*d4cba322Smark if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || lp-lasttag < value(TAGLENGTH))) { 552aecfa433Smark #ifdef VMUNIX 553aecfa433Smark if (*lp > *cp) 554aecfa433Smark bot = mid + 1; 555aecfa433Smark else 556aecfa433Smark top = mid - 1; 557aecfa433Smark #endif 558aecfa433Smark /* Not this tag. Try the next */ 55906fb5685Smark continue; 560aecfa433Smark } 561aecfa433Smark 562aecfa433Smark /* 563aecfa433Smark * We found the tag. Decode the line in the file. 564aecfa433Smark */ 565aecfa433Smark #ifdef VMUNIX 566aecfa433Smark fclose(iof); 567aecfa433Smark #else 56806fb5685Smark close(io); 569aecfa433Smark #endif 570*d4cba322Smark /* Rest of tag if abbreviated */ 571*d4cba322Smark while (!iswhite(*cp)) 572*d4cba322Smark cp++; 573*d4cba322Smark 574aecfa433Smark /* name of file */ 57506fb5685Smark while (*cp && iswhite(*cp)) 57606fb5685Smark cp++; 57706fb5685Smark if (!*cp) 57806fb5685Smark badtags: 57906fb5685Smark serror("%s: Bad tags file entry", lasttag); 58006fb5685Smark lp = filebuf; 58106fb5685Smark while (*cp && *cp != ' ' && *cp != '\t') { 58206fb5685Smark if (lp < &filebuf[sizeof filebuf - 2]) 58306fb5685Smark *lp++ = *cp; 58406fb5685Smark cp++; 58506fb5685Smark } 58606fb5685Smark *lp++ = 0; 587aecfa433Smark 58806fb5685Smark if (*cp == 0) 58906fb5685Smark goto badtags; 59006fb5685Smark if (dol != zero) { 59106fb5685Smark /* 59206fb5685Smark * Save current position in 't for ^^ in visual. 59306fb5685Smark */ 59406fb5685Smark names['t'-'a'] = *dot &~ 01; 59506fb5685Smark if (inopen) { 596*d4cba322Smark extern char *ncols['z'-'a'+2]; 59706fb5685Smark extern char *cursor; 59806fb5685Smark 59906fb5685Smark ncols['t'-'a'] = cursor; 60006fb5685Smark } 60106fb5685Smark } 60206fb5685Smark strcpy(cmdbuf, cp); 60306fb5685Smark if (strcmp(filebuf, savedfile) || !edited) { 60406fb5685Smark char cmdbuf2[sizeof filebuf + 10]; 60506fb5685Smark 606aecfa433Smark /* Different file. Do autowrite & get it. */ 60706fb5685Smark if (!quick) { 60806fb5685Smark ckaw(); 60906fb5685Smark if (chng && dol > zero) 61006fb5685Smark error("No write@since last change (:tag! overrides)"); 61106fb5685Smark } 61206fb5685Smark oglobp = globp; 61306fb5685Smark strcpy(cmdbuf2, "e! "); 61406fb5685Smark strcat(cmdbuf2, filebuf); 61506fb5685Smark globp = cmdbuf2; 61606fb5685Smark d = peekc; ungetchar(0); 617aecfa433Smark commands(1, 1); 618aecfa433Smark peekc = d; 619aecfa433Smark globp = oglobp; 620aecfa433Smark value(MAGIC) = omagic; 621aecfa433Smark samef = 0; 622aecfa433Smark } 623aecfa433Smark 624aecfa433Smark /* 625aecfa433Smark * Look for pattern in the current file. 626aecfa433Smark */ 627aecfa433Smark oglobp = globp; 628aecfa433Smark globp = cmdbuf; 629aecfa433Smark d = peekc; ungetchar(0); 630aecfa433Smark if (samef) 631aecfa433Smark markpr(dot); 632f80bacf1Smark /* 633f80bacf1Smark * BUG: if it isn't found (user edited header 634f80bacf1Smark * line) we get left in nomagic mode. 635f80bacf1Smark */ 636f80bacf1Smark value(MAGIC) = 0; 63706fb5685Smark commands(1, 1); 63806fb5685Smark peekc = d; 63906fb5685Smark globp = oglobp; 640f80bacf1Smark value(MAGIC) = omagic; 64106fb5685Smark return; 642aecfa433Smark } /* end of "for each tag in file" */ 643aecfa433Smark 644aecfa433Smark /* 645aecfa433Smark * No such tag in this file. Close it and try the next. 646aecfa433Smark */ 647aecfa433Smark #ifdef VMUNIX 648aecfa433Smark fclose(iof); 649aecfa433Smark #else 650aecfa433Smark close(io); 651aecfa433Smark #endif 652aecfa433Smark } /* end of "for each file in path" */ 653aecfa433Smark if (tfcount <= 0) 65406fb5685Smark error("No tags file"); 655aecfa433Smark else 65606fb5685Smark serror("%s: No such tag@in tags file", lasttag); 65706fb5685Smark } 65806fb5685Smark 65906fb5685Smark /* 66006fb5685Smark * Save lines from addr1 thru addr2 as though 66106fb5685Smark * they had been deleted. 66206fb5685Smark */ 66306fb5685Smark yank() 66406fb5685Smark { 66506fb5685Smark 666aecfa433Smark if (!FIXUNDO) 667aecfa433Smark error("Can't yank inside global/macro"); 66806fb5685Smark save12(); 66906fb5685Smark undkind = UNDNONE; 67006fb5685Smark killcnt(addr2 - addr1 + 1); 67106fb5685Smark } 67206fb5685Smark 67306fb5685Smark /* 67406fb5685Smark * z command; print windows of text in the file. 67506fb5685Smark * 67606fb5685Smark * If this seems unreasonably arcane, the reasons 67706fb5685Smark * are historical. This is one of the first commands 67806fb5685Smark * added to the first ex (then called en) and the 67906fb5685Smark * number of facilities here were the major advantage 68006fb5685Smark * of en over ed since they allowed more use to be 68106fb5685Smark * made of fast terminals w/o typing .,.22p all the time. 68206fb5685Smark */ 68306fb5685Smark bool zhadpr; 68406fb5685Smark bool znoclear; 68506fb5685Smark short zweight; 68606fb5685Smark 68706fb5685Smark zop(hadpr) 68806fb5685Smark int hadpr; 68906fb5685Smark { 69006fb5685Smark register int c, lines, op; 69106fb5685Smark bool excl; 69206fb5685Smark 69306fb5685Smark zhadpr = hadpr; 69406fb5685Smark notempty(); 69506fb5685Smark znoclear = 0; 69606fb5685Smark zweight = 0; 69706fb5685Smark excl = exclam(); 69806fb5685Smark switch (c = op = getchar()) { 69906fb5685Smark 70006fb5685Smark case '^': 70106fb5685Smark zweight = 1; 70206fb5685Smark case '-': 70306fb5685Smark case '+': 70406fb5685Smark while (peekchar() == op) { 70506fb5685Smark ignchar(); 70606fb5685Smark zweight++; 70706fb5685Smark } 70806fb5685Smark case '=': 70906fb5685Smark case '.': 71006fb5685Smark c = getchar(); 71106fb5685Smark break; 71206fb5685Smark 71306fb5685Smark case EOF: 71406fb5685Smark znoclear++; 71506fb5685Smark break; 71606fb5685Smark 71706fb5685Smark default: 71806fb5685Smark op = 0; 71906fb5685Smark break; 72006fb5685Smark } 72106fb5685Smark if (isdigit(c)) { 72206fb5685Smark lines = c - '0'; 72306fb5685Smark for(;;) { 72406fb5685Smark c = getchar(); 72506fb5685Smark if (!isdigit(c)) 72606fb5685Smark break; 72706fb5685Smark lines *= 10; 72806fb5685Smark lines += c - '0'; 72906fb5685Smark } 73006fb5685Smark if (lines < LINES) 73106fb5685Smark znoclear++; 73206fb5685Smark value(WINDOW) = lines; 73306fb5685Smark if (op == '=') 73406fb5685Smark lines += 2; 73506fb5685Smark } else 73606fb5685Smark lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL); 73706fb5685Smark if (inopen || c != EOF) { 73806fb5685Smark ungetchar(c); 73906fb5685Smark newline(); 74006fb5685Smark } 74106fb5685Smark addr1 = addr2; 74206fb5685Smark if (addr2 == 0 && dot < dol && op == 0) 74306fb5685Smark addr1 = addr2 = dot+1; 74406fb5685Smark setdot(); 74506fb5685Smark zop2(lines, op); 74606fb5685Smark } 74706fb5685Smark 74806fb5685Smark zop2(lines, op) 74906fb5685Smark register int lines; 75006fb5685Smark register int op; 75106fb5685Smark { 75206fb5685Smark register line *split; 75306fb5685Smark 75406fb5685Smark split = NULL; 75506fb5685Smark switch (op) { 75606fb5685Smark 75706fb5685Smark case EOF: 75806fb5685Smark if (addr2 == dol) 75906fb5685Smark error("\nAt EOF"); 76006fb5685Smark case '+': 76106fb5685Smark if (addr2 == dol) 76206fb5685Smark error("At EOF"); 76306fb5685Smark addr2 += lines * zweight; 76406fb5685Smark if (addr2 > dol) 76506fb5685Smark error("Hit BOTTOM"); 76606fb5685Smark addr2++; 76706fb5685Smark default: 76806fb5685Smark addr1 = addr2; 76906fb5685Smark addr2 += lines-1; 77006fb5685Smark dot = addr2; 77106fb5685Smark break; 77206fb5685Smark 77306fb5685Smark case '=': 77406fb5685Smark case '.': 77506fb5685Smark znoclear = 0; 77606fb5685Smark lines--; 77706fb5685Smark lines >>= 1; 77806fb5685Smark if (op == '=') 77906fb5685Smark lines--; 78006fb5685Smark addr1 = addr2 - lines; 78106fb5685Smark if (op == '=') 78206fb5685Smark dot = split = addr2; 78306fb5685Smark addr2 += lines; 78406fb5685Smark if (op == '.') { 78506fb5685Smark markDOT(); 78606fb5685Smark dot = addr2; 78706fb5685Smark } 78806fb5685Smark break; 78906fb5685Smark 79006fb5685Smark case '^': 79106fb5685Smark case '-': 79206fb5685Smark addr2 -= lines * zweight; 79306fb5685Smark if (addr2 < one) 79406fb5685Smark error("Hit TOP"); 79506fb5685Smark lines--; 79606fb5685Smark addr1 = addr2 - lines; 79706fb5685Smark dot = addr2; 79806fb5685Smark break; 79906fb5685Smark } 80006fb5685Smark if (addr1 <= zero) 80106fb5685Smark addr1 = one; 80206fb5685Smark if (addr2 > dol) 80306fb5685Smark addr2 = dol; 80406fb5685Smark if (dot > dol) 80506fb5685Smark dot = dol; 80606fb5685Smark if (addr1 > addr2) 80706fb5685Smark return; 80806fb5685Smark if (op == EOF && zhadpr) { 80906fb5685Smark getline(*addr1); 81006fb5685Smark putchar('\r' | QUOTE); 81106fb5685Smark shudclob = 1; 81206fb5685Smark } else if (znoclear == 0 && CL != NOSTR && !inopen) { 81306fb5685Smark flush1(); 81406fb5685Smark vclear(); 81506fb5685Smark } 81606fb5685Smark if (addr2 - addr1 > 1) 81706fb5685Smark pstart(); 81806fb5685Smark if (split) { 81906fb5685Smark plines(addr1, split - 1, 0); 82006fb5685Smark splitit(); 82106fb5685Smark plines(split, split, 0); 82206fb5685Smark splitit(); 82306fb5685Smark addr1 = split + 1; 82406fb5685Smark } 82506fb5685Smark plines(addr1, addr2, 0); 82606fb5685Smark } 82706fb5685Smark 82806fb5685Smark static 82906fb5685Smark splitit() 83006fb5685Smark { 83106fb5685Smark register int l; 83206fb5685Smark 83306fb5685Smark for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--) 83406fb5685Smark putchar('-'); 83506fb5685Smark putnl(); 83606fb5685Smark } 83706fb5685Smark 83806fb5685Smark plines(adr1, adr2, movedot) 83906fb5685Smark line *adr1; 84006fb5685Smark register line *adr2; 84106fb5685Smark bool movedot; 84206fb5685Smark { 84306fb5685Smark register line *addr; 84406fb5685Smark 84506fb5685Smark pofix(); 84606fb5685Smark for (addr = adr1; addr <= adr2; addr++) { 84706fb5685Smark getline(*addr); 84806fb5685Smark pline(lineno(addr)); 84906fb5685Smark if (inopen) 85006fb5685Smark putchar('\n' | QUOTE); 85106fb5685Smark if (movedot) 85206fb5685Smark dot = addr; 85306fb5685Smark } 85406fb5685Smark } 85506fb5685Smark 85606fb5685Smark pofix() 85706fb5685Smark { 85806fb5685Smark 85906fb5685Smark if (inopen && Outchar != termchar) { 86006fb5685Smark vnfl(); 86106fb5685Smark setoutt(); 86206fb5685Smark } 86306fb5685Smark } 86406fb5685Smark 86506fb5685Smark /* 86606fb5685Smark * Dudley doright to the rescue. 86706fb5685Smark * Undo saves the day again. 86806fb5685Smark * A tip of the hatlo hat to Warren Teitleman 86906fb5685Smark * who made undo as useful as do. 87006fb5685Smark * 87106fb5685Smark * Command level undo works easily because 87206fb5685Smark * the editor has a unique temporary file 87306fb5685Smark * index for every line which ever existed. 87406fb5685Smark * We don't have to save large blocks of text, 87506fb5685Smark * only the indices which are small. We do this 87606fb5685Smark * by moving them to after the last line in the 87706fb5685Smark * line buffer array, and marking down info 87806fb5685Smark * about whence they came. 87906fb5685Smark * 88006fb5685Smark * Undo is its own inverse. 88106fb5685Smark */ 88206fb5685Smark undo(c) 88306fb5685Smark bool c; 88406fb5685Smark { 88506fb5685Smark register int i; 88606fb5685Smark register line *jp, *kp; 88706fb5685Smark line *dolp1, *newdol, *newadot; 88806fb5685Smark 889aecfa433Smark #ifdef TRACE 890aecfa433Smark if (trace) 891aecfa433Smark vudump("before undo"); 892aecfa433Smark #endif 89306fb5685Smark if (inglobal && inopen <= 0) 89406fb5685Smark error("Can't undo in global@commands"); 89506fb5685Smark if (!c) 89606fb5685Smark somechange(); 89706fb5685Smark pkill[0] = pkill[1] = 0; 89806fb5685Smark change(); 89906fb5685Smark if (undkind == UNDMOVE) { 90006fb5685Smark /* 90106fb5685Smark * Command to be undone is a move command. 90206fb5685Smark * This is handled as a special case by noting that 90306fb5685Smark * a move "a,b m c" can be inverted by another move. 90406fb5685Smark */ 90506fb5685Smark if ((i = (jp = unddel) - undap2) > 0) { 90606fb5685Smark /* 90706fb5685Smark * when c > b inverse is a+(c-b),c m a-1 90806fb5685Smark */ 90906fb5685Smark addr2 = jp; 91006fb5685Smark addr1 = (jp = undap1) + i; 91106fb5685Smark unddel = jp-1; 91206fb5685Smark } else { 91306fb5685Smark /* 91406fb5685Smark * when b > c inverse is c+1,c+1+(b-a) m b 91506fb5685Smark */ 91606fb5685Smark addr1 = ++jp; 91706fb5685Smark addr2 = jp + ((unddel = undap2) - undap1); 91806fb5685Smark } 91906fb5685Smark kp = undap1; 92006fb5685Smark move1(0, unddel); 92106fb5685Smark dot = kp; 92206fb5685Smark Command = "move"; 92306fb5685Smark killed(); 92406fb5685Smark } else { 92506fb5685Smark int cnt; 92606fb5685Smark 92706fb5685Smark newadot = dot; 92806fb5685Smark cnt = lineDOL(); 92906fb5685Smark newdol = dol; 93006fb5685Smark dolp1 = dol + 1; 93106fb5685Smark /* 93206fb5685Smark * Command to be undone is a non-move. 93306fb5685Smark * All such commands are treated as a combination of 93406fb5685Smark * a delete command and a append command. 93506fb5685Smark * We first move the lines appended by the last command 93606fb5685Smark * from undap1 to undap2-1 so that they are just before the 93706fb5685Smark * saved deleted lines. 93806fb5685Smark */ 93906fb5685Smark if ((i = (kp = undap2) - (jp = undap1)) > 0) { 94006fb5685Smark if (kp != dolp1) { 94106fb5685Smark reverse(jp, kp); 94206fb5685Smark reverse(kp, dolp1); 94306fb5685Smark reverse(jp, dolp1); 94406fb5685Smark } 94506fb5685Smark /* 94606fb5685Smark * Account for possible backward motion of target 94706fb5685Smark * for restoration of saved deleted lines. 94806fb5685Smark */ 94906fb5685Smark if (unddel >= jp) 95006fb5685Smark unddel -= i; 95106fb5685Smark newdol -= i; 95206fb5685Smark /* 95306fb5685Smark * For the case where no lines are restored, dot 95406fb5685Smark * is the line before the first line deleted. 95506fb5685Smark */ 95606fb5685Smark dot = jp-1; 95706fb5685Smark } 95806fb5685Smark /* 95906fb5685Smark * Now put the deleted lines, if any, back where they were. 96006fb5685Smark * Basic operation is: dol+1,unddol m unddel 96106fb5685Smark */ 96206fb5685Smark if (undkind == UNDPUT) { 96306fb5685Smark unddel = undap1 - 1; 96406fb5685Smark squish(); 96506fb5685Smark } 96606fb5685Smark jp = unddel + 1; 96706fb5685Smark if ((i = (kp = unddol) - dol) > 0) { 96806fb5685Smark if (jp != dolp1) { 96906fb5685Smark reverse(jp, dolp1); 97006fb5685Smark reverse(dolp1, ++kp); 97106fb5685Smark reverse(jp, kp); 97206fb5685Smark } 97306fb5685Smark /* 97406fb5685Smark * Account for possible forward motion of the target 97506fb5685Smark * for restoration of the deleted lines. 97606fb5685Smark */ 97706fb5685Smark if (undap1 >= jp) 97806fb5685Smark undap1 += i; 97906fb5685Smark /* 98006fb5685Smark * Dot is the first resurrected line. 98106fb5685Smark */ 98206fb5685Smark dot = jp; 98306fb5685Smark newdol += i; 98406fb5685Smark } 98506fb5685Smark /* 98606fb5685Smark * Clean up so we are invertible 98706fb5685Smark */ 98806fb5685Smark unddel = undap1 - 1; 98906fb5685Smark undap1 = jp; 99006fb5685Smark undap2 = jp + i; 99106fb5685Smark dol = newdol; 99206fb5685Smark netchHAD(cnt); 99306fb5685Smark if (undkind == UNDALL) { 99406fb5685Smark dot = undadot; 99506fb5685Smark undadot = newadot; 996aecfa433Smark } else 99706fb5685Smark undkind = UNDCHANGE; 99806fb5685Smark } 999*d4cba322Smark /* 1000*d4cba322Smark * Defensive programming - after a munged undadot. 1001*d4cba322Smark * Also handle empty buffer case. 1002*d4cba322Smark */ 1003*d4cba322Smark if ((dot <= zero || dot > dol) && dot != dol) 100406fb5685Smark dot = one; 1005aecfa433Smark #ifdef TRACE 1006aecfa433Smark if (trace) 1007aecfa433Smark vudump("after undo"); 1008aecfa433Smark #endif 100906fb5685Smark } 101006fb5685Smark 101106fb5685Smark /* 101206fb5685Smark * Be (almost completely) sure there really 101306fb5685Smark * was a change, before claiming to undo. 101406fb5685Smark */ 101506fb5685Smark somechange() 101606fb5685Smark { 101706fb5685Smark register line *ip, *jp; 101806fb5685Smark 101906fb5685Smark switch (undkind) { 102006fb5685Smark 102106fb5685Smark case UNDMOVE: 102206fb5685Smark return; 102306fb5685Smark 102406fb5685Smark case UNDCHANGE: 102506fb5685Smark if (undap1 == undap2 && dol == unddol) 102606fb5685Smark break; 102706fb5685Smark return; 102806fb5685Smark 102906fb5685Smark case UNDPUT: 103006fb5685Smark if (undap1 != undap2) 103106fb5685Smark return; 103206fb5685Smark break; 103306fb5685Smark 103406fb5685Smark case UNDALL: 103506fb5685Smark if (unddol - dol != lineDOL()) 103606fb5685Smark return; 103706fb5685Smark for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) 103806fb5685Smark if ((*ip &~ 01) != (*jp &~ 01)) 103906fb5685Smark return; 104006fb5685Smark break; 104106fb5685Smark 104206fb5685Smark case UNDNONE: 104306fb5685Smark error("Nothing to undo"); 104406fb5685Smark } 104506fb5685Smark error("Nothing changed|Last undoable command didn't change anything"); 104606fb5685Smark } 104706fb5685Smark 104806fb5685Smark /* 104906fb5685Smark * Map command: 105006fb5685Smark * map src dest 105106fb5685Smark */ 1052*d4cba322Smark mapcmd(un, ab) 105306fb5685Smark int un; /* true if this is unmap command */ 1054*d4cba322Smark int ab; /* true if this is abbr command */ 105506fb5685Smark { 1056*d4cba322Smark char lhs[100], rhs[100]; /* max sizes resp. */ 105706fb5685Smark register char *p; 105806fb5685Smark register char c; 105906fb5685Smark char *dname; 1060aecfa433Smark struct maps *mp; /* the map structure we are working on */ 106106fb5685Smark 1062*d4cba322Smark mp = ab ? abbrevs : exclam() ? immacs : arrows; 106306fb5685Smark if (skipend()) { 106406fb5685Smark int i; 106506fb5685Smark 106606fb5685Smark /* print current mapping values */ 106706fb5685Smark if (peekchar() != EOF) 106806fb5685Smark ignchar(); 1069*d4cba322Smark if (un) 1070*d4cba322Smark error("Missing lhs"); 107106fb5685Smark if (inopen) 107206fb5685Smark pofix(); 1073aecfa433Smark for (i=0; mp[i].mapto; i++) 1074aecfa433Smark if (mp[i].cap) { 1075aecfa433Smark lprintf("%s", mp[i].descr); 107606fb5685Smark putchar('\t'); 1077aecfa433Smark lprintf("%s", mp[i].cap); 107806fb5685Smark putchar('\t'); 1079aecfa433Smark lprintf("%s", mp[i].mapto); 108006fb5685Smark putNFL(); 108106fb5685Smark } 108206fb5685Smark return; 108306fb5685Smark } 108406fb5685Smark 108506fb5685Smark ignore(skipwh()); 108606fb5685Smark for (p=lhs; ; ) { 108706fb5685Smark c = getchar(); 108806fb5685Smark if (c == CTRL(v)) { 108906fb5685Smark c = getchar(); 1090*d4cba322Smark } else if (!un && any(c, " \t")) { 1091*d4cba322Smark /* End of lhs */ 109206fb5685Smark break; 1093*d4cba322Smark } else if (endcmd(c) && c!='"') { 109406fb5685Smark ungetchar(c); 109506fb5685Smark if (un) { 109606fb5685Smark newline(); 1097*d4cba322Smark *p = 0; 1098aecfa433Smark addmac(lhs, NOSTR, NOSTR, mp); 109906fb5685Smark return; 110006fb5685Smark } else 110106fb5685Smark error("Missing rhs"); 110206fb5685Smark } 110306fb5685Smark *p++ = c; 110406fb5685Smark } 110506fb5685Smark *p = 0; 110606fb5685Smark 110706fb5685Smark if (skipend()) 110806fb5685Smark error("Missing rhs"); 110906fb5685Smark for (p=rhs; ; ) { 111006fb5685Smark c = getchar(); 111106fb5685Smark if (c == CTRL(v)) { 111206fb5685Smark c = getchar(); 1113*d4cba322Smark } else if (endcmd(c) && c!='"') { 111406fb5685Smark ungetchar(c); 111506fb5685Smark break; 111606fb5685Smark } 111706fb5685Smark *p++ = c; 111806fb5685Smark } 111906fb5685Smark *p = 0; 112006fb5685Smark newline(); 112106fb5685Smark /* 112206fb5685Smark * Special hack for function keys: #1 means key f1, etc. 112306fb5685Smark * If the terminal doesn't have function keys, we just use #1. 112406fb5685Smark */ 112506fb5685Smark if (lhs[0] == '#') { 112606fb5685Smark char *fnkey; 112706fb5685Smark char *fkey(); 112806fb5685Smark char funkey[3]; 112906fb5685Smark 113006fb5685Smark fnkey = fkey(lhs[1] - '0'); 113106fb5685Smark funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; 113206fb5685Smark if (fnkey) 113306fb5685Smark strcpy(lhs, fnkey); 113406fb5685Smark dname = funkey; 113506fb5685Smark } else { 113606fb5685Smark dname = lhs; 113706fb5685Smark } 1138aecfa433Smark addmac(lhs,rhs,dname,mp); 113906fb5685Smark } 114006fb5685Smark 114106fb5685Smark /* 114206fb5685Smark * Add a macro definition to those that already exist. The sequence of 114306fb5685Smark * chars "src" is mapped into "dest". If src is already mapped into something 114406fb5685Smark * this overrides the mapping. There is no recursion. Unmap is done by 1145aecfa433Smark * using NOSTR for dest. Dname is what to show in listings. mp is 1146aecfa433Smark * the structure to affect (arrows, etc). 114706fb5685Smark */ 1148aecfa433Smark addmac(src,dest,dname,mp) 114906fb5685Smark register char *src, *dest, *dname; 1150aecfa433Smark register struct maps *mp; 115106fb5685Smark { 115206fb5685Smark register int slot, zer; 115306fb5685Smark 1154*d4cba322Smark #ifdef TRACE 1155*d4cba322Smark if (trace) 1156*d4cba322Smark fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp); 1157*d4cba322Smark #endif 1158aecfa433Smark if (dest && mp==arrows) { 115906fb5685Smark /* Make sure user doesn't screw himself */ 116006fb5685Smark /* 1161f80bacf1Smark * Prevent tail recursion. We really should be 1162f80bacf1Smark * checking to see if src is a suffix of dest 1163aecfa433Smark * but this makes mapping involving escapes that 1164aecfa433Smark * is reasonable mess up. 116506fb5685Smark */ 1166f80bacf1Smark if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) 1167f80bacf1Smark error("No tail recursion"); 116806fb5685Smark /* 116906fb5685Smark * We don't let the user rob himself of ":", and making 117006fb5685Smark * multi char words is a bad idea so we don't allow it. 117106fb5685Smark * Note that if user sets mapinput and maps all of return, 117206fb5685Smark * linefeed, and escape, he can screw himself. This is 117306fb5685Smark * so weird I don't bother to check for it. 117406fb5685Smark */ 117506fb5685Smark if (isalpha(src[0]) && src[1] || any(src[0],":")) 117606fb5685Smark error("Too dangerous to map that"); 1177aecfa433Smark } 1178aecfa433Smark else if (dest) { 1179aecfa433Smark /* check for tail recursion in input mode: fussier */ 1180aecfa433Smark if (eq(src, dest+strlen(dest)-strlen(src))) 1181aecfa433Smark error("No tail recursion"); 1182aecfa433Smark } 118306fb5685Smark /* 118406fb5685Smark * If the src were null it would cause the dest to 118506fb5685Smark * be mapped always forever. This is not good. 118606fb5685Smark */ 1187aecfa433Smark if (src == NOSTR || src[0] == 0) 1188aecfa433Smark error("Missing lhs"); 118906fb5685Smark 119006fb5685Smark /* see if we already have a def for src */ 119106fb5685Smark zer = -1; 1192aecfa433Smark for (slot=0; mp[slot].mapto; slot++) { 1193aecfa433Smark if (mp[slot].cap) { 1194*d4cba322Smark if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto)) 119506fb5685Smark break; /* if so, reuse slot */ 119606fb5685Smark } else { 119706fb5685Smark zer = slot; /* remember an empty slot */ 119806fb5685Smark } 119906fb5685Smark } 120006fb5685Smark 120106fb5685Smark if (dest == NOSTR) { 120206fb5685Smark /* unmap */ 1203aecfa433Smark if (mp[slot].cap) { 1204aecfa433Smark mp[slot].cap = NOSTR; 1205aecfa433Smark mp[slot].descr = NOSTR; 120606fb5685Smark } else { 120706fb5685Smark error("Not mapped|That macro wasn't mapped"); 120806fb5685Smark } 120906fb5685Smark return; 121006fb5685Smark } 121106fb5685Smark 121206fb5685Smark /* reuse empty slot, if we found one and src isn't already defined */ 1213aecfa433Smark if (zer >= 0 && mp[slot].mapto == 0) 121406fb5685Smark slot = zer; 121506fb5685Smark 121606fb5685Smark /* if not, append to end */ 121706fb5685Smark if (slot >= MAXNOMACS) 121806fb5685Smark error("Too many macros"); 121906fb5685Smark if (msnext == 0) /* first time */ 122006fb5685Smark msnext = mapspace; 122106fb5685Smark /* Check is a bit conservative, we charge for dname even if reusing src */ 122206fb5685Smark if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) 122306fb5685Smark error("Too much macro text"); 122406fb5685Smark CP(msnext, src); 1225aecfa433Smark mp[slot].cap = msnext; 122606fb5685Smark msnext += strlen(src) + 1; /* plus 1 for null on the end */ 122706fb5685Smark CP(msnext, dest); 1228aecfa433Smark mp[slot].mapto = msnext; 122906fb5685Smark msnext += strlen(dest) + 1; 123006fb5685Smark if (dname) { 123106fb5685Smark CP(msnext, dname); 1232aecfa433Smark mp[slot].descr = msnext; 123306fb5685Smark msnext += strlen(dname) + 1; 123406fb5685Smark } else { 123506fb5685Smark /* default descr to string user enters */ 1236aecfa433Smark mp[slot].descr = src; 123706fb5685Smark } 123806fb5685Smark } 123906fb5685Smark 124006fb5685Smark /* 124106fb5685Smark * Implements macros from command mode. c is the buffer to 124206fb5685Smark * get the macro from. 124306fb5685Smark */ 124406fb5685Smark cmdmac(c) 124506fb5685Smark char c; 124606fb5685Smark { 124706fb5685Smark char macbuf[BUFSIZ]; 124806fb5685Smark line *ad, *a1, *a2; 124906fb5685Smark char *oglobp; 125006fb5685Smark char pk; 125106fb5685Smark bool oinglobal; 125206fb5685Smark 125306fb5685Smark lastmac = c; 125406fb5685Smark oglobp = globp; 125506fb5685Smark oinglobal = inglobal; 125606fb5685Smark pk = peekc; peekc = 0; 125706fb5685Smark if (inglobal < 2) 125806fb5685Smark inglobal = 1; 125906fb5685Smark regbuf(c, macbuf, sizeof(macbuf)); 126006fb5685Smark a1 = addr1; a2 = addr2; 126106fb5685Smark for (ad=a1; ad<=a2; ad++) { 126206fb5685Smark globp = macbuf; 126306fb5685Smark dot = ad; 126406fb5685Smark commands(1,1); 126506fb5685Smark } 126606fb5685Smark globp = oglobp; 126706fb5685Smark inglobal = oinglobal; 126806fb5685Smark peekc = pk; 126906fb5685Smark } 1270