1e4e787efSdist /* 2e4e787efSdist * Copyright (c) 1980 Regents of the University of California. 3e4e787efSdist * All rights reserved. The Berkeley software License Agreement 4e4e787efSdist * specifies the terms and conditions for redistribution. 5e4e787efSdist */ 6e4e787efSdist 7e4e787efSdist #ifndef lint 8*2949201cSbostic static char *sccsid = "@(#)ex_cmdsub.c 7.9 (Berkeley) 01/02/88"; 9e4e787efSdist #endif not lint 10e4e787efSdist 1106fb5685Smark #include "ex.h" 1206fb5685Smark #include "ex_argv.h" 1306fb5685Smark #include "ex_temp.h" 1406fb5685Smark #include "ex_tty.h" 15d4cba322Smark #include "ex_vis.h" 1606fb5685Smark 1706fb5685Smark /* 1806fb5685Smark * Command mode subroutines implementing 1906fb5685Smark * append, args, copy, delete, join, move, put, 2006fb5685Smark * shift, tag, yank, z and undo 2106fb5685Smark */ 2206fb5685Smark 2306fb5685Smark bool endline = 1; 2406fb5685Smark line *tad1; 25d4cba322Smark static jnoop(); 2606fb5685Smark 2706fb5685Smark /* 2806fb5685Smark * Append after line a lines returned by function f. 2906fb5685Smark * Be careful about intermediate states to avoid scramble 3006fb5685Smark * if an interrupt comes in. 3106fb5685Smark */ 3206fb5685Smark append(f, a) 3306fb5685Smark int (*f)(); 3406fb5685Smark line *a; 3506fb5685Smark { 3606fb5685Smark register line *a1, *a2, *rdot; 3706fb5685Smark int nline; 3806fb5685Smark 3906fb5685Smark nline = 0; 4006fb5685Smark dot = a; 41aecfa433Smark if(FIXUNDO && !inopen && f!=getsub) { 4206fb5685Smark undap1 = undap2 = dot + 1; 4306fb5685Smark undkind = UNDCHANGE; 4406fb5685Smark } 4506fb5685Smark while ((*f)() == 0) { 4606fb5685Smark if (truedol >= endcore) { 4706fb5685Smark if (morelines() < 0) { 48aecfa433Smark if (FIXUNDO && f == getsub) { 4906fb5685Smark undap1 = addr1; 5006fb5685Smark undap2 = addr2 + 1; 5106fb5685Smark } 5206fb5685Smark error("Out of memory@- too many lines in file"); 5306fb5685Smark } 5406fb5685Smark } 5506fb5685Smark nline++; 5606fb5685Smark a1 = truedol + 1; 5706fb5685Smark a2 = a1 + 1; 5806fb5685Smark dot++; 5906fb5685Smark undap2++; 6006fb5685Smark dol++; 6106fb5685Smark unddol++; 6206fb5685Smark truedol++; 6306fb5685Smark for (rdot = dot; a1 > rdot;) 6406fb5685Smark *--a2 = *--a1; 6506fb5685Smark *rdot = 0; 6606fb5685Smark putmark(rdot); 6706fb5685Smark if (f == gettty) { 6806fb5685Smark dirtcnt++; 6906fb5685Smark TSYNC(); 7006fb5685Smark } 7106fb5685Smark } 7206fb5685Smark return (nline); 7306fb5685Smark } 7406fb5685Smark 7506fb5685Smark appendnone() 7606fb5685Smark { 7706fb5685Smark 78aecfa433Smark if(FIXUNDO) { 7906fb5685Smark undkind = UNDCHANGE; 8006fb5685Smark undap1 = undap2 = addr1; 8106fb5685Smark } 8206fb5685Smark } 8306fb5685Smark 8406fb5685Smark /* 8506fb5685Smark * Print out the argument list, with []'s around the current name. 8606fb5685Smark */ 8706fb5685Smark pargs() 8806fb5685Smark { 8906fb5685Smark register char **av = argv0, *as = args0; 9006fb5685Smark register int ac; 9106fb5685Smark 9206fb5685Smark for (ac = 0; ac < argc0; ac++) { 9306fb5685Smark if (ac != 0) 94c0a5300eSconrad ex_putchar(' ' | QUOTE); 9506fb5685Smark if (ac + argc == argc0 - 1) 96c0a5300eSconrad ex_printf("["); 9706fb5685Smark lprintf("%s", as); 9806fb5685Smark if (ac + argc == argc0 - 1) 99c0a5300eSconrad ex_printf("]"); 10006fb5685Smark as = av ? *++av : strend(as) + 1; 10106fb5685Smark } 10206fb5685Smark noonl(); 10306fb5685Smark } 10406fb5685Smark 10506fb5685Smark /* 10606fb5685Smark * Delete lines; two cases are if we are really deleting, 10706fb5685Smark * more commonly we are just moving lines to the undo save area. 10806fb5685Smark */ 109c0a5300eSconrad ex_delete(hush) 11006fb5685Smark bool hush; 11106fb5685Smark { 11206fb5685Smark register line *a1, *a2; 11306fb5685Smark 11406fb5685Smark nonzero(); 115aecfa433Smark if(FIXUNDO) { 11606fb5685Smark register int (*dsavint)(); 11706fb5685Smark 118269e4ba1Sdist #ifdef TRACE 119269e4ba1Sdist if (trace) 120269e4ba1Sdist vudump("before delete"); 121269e4ba1Sdist #endif 12206fb5685Smark change(); 12306fb5685Smark dsavint = signal(SIGINT, SIG_IGN); 12406fb5685Smark undkind = UNDCHANGE; 12506fb5685Smark a1 = addr1; 12606fb5685Smark squish(); 12706fb5685Smark a2 = addr2; 12806fb5685Smark if (a2++ != dol) { 12906fb5685Smark reverse(a1, a2); 13006fb5685Smark reverse(a2, dol + 1); 13106fb5685Smark reverse(a1, dol + 1); 13206fb5685Smark } 13306fb5685Smark dol -= a2 - a1; 13406fb5685Smark unddel = a1 - 1; 13506fb5685Smark if (a1 > dol) 13606fb5685Smark a1 = dol; 13706fb5685Smark dot = a1; 13806fb5685Smark pkill[0] = pkill[1] = 0; 13906fb5685Smark signal(SIGINT, dsavint); 140269e4ba1Sdist #ifdef TRACE 141269e4ba1Sdist if (trace) 142269e4ba1Sdist vudump("after delete"); 143269e4ba1Sdist #endif 14406fb5685Smark } else { 14506fb5685Smark register line *a3; 14606fb5685Smark register int i; 14706fb5685Smark 14806fb5685Smark change(); 14906fb5685Smark a1 = addr1; 15006fb5685Smark a2 = addr2 + 1; 15106fb5685Smark a3 = truedol; 15206fb5685Smark i = a2 - a1; 15306fb5685Smark unddol -= i; 15406fb5685Smark undap2 -= i; 15506fb5685Smark dol -= i; 15606fb5685Smark truedol -= i; 15706fb5685Smark do 15806fb5685Smark *a1++ = *a2++; 15906fb5685Smark while (a2 <= a3); 16006fb5685Smark a1 = addr1; 16106fb5685Smark if (a1 > dol) 16206fb5685Smark a1 = dol; 16306fb5685Smark dot = a1; 16406fb5685Smark } 16506fb5685Smark if (!hush) 16606fb5685Smark killed(); 16706fb5685Smark } 16806fb5685Smark 16906fb5685Smark deletenone() 17006fb5685Smark { 17106fb5685Smark 172aecfa433Smark if(FIXUNDO) { 17306fb5685Smark undkind = UNDCHANGE; 17406fb5685Smark squish(); 17506fb5685Smark unddel = addr1; 17606fb5685Smark } 17706fb5685Smark } 17806fb5685Smark 17906fb5685Smark /* 18006fb5685Smark * Crush out the undo save area, moving the open/visual 18106fb5685Smark * save area down in its place. 18206fb5685Smark */ 18306fb5685Smark squish() 18406fb5685Smark { 18506fb5685Smark register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1; 18606fb5685Smark 187aecfa433Smark if(FIXUNDO) { 18806fb5685Smark if (inopen == -1) 18906fb5685Smark return; 19006fb5685Smark if (a1 < a2 && a2 < a3) 19106fb5685Smark do 19206fb5685Smark *a1++ = *a2++; 19306fb5685Smark while (a2 < a3); 19406fb5685Smark truedol -= unddol - dol; 19506fb5685Smark unddol = dol; 19606fb5685Smark } 197aecfa433Smark } 19806fb5685Smark 19906fb5685Smark /* 20006fb5685Smark * Join lines. Special hacks put in spaces, two spaces if 20106fb5685Smark * preceding line ends with '.', or no spaces if next line starts with ). 20206fb5685Smark */ 20306fb5685Smark static int jcount, jnoop(); 20406fb5685Smark 20506fb5685Smark join(c) 20606fb5685Smark int c; 20706fb5685Smark { 20806fb5685Smark register line *a1; 20906fb5685Smark register char *cp, *cp1; 21006fb5685Smark 21106fb5685Smark cp = genbuf; 21206fb5685Smark *cp = 0; 21306fb5685Smark for (a1 = addr1; a1 <= addr2; a1++) { 21406fb5685Smark getline(*a1); 21506fb5685Smark cp1 = linebuf; 21606fb5685Smark if (a1 != addr1 && c == 0) { 21706fb5685Smark while (*cp1 == ' ' || *cp1 == '\t') 21806fb5685Smark cp1++; 21906fb5685Smark if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') { 22006fb5685Smark if (*cp1 != ')') { 22106fb5685Smark *cp++ = ' '; 22206fb5685Smark if (cp[-2] == '.') 22306fb5685Smark *cp++ = ' '; 22406fb5685Smark } 22506fb5685Smark } 22606fb5685Smark } 22706fb5685Smark while (*cp++ = *cp1++) 22806fb5685Smark if (cp > &genbuf[LBSIZE-2]) 22906fb5685Smark error("Line overflow|Result line of join would be too long"); 23006fb5685Smark cp--; 23106fb5685Smark } 23206fb5685Smark strcLIN(genbuf); 233c0a5300eSconrad ex_delete(0); 23406fb5685Smark jcount = 1; 235d4cba322Smark if (FIXUNDO) 236d4cba322Smark undap1 = undap2 = addr1; 23706fb5685Smark ignore(append(jnoop, --addr1)); 238d4cba322Smark if (FIXUNDO) 239d4cba322Smark vundkind = VMANY; 24006fb5685Smark } 24106fb5685Smark 24206fb5685Smark static 24306fb5685Smark jnoop() 24406fb5685Smark { 24506fb5685Smark 24606fb5685Smark return(--jcount); 24706fb5685Smark } 24806fb5685Smark 24906fb5685Smark /* 25006fb5685Smark * Move and copy lines. Hard work is done by move1 which 25106fb5685Smark * is also called by undo. 25206fb5685Smark */ 25306fb5685Smark int getcopy(); 25406fb5685Smark 25506fb5685Smark move() 25606fb5685Smark { 25706fb5685Smark register line *adt; 25806fb5685Smark bool iscopy = 0; 25906fb5685Smark 26006fb5685Smark if (Command[0] == 'm') { 26106fb5685Smark setdot1(); 26206fb5685Smark markpr(addr2 == dot ? addr1 - 1 : addr2 + 1); 26306fb5685Smark } else { 26406fb5685Smark iscopy++; 26506fb5685Smark setdot(); 26606fb5685Smark } 26706fb5685Smark nonzero(); 268269e4ba1Sdist adt = address((char*)0); 26906fb5685Smark if (adt == 0) 27006fb5685Smark serror("%s where?|%s requires a trailing address", Command); 27106fb5685Smark newline(); 27206fb5685Smark move1(iscopy, adt); 27306fb5685Smark killed(); 27406fb5685Smark } 27506fb5685Smark 27606fb5685Smark move1(cflag, addrt) 27706fb5685Smark int cflag; 27806fb5685Smark line *addrt; 27906fb5685Smark { 28006fb5685Smark register line *adt, *ad1, *ad2; 28106fb5685Smark int lines; 28206fb5685Smark 28306fb5685Smark adt = addrt; 28406fb5685Smark lines = (addr2 - addr1) + 1; 28506fb5685Smark if (cflag) { 28606fb5685Smark tad1 = addr1; 28706fb5685Smark ad1 = dol; 28806fb5685Smark ignore(append(getcopy, ad1++)); 28906fb5685Smark ad2 = dol; 29006fb5685Smark } else { 29106fb5685Smark ad2 = addr2; 29206fb5685Smark for (ad1 = addr1; ad1 <= ad2;) 29306fb5685Smark *ad1++ &= ~01; 29406fb5685Smark ad1 = addr1; 29506fb5685Smark } 29606fb5685Smark ad2++; 29706fb5685Smark if (adt < ad1) { 29806fb5685Smark if (adt + 1 == ad1 && !cflag && !inglobal) 29906fb5685Smark error("That move would do nothing!"); 30006fb5685Smark dot = adt + (ad2 - ad1); 30106fb5685Smark if (++adt != ad1) { 30206fb5685Smark reverse(adt, ad1); 30306fb5685Smark reverse(ad1, ad2); 30406fb5685Smark reverse(adt, ad2); 30506fb5685Smark } 30606fb5685Smark } else if (adt >= ad2) { 30706fb5685Smark dot = adt++; 30806fb5685Smark reverse(ad1, ad2); 30906fb5685Smark reverse(ad2, adt); 31006fb5685Smark reverse(ad1, adt); 31106fb5685Smark } else 31206fb5685Smark error("Move to a moved line"); 31306fb5685Smark change(); 31406fb5685Smark if (!inglobal) 315aecfa433Smark if(FIXUNDO) { 31606fb5685Smark if (cflag) { 31706fb5685Smark undap1 = addrt + 1; 31806fb5685Smark undap2 = undap1 + lines; 31906fb5685Smark deletenone(); 32006fb5685Smark } else { 32106fb5685Smark undkind = UNDMOVE; 32206fb5685Smark undap1 = addr1; 32306fb5685Smark undap2 = addr2; 32406fb5685Smark unddel = addrt; 32506fb5685Smark squish(); 32606fb5685Smark } 32706fb5685Smark } 328aecfa433Smark } 32906fb5685Smark 33006fb5685Smark getcopy() 33106fb5685Smark { 33206fb5685Smark 33306fb5685Smark if (tad1 > addr2) 33406fb5685Smark return (EOF); 33506fb5685Smark getline(*tad1++); 33606fb5685Smark return (0); 33706fb5685Smark } 33806fb5685Smark 33906fb5685Smark /* 34006fb5685Smark * Put lines in the buffer from the undo save area. 34106fb5685Smark */ 34206fb5685Smark getput() 34306fb5685Smark { 34406fb5685Smark 34506fb5685Smark if (tad1 > unddol) 34606fb5685Smark return (EOF); 34706fb5685Smark getline(*tad1++); 34806fb5685Smark tad1++; 34906fb5685Smark return (0); 35006fb5685Smark } 35106fb5685Smark 35206fb5685Smark put() 35306fb5685Smark { 35406fb5685Smark register int cnt; 35506fb5685Smark 356aecfa433Smark if (!FIXUNDO) 357aecfa433Smark error("Cannot put inside global/macro"); 35806fb5685Smark cnt = unddol - dol; 35906fb5685Smark if (cnt && inopen && pkill[0] && pkill[1]) { 36006fb5685Smark pragged(1); 36106fb5685Smark return; 36206fb5685Smark } 36306fb5685Smark tad1 = dol + 1; 36406fb5685Smark ignore(append(getput, addr2)); 36506fb5685Smark undkind = UNDPUT; 36606fb5685Smark notecnt = cnt; 36706fb5685Smark netchange(cnt); 36806fb5685Smark } 36906fb5685Smark 37006fb5685Smark /* 37106fb5685Smark * A tricky put, of a group of lines in the middle 37206fb5685Smark * of an existing line. Only from open/visual. 37306fb5685Smark * Argument says pkills have meaning, e.g. called from 37406fb5685Smark * put; it is 0 on calls from putreg. 37506fb5685Smark */ 37606fb5685Smark pragged(kill) 37706fb5685Smark bool kill; 37806fb5685Smark { 37906fb5685Smark extern char *cursor; 38006fb5685Smark register char *gp = &genbuf[cursor - linebuf]; 38106fb5685Smark 38206fb5685Smark /* 38306fb5685Smark * This kind of stuff is TECO's forte. 38406fb5685Smark * We just grunge along, since it cuts 38506fb5685Smark * across our line-oriented model of the world 38606fb5685Smark * almost scrambling our addled brain. 38706fb5685Smark */ 38806fb5685Smark if (!kill) 38906fb5685Smark getDOT(); 39006fb5685Smark strcpy(genbuf, linebuf); 39106fb5685Smark getline(*unddol); 39206fb5685Smark if (kill) 39306fb5685Smark *pkill[1] = 0; 39406fb5685Smark strcat(linebuf, gp); 39506fb5685Smark putmark(unddol); 39606fb5685Smark getline(dol[1]); 39706fb5685Smark if (kill) 39806fb5685Smark strcLIN(pkill[0]); 39906fb5685Smark strcpy(gp, linebuf); 40006fb5685Smark strcLIN(genbuf); 40106fb5685Smark putmark(dol+1); 40206fb5685Smark undkind = UNDCHANGE; 40306fb5685Smark undap1 = dot; 40406fb5685Smark undap2 = dot + 1; 40506fb5685Smark unddel = dot - 1; 40606fb5685Smark undo(1); 40706fb5685Smark } 40806fb5685Smark 40906fb5685Smark /* 41006fb5685Smark * Shift lines, based on c. 41106fb5685Smark * If c is neither < nor >, then this is a lisp aligning =. 41206fb5685Smark */ 41306fb5685Smark shift(c, cnt) 41406fb5685Smark int c; 41506fb5685Smark int cnt; 41606fb5685Smark { 41706fb5685Smark register line *addr; 41806fb5685Smark register char *cp; 41906fb5685Smark char *dp; 42006fb5685Smark register int i; 42106fb5685Smark 422aecfa433Smark if(FIXUNDO) 42306fb5685Smark save12(), undkind = UNDCHANGE; 42406fb5685Smark cnt *= value(SHIFTWIDTH); 42506fb5685Smark for (addr = addr1; addr <= addr2; addr++) { 42606fb5685Smark dot = addr; 42706fb5685Smark #ifdef LISPCODE 42806fb5685Smark if (c == '=' && addr == addr1 && addr != addr2) 42906fb5685Smark continue; 43006fb5685Smark #endif 43106fb5685Smark getDOT(); 43206fb5685Smark i = whitecnt(linebuf); 43306fb5685Smark switch (c) { 43406fb5685Smark 43506fb5685Smark case '>': 43606fb5685Smark if (linebuf[0] == 0) 43706fb5685Smark continue; 43806fb5685Smark cp = genindent(i + cnt); 43906fb5685Smark break; 44006fb5685Smark 44106fb5685Smark case '<': 44206fb5685Smark if (i == 0) 44306fb5685Smark continue; 44406fb5685Smark i -= cnt; 44506fb5685Smark cp = i > 0 ? genindent(i) : genbuf; 44606fb5685Smark break; 44706fb5685Smark 44806fb5685Smark #ifdef LISPCODE 44906fb5685Smark default: 45006fb5685Smark i = lindent(addr); 45106fb5685Smark getDOT(); 45206fb5685Smark cp = genindent(i); 45306fb5685Smark break; 45406fb5685Smark #endif 45506fb5685Smark } 45606fb5685Smark if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2]) 45706fb5685Smark error("Line too long|Result line after shift would be too long"); 45806fb5685Smark CP(cp, dp); 45906fb5685Smark strcLIN(genbuf); 46006fb5685Smark putmark(addr); 46106fb5685Smark } 46206fb5685Smark killed(); 46306fb5685Smark } 46406fb5685Smark 46506fb5685Smark /* 46606fb5685Smark * Find a tag in the tags file. 46706fb5685Smark * Most work here is in parsing the tags file itself. 46806fb5685Smark */ 46906fb5685Smark tagfind(quick) 47006fb5685Smark bool quick; 47106fb5685Smark { 47206fb5685Smark char cmdbuf[BUFSIZ]; 47306fb5685Smark char filebuf[FNSIZE]; 474aecfa433Smark char tagfbuf[128]; 47506fb5685Smark register int c, d; 47606fb5685Smark bool samef = 1; 477aecfa433Smark int tfcount = 0; 478aecfa433Smark int omagic; 479aecfa433Smark char *fn, *fne; 480269e4ba1Sdist struct stat sbuf; 481269e4ba1Sdist #ifdef FASTTAG 482269e4ba1Sdist int iof; 483269e4ba1Sdist char iofbuf[MAXBSIZE]; 484aecfa433Smark long mid; /* assumed byte offset */ 485aecfa433Smark long top, bot; /* length of tag file */ 486aecfa433Smark #endif 48706fb5685Smark 48806fb5685Smark omagic = value(MAGIC); 48906fb5685Smark if (!skipend()) { 49006fb5685Smark register char *lp = lasttag; 49106fb5685Smark 49206fb5685Smark while (!iswhite(peekchar()) && !endcmd(peekchar())) 49306fb5685Smark if (lp < &lasttag[sizeof lasttag - 2]) 494c0a5300eSconrad *lp++ = ex_getchar(); 49506fb5685Smark else 49606fb5685Smark ignchar(); 49706fb5685Smark *lp++ = 0; 49806fb5685Smark if (!endcmd(peekchar())) 49906fb5685Smark badtag: 50006fb5685Smark error("Bad tag|Give one tag per line"); 50106fb5685Smark } else if (lasttag[0] == 0) 50206fb5685Smark error("No previous tag"); 503c0a5300eSconrad c = ex_getchar(); 50406fb5685Smark if (!endcmd(c)) 50506fb5685Smark goto badtag; 50606fb5685Smark if (c == EOF) 50706fb5685Smark ungetchar(c); 50806fb5685Smark clrstats(); 509aecfa433Smark 510aecfa433Smark /* 511aecfa433Smark * Loop once for each file in tags "path". 512aecfa433Smark */ 513aecfa433Smark CP(tagfbuf, svalue(TAGS)); 514aecfa433Smark fne = tagfbuf - 1; 515aecfa433Smark while (fne) { 516aecfa433Smark fn = ++fne; 517aecfa433Smark while (*fne && *fne != ' ') 518aecfa433Smark fne++; 519aecfa433Smark if (*fne == 0) 520aecfa433Smark fne = 0; /* done, quit after this time */ 521aecfa433Smark else 522aecfa433Smark *fne = 0; /* null terminate filename */ 523269e4ba1Sdist #ifdef FASTTAG 524269e4ba1Sdist iof = topen(fn, iofbuf); 525269e4ba1Sdist if (iof == -1) 526aecfa433Smark continue; 527aecfa433Smark tfcount++; 528269e4ba1Sdist fstat(iof, &sbuf); 529aecfa433Smark top = sbuf.st_size; 530269e4ba1Sdist if (top == 0L ) 531aecfa433Smark top = -1L; 532aecfa433Smark bot = 0L; 533aecfa433Smark while (top >= bot) { 534aecfa433Smark #else 535aecfa433Smark /* 536aecfa433Smark * Avoid stdio and scan tag file linearly. 537aecfa433Smark */ 538aecfa433Smark io = open(fn, 0); 539aecfa433Smark if (io<0) 540aecfa433Smark continue; 541d4cba322Smark tfcount++; 542269e4ba1Sdist if (fstat(io, &sbuf) < 0) 543269e4ba1Sdist bsize = LBSIZE; 544269e4ba1Sdist else { 545269e4ba1Sdist bsize = sbuf.st_blksize; 546269e4ba1Sdist if (bsize <= 0) 547269e4ba1Sdist bsize = LBSIZE; 548269e4ba1Sdist } 54906fb5685Smark while (getfile() == 0) { 550aecfa433Smark #endif 551aecfa433Smark /* loop for each tags file entry */ 55206fb5685Smark register char *cp = linebuf; 55306fb5685Smark register char *lp = lasttag; 55406fb5685Smark char *oglobp; 55506fb5685Smark 556269e4ba1Sdist #ifdef FASTTAG 557aecfa433Smark mid = (top + bot) / 2; 558269e4ba1Sdist tseek(iof, mid); 559aecfa433Smark if (mid > 0) /* to get first tag in file to work */ 560269e4ba1Sdist /* scan to next \n */ 561269e4ba1Sdist if(tgets(linebuf, sizeof linebuf, iof)==NULL) 562269e4ba1Sdist goto goleft; 563269e4ba1Sdist /* get the line itself */ 564269e4ba1Sdist if(tgets(linebuf, sizeof linebuf, iof)==NULL) 565269e4ba1Sdist goto goleft; 566269e4ba1Sdist #ifdef TDEBUG 567c0a5300eSconrad ex_printf("tag: %o %o %o %s\n", bot, mid, top, linebuf); 568269e4ba1Sdist #endif 569aecfa433Smark #endif 57006fb5685Smark while (*cp && *lp == *cp) 57106fb5685Smark cp++, lp++; 572269e4ba1Sdist if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 || 573269e4ba1Sdist lp-lasttag < value(TAGLENGTH))) { 574269e4ba1Sdist #ifdef FASTTAG 575aecfa433Smark if (*lp > *cp) 576aecfa433Smark bot = mid + 1; 577aecfa433Smark else 578269e4ba1Sdist goleft: 579aecfa433Smark top = mid - 1; 580aecfa433Smark #endif 581aecfa433Smark /* Not this tag. Try the next */ 58206fb5685Smark continue; 583aecfa433Smark } 584aecfa433Smark 585aecfa433Smark /* 586aecfa433Smark * We found the tag. Decode the line in the file. 587aecfa433Smark */ 588269e4ba1Sdist #ifdef FASTTAG 589269e4ba1Sdist tclose(iof); 590aecfa433Smark #else 59106fb5685Smark close(io); 592aecfa433Smark #endif 593d4cba322Smark /* Rest of tag if abbreviated */ 594d4cba322Smark while (!iswhite(*cp)) 595d4cba322Smark cp++; 596d4cba322Smark 597aecfa433Smark /* name of file */ 59806fb5685Smark while (*cp && iswhite(*cp)) 59906fb5685Smark cp++; 60006fb5685Smark if (!*cp) 60106fb5685Smark badtags: 60206fb5685Smark serror("%s: Bad tags file entry", lasttag); 60306fb5685Smark lp = filebuf; 60406fb5685Smark while (*cp && *cp != ' ' && *cp != '\t') { 60506fb5685Smark if (lp < &filebuf[sizeof filebuf - 2]) 60606fb5685Smark *lp++ = *cp; 60706fb5685Smark cp++; 60806fb5685Smark } 60906fb5685Smark *lp++ = 0; 610aecfa433Smark 61106fb5685Smark if (*cp == 0) 61206fb5685Smark goto badtags; 61306fb5685Smark if (dol != zero) { 61406fb5685Smark /* 61506fb5685Smark * Save current position in 't for ^^ in visual. 61606fb5685Smark */ 61706fb5685Smark names['t'-'a'] = *dot &~ 01; 61806fb5685Smark if (inopen) { 619d4cba322Smark extern char *ncols['z'-'a'+2]; 62006fb5685Smark extern char *cursor; 62106fb5685Smark 62206fb5685Smark ncols['t'-'a'] = cursor; 62306fb5685Smark } 62406fb5685Smark } 62506fb5685Smark strcpy(cmdbuf, cp); 62606fb5685Smark if (strcmp(filebuf, savedfile) || !edited) { 62706fb5685Smark char cmdbuf2[sizeof filebuf + 10]; 62806fb5685Smark 629aecfa433Smark /* Different file. Do autowrite & get it. */ 63006fb5685Smark if (!quick) { 63106fb5685Smark ckaw(); 63206fb5685Smark if (chng && dol > zero) 63306fb5685Smark error("No write@since last change (:tag! overrides)"); 63406fb5685Smark } 63506fb5685Smark oglobp = globp; 63606fb5685Smark strcpy(cmdbuf2, "e! "); 63706fb5685Smark strcat(cmdbuf2, filebuf); 63806fb5685Smark globp = cmdbuf2; 63906fb5685Smark d = peekc; ungetchar(0); 640aecfa433Smark commands(1, 1); 641aecfa433Smark peekc = d; 642aecfa433Smark globp = oglobp; 643aecfa433Smark value(MAGIC) = omagic; 644aecfa433Smark samef = 0; 645aecfa433Smark } 646aecfa433Smark 647aecfa433Smark /* 648aecfa433Smark * Look for pattern in the current file. 649aecfa433Smark */ 650aecfa433Smark oglobp = globp; 651aecfa433Smark globp = cmdbuf; 652aecfa433Smark d = peekc; ungetchar(0); 653aecfa433Smark if (samef) 654aecfa433Smark markpr(dot); 655f80bacf1Smark /* 656f80bacf1Smark * BUG: if it isn't found (user edited header 657f80bacf1Smark * line) we get left in nomagic mode. 658f80bacf1Smark */ 659f80bacf1Smark value(MAGIC) = 0; 66006fb5685Smark commands(1, 1); 66106fb5685Smark peekc = d; 66206fb5685Smark globp = oglobp; 663f80bacf1Smark value(MAGIC) = omagic; 66406fb5685Smark return; 665aecfa433Smark } /* end of "for each tag in file" */ 666aecfa433Smark 667aecfa433Smark /* 668aecfa433Smark * No such tag in this file. Close it and try the next. 669aecfa433Smark */ 670269e4ba1Sdist #ifdef FASTTAG 671269e4ba1Sdist tclose(iof); 672aecfa433Smark #else 673aecfa433Smark close(io); 674aecfa433Smark #endif 675aecfa433Smark } /* end of "for each file in path" */ 676aecfa433Smark if (tfcount <= 0) 67706fb5685Smark error("No tags file"); 678aecfa433Smark else 67906fb5685Smark serror("%s: No such tag@in tags file", lasttag); 68006fb5685Smark } 68106fb5685Smark 68206fb5685Smark /* 68306fb5685Smark * Save lines from addr1 thru addr2 as though 68406fb5685Smark * they had been deleted. 68506fb5685Smark */ 68606fb5685Smark yank() 68706fb5685Smark { 68806fb5685Smark 689aecfa433Smark if (!FIXUNDO) 690aecfa433Smark error("Can't yank inside global/macro"); 69106fb5685Smark save12(); 69206fb5685Smark undkind = UNDNONE; 69306fb5685Smark killcnt(addr2 - addr1 + 1); 69406fb5685Smark } 69506fb5685Smark 69606fb5685Smark /* 69706fb5685Smark * z command; print windows of text in the file. 69806fb5685Smark * 69906fb5685Smark * If this seems unreasonably arcane, the reasons 70006fb5685Smark * are historical. This is one of the first commands 70106fb5685Smark * added to the first ex (then called en) and the 70206fb5685Smark * number of facilities here were the major advantage 70306fb5685Smark * of en over ed since they allowed more use to be 70406fb5685Smark * made of fast terminals w/o typing .,.22p all the time. 70506fb5685Smark */ 70606fb5685Smark bool zhadpr; 70706fb5685Smark bool znoclear; 70806fb5685Smark short zweight; 70906fb5685Smark 71006fb5685Smark zop(hadpr) 71106fb5685Smark int hadpr; 71206fb5685Smark { 71306fb5685Smark register int c, lines, op; 71406fb5685Smark bool excl; 71506fb5685Smark 71606fb5685Smark zhadpr = hadpr; 71706fb5685Smark notempty(); 71806fb5685Smark znoclear = 0; 71906fb5685Smark zweight = 0; 72006fb5685Smark excl = exclam(); 721c0a5300eSconrad switch (c = op = ex_getchar()) { 72206fb5685Smark 72306fb5685Smark case '^': 72406fb5685Smark zweight = 1; 72506fb5685Smark case '-': 72606fb5685Smark case '+': 72706fb5685Smark while (peekchar() == op) { 72806fb5685Smark ignchar(); 72906fb5685Smark zweight++; 73006fb5685Smark } 73106fb5685Smark case '=': 73206fb5685Smark case '.': 733c0a5300eSconrad c = ex_getchar(); 73406fb5685Smark break; 73506fb5685Smark 73606fb5685Smark case EOF: 73706fb5685Smark znoclear++; 73806fb5685Smark break; 73906fb5685Smark 74006fb5685Smark default: 74106fb5685Smark op = 0; 74206fb5685Smark break; 74306fb5685Smark } 74406fb5685Smark if (isdigit(c)) { 74506fb5685Smark lines = c - '0'; 74606fb5685Smark for(;;) { 747c0a5300eSconrad c = ex_getchar(); 74806fb5685Smark if (!isdigit(c)) 74906fb5685Smark break; 75006fb5685Smark lines *= 10; 75106fb5685Smark lines += c - '0'; 75206fb5685Smark } 75306fb5685Smark if (lines < LINES) 75406fb5685Smark znoclear++; 75506fb5685Smark value(WINDOW) = lines; 75606fb5685Smark if (op == '=') 75706fb5685Smark lines += 2; 75806fb5685Smark } else 75906fb5685Smark lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL); 76006fb5685Smark if (inopen || c != EOF) { 76106fb5685Smark ungetchar(c); 76206fb5685Smark newline(); 76306fb5685Smark } 76406fb5685Smark addr1 = addr2; 76506fb5685Smark if (addr2 == 0 && dot < dol && op == 0) 76606fb5685Smark addr1 = addr2 = dot+1; 76706fb5685Smark setdot(); 76806fb5685Smark zop2(lines, op); 76906fb5685Smark } 77006fb5685Smark 77106fb5685Smark zop2(lines, op) 77206fb5685Smark register int lines; 77306fb5685Smark register int op; 77406fb5685Smark { 77506fb5685Smark register line *split; 77606fb5685Smark 77706fb5685Smark split = NULL; 77806fb5685Smark switch (op) { 77906fb5685Smark 78006fb5685Smark case EOF: 78106fb5685Smark if (addr2 == dol) 78206fb5685Smark error("\nAt EOF"); 78306fb5685Smark case '+': 78406fb5685Smark if (addr2 == dol) 78506fb5685Smark error("At EOF"); 78606fb5685Smark addr2 += lines * zweight; 78706fb5685Smark if (addr2 > dol) 78806fb5685Smark error("Hit BOTTOM"); 78906fb5685Smark addr2++; 79006fb5685Smark default: 79106fb5685Smark addr1 = addr2; 79206fb5685Smark addr2 += lines-1; 79306fb5685Smark dot = addr2; 79406fb5685Smark break; 79506fb5685Smark 79606fb5685Smark case '=': 79706fb5685Smark case '.': 79806fb5685Smark znoclear = 0; 79906fb5685Smark lines--; 80006fb5685Smark lines >>= 1; 80106fb5685Smark if (op == '=') 80206fb5685Smark lines--; 80306fb5685Smark addr1 = addr2 - lines; 80406fb5685Smark if (op == '=') 80506fb5685Smark dot = split = addr2; 80606fb5685Smark addr2 += lines; 80706fb5685Smark if (op == '.') { 80806fb5685Smark markDOT(); 80906fb5685Smark dot = addr2; 81006fb5685Smark } 81106fb5685Smark break; 81206fb5685Smark 81306fb5685Smark case '^': 81406fb5685Smark case '-': 81506fb5685Smark addr2 -= lines * zweight; 81606fb5685Smark if (addr2 < one) 81706fb5685Smark error("Hit TOP"); 81806fb5685Smark lines--; 81906fb5685Smark addr1 = addr2 - lines; 82006fb5685Smark dot = addr2; 82106fb5685Smark break; 82206fb5685Smark } 82306fb5685Smark if (addr1 <= zero) 82406fb5685Smark addr1 = one; 82506fb5685Smark if (addr2 > dol) 82606fb5685Smark addr2 = dol; 82706fb5685Smark if (dot > dol) 82806fb5685Smark dot = dol; 82906fb5685Smark if (addr1 > addr2) 83006fb5685Smark return; 83106fb5685Smark if (op == EOF && zhadpr) { 83206fb5685Smark getline(*addr1); 833c0a5300eSconrad ex_putchar('\r' | QUOTE); 83406fb5685Smark shudclob = 1; 83506fb5685Smark } else if (znoclear == 0 && CL != NOSTR && !inopen) { 83606fb5685Smark flush1(); 83706fb5685Smark vclear(); 83806fb5685Smark } 83906fb5685Smark if (addr2 - addr1 > 1) 84006fb5685Smark pstart(); 84106fb5685Smark if (split) { 84206fb5685Smark plines(addr1, split - 1, 0); 84306fb5685Smark splitit(); 84406fb5685Smark plines(split, split, 0); 84506fb5685Smark splitit(); 84606fb5685Smark addr1 = split + 1; 84706fb5685Smark } 84806fb5685Smark plines(addr1, addr2, 0); 84906fb5685Smark } 85006fb5685Smark 85106fb5685Smark static 85206fb5685Smark splitit() 85306fb5685Smark { 85406fb5685Smark register int l; 85506fb5685Smark 85606fb5685Smark for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--) 857c0a5300eSconrad ex_putchar('-'); 85806fb5685Smark putnl(); 85906fb5685Smark } 86006fb5685Smark 86106fb5685Smark plines(adr1, adr2, movedot) 86206fb5685Smark line *adr1; 86306fb5685Smark register line *adr2; 86406fb5685Smark bool movedot; 86506fb5685Smark { 86606fb5685Smark register line *addr; 86706fb5685Smark 86806fb5685Smark pofix(); 86906fb5685Smark for (addr = adr1; addr <= adr2; addr++) { 87006fb5685Smark getline(*addr); 87106fb5685Smark pline(lineno(addr)); 87206fb5685Smark if (inopen) 873c0a5300eSconrad ex_putchar('\n' | QUOTE); 87406fb5685Smark if (movedot) 87506fb5685Smark dot = addr; 87606fb5685Smark } 87706fb5685Smark } 87806fb5685Smark 87906fb5685Smark pofix() 88006fb5685Smark { 88106fb5685Smark 88206fb5685Smark if (inopen && Outchar != termchar) { 88306fb5685Smark vnfl(); 88406fb5685Smark setoutt(); 88506fb5685Smark } 88606fb5685Smark } 88706fb5685Smark 88806fb5685Smark /* 88906fb5685Smark * Dudley doright to the rescue. 89006fb5685Smark * Undo saves the day again. 89106fb5685Smark * A tip of the hatlo hat to Warren Teitleman 89206fb5685Smark * who made undo as useful as do. 89306fb5685Smark * 89406fb5685Smark * Command level undo works easily because 89506fb5685Smark * the editor has a unique temporary file 89606fb5685Smark * index for every line which ever existed. 89706fb5685Smark * We don't have to save large blocks of text, 89806fb5685Smark * only the indices which are small. We do this 89906fb5685Smark * by moving them to after the last line in the 90006fb5685Smark * line buffer array, and marking down info 90106fb5685Smark * about whence they came. 90206fb5685Smark * 90306fb5685Smark * Undo is its own inverse. 90406fb5685Smark */ 90506fb5685Smark undo(c) 90606fb5685Smark bool c; 90706fb5685Smark { 90806fb5685Smark register int i; 90906fb5685Smark register line *jp, *kp; 91006fb5685Smark line *dolp1, *newdol, *newadot; 91106fb5685Smark 912aecfa433Smark #ifdef TRACE 913aecfa433Smark if (trace) 914aecfa433Smark vudump("before undo"); 915aecfa433Smark #endif 91606fb5685Smark if (inglobal && inopen <= 0) 91706fb5685Smark error("Can't undo in global@commands"); 91806fb5685Smark if (!c) 91906fb5685Smark somechange(); 92006fb5685Smark pkill[0] = pkill[1] = 0; 92106fb5685Smark change(); 92206fb5685Smark if (undkind == UNDMOVE) { 92306fb5685Smark /* 92406fb5685Smark * Command to be undone is a move command. 92506fb5685Smark * This is handled as a special case by noting that 92606fb5685Smark * a move "a,b m c" can be inverted by another move. 92706fb5685Smark */ 92806fb5685Smark if ((i = (jp = unddel) - undap2) > 0) { 92906fb5685Smark /* 93006fb5685Smark * when c > b inverse is a+(c-b),c m a-1 93106fb5685Smark */ 93206fb5685Smark addr2 = jp; 93306fb5685Smark addr1 = (jp = undap1) + i; 93406fb5685Smark unddel = jp-1; 93506fb5685Smark } else { 93606fb5685Smark /* 93706fb5685Smark * when b > c inverse is c+1,c+1+(b-a) m b 93806fb5685Smark */ 93906fb5685Smark addr1 = ++jp; 94006fb5685Smark addr2 = jp + ((unddel = undap2) - undap1); 94106fb5685Smark } 94206fb5685Smark kp = undap1; 94306fb5685Smark move1(0, unddel); 94406fb5685Smark dot = kp; 94506fb5685Smark Command = "move"; 94606fb5685Smark killed(); 94706fb5685Smark } else { 94806fb5685Smark int cnt; 94906fb5685Smark 95006fb5685Smark newadot = dot; 95106fb5685Smark cnt = lineDOL(); 95206fb5685Smark newdol = dol; 95306fb5685Smark dolp1 = dol + 1; 95406fb5685Smark /* 95506fb5685Smark * Command to be undone is a non-move. 95606fb5685Smark * All such commands are treated as a combination of 95706fb5685Smark * a delete command and a append command. 95806fb5685Smark * We first move the lines appended by the last command 95906fb5685Smark * from undap1 to undap2-1 so that they are just before the 96006fb5685Smark * saved deleted lines. 96106fb5685Smark */ 96206fb5685Smark if ((i = (kp = undap2) - (jp = undap1)) > 0) { 96306fb5685Smark if (kp != dolp1) { 96406fb5685Smark reverse(jp, kp); 96506fb5685Smark reverse(kp, dolp1); 96606fb5685Smark reverse(jp, dolp1); 96706fb5685Smark } 96806fb5685Smark /* 96906fb5685Smark * Account for possible backward motion of target 97006fb5685Smark * for restoration of saved deleted lines. 97106fb5685Smark */ 97206fb5685Smark if (unddel >= jp) 97306fb5685Smark unddel -= i; 97406fb5685Smark newdol -= i; 97506fb5685Smark /* 97606fb5685Smark * For the case where no lines are restored, dot 97706fb5685Smark * is the line before the first line deleted. 97806fb5685Smark */ 97906fb5685Smark dot = jp-1; 98006fb5685Smark } 98106fb5685Smark /* 98206fb5685Smark * Now put the deleted lines, if any, back where they were. 98306fb5685Smark * Basic operation is: dol+1,unddol m unddel 98406fb5685Smark */ 98506fb5685Smark if (undkind == UNDPUT) { 98606fb5685Smark unddel = undap1 - 1; 98706fb5685Smark squish(); 98806fb5685Smark } 98906fb5685Smark jp = unddel + 1; 99006fb5685Smark if ((i = (kp = unddol) - dol) > 0) { 99106fb5685Smark if (jp != dolp1) { 99206fb5685Smark reverse(jp, dolp1); 99306fb5685Smark reverse(dolp1, ++kp); 99406fb5685Smark reverse(jp, kp); 99506fb5685Smark } 99606fb5685Smark /* 99706fb5685Smark * Account for possible forward motion of the target 99806fb5685Smark * for restoration of the deleted lines. 99906fb5685Smark */ 100006fb5685Smark if (undap1 >= jp) 100106fb5685Smark undap1 += i; 100206fb5685Smark /* 100306fb5685Smark * Dot is the first resurrected line. 100406fb5685Smark */ 100506fb5685Smark dot = jp; 100606fb5685Smark newdol += i; 100706fb5685Smark } 100806fb5685Smark /* 100906fb5685Smark * Clean up so we are invertible 101006fb5685Smark */ 101106fb5685Smark unddel = undap1 - 1; 101206fb5685Smark undap1 = jp; 101306fb5685Smark undap2 = jp + i; 101406fb5685Smark dol = newdol; 101506fb5685Smark netchHAD(cnt); 101606fb5685Smark if (undkind == UNDALL) { 101706fb5685Smark dot = undadot; 101806fb5685Smark undadot = newadot; 1019aecfa433Smark } else 102006fb5685Smark undkind = UNDCHANGE; 102106fb5685Smark } 1022d4cba322Smark /* 1023d4cba322Smark * Defensive programming - after a munged undadot. 1024d4cba322Smark * Also handle empty buffer case. 1025d4cba322Smark */ 1026d4cba322Smark if ((dot <= zero || dot > dol) && dot != dol) 102706fb5685Smark dot = one; 1028aecfa433Smark #ifdef TRACE 1029aecfa433Smark if (trace) 1030aecfa433Smark vudump("after undo"); 1031aecfa433Smark #endif 103206fb5685Smark } 103306fb5685Smark 103406fb5685Smark /* 103506fb5685Smark * Be (almost completely) sure there really 103606fb5685Smark * was a change, before claiming to undo. 103706fb5685Smark */ 103806fb5685Smark somechange() 103906fb5685Smark { 104006fb5685Smark register line *ip, *jp; 104106fb5685Smark 104206fb5685Smark switch (undkind) { 104306fb5685Smark 104406fb5685Smark case UNDMOVE: 104506fb5685Smark return; 104606fb5685Smark 104706fb5685Smark case UNDCHANGE: 104806fb5685Smark if (undap1 == undap2 && dol == unddol) 104906fb5685Smark break; 105006fb5685Smark return; 105106fb5685Smark 105206fb5685Smark case UNDPUT: 105306fb5685Smark if (undap1 != undap2) 105406fb5685Smark return; 105506fb5685Smark break; 105606fb5685Smark 105706fb5685Smark case UNDALL: 105806fb5685Smark if (unddol - dol != lineDOL()) 105906fb5685Smark return; 106006fb5685Smark for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++) 106106fb5685Smark if ((*ip &~ 01) != (*jp &~ 01)) 106206fb5685Smark return; 106306fb5685Smark break; 106406fb5685Smark 106506fb5685Smark case UNDNONE: 106606fb5685Smark error("Nothing to undo"); 106706fb5685Smark } 106806fb5685Smark error("Nothing changed|Last undoable command didn't change anything"); 106906fb5685Smark } 107006fb5685Smark 107106fb5685Smark /* 107206fb5685Smark * Map command: 107306fb5685Smark * map src dest 107406fb5685Smark */ 1075d4cba322Smark mapcmd(un, ab) 107606fb5685Smark int un; /* true if this is unmap command */ 1077d4cba322Smark int ab; /* true if this is abbr command */ 107806fb5685Smark { 1079d4cba322Smark char lhs[100], rhs[100]; /* max sizes resp. */ 108006fb5685Smark register char *p; 1081269e4ba1Sdist register int c; /* mjm: char --> int */ 108206fb5685Smark char *dname; 1083aecfa433Smark struct maps *mp; /* the map structure we are working on */ 108406fb5685Smark 1085d4cba322Smark mp = ab ? abbrevs : exclam() ? immacs : arrows; 108606fb5685Smark if (skipend()) { 108706fb5685Smark int i; 108806fb5685Smark 108906fb5685Smark /* print current mapping values */ 109006fb5685Smark if (peekchar() != EOF) 109106fb5685Smark ignchar(); 1092d4cba322Smark if (un) 1093d4cba322Smark error("Missing lhs"); 109406fb5685Smark if (inopen) 109506fb5685Smark pofix(); 1096aecfa433Smark for (i=0; mp[i].mapto; i++) 1097aecfa433Smark if (mp[i].cap) { 1098aecfa433Smark lprintf("%s", mp[i].descr); 1099c0a5300eSconrad ex_putchar('\t'); 1100aecfa433Smark lprintf("%s", mp[i].cap); 1101c0a5300eSconrad ex_putchar('\t'); 1102aecfa433Smark lprintf("%s", mp[i].mapto); 110306fb5685Smark putNFL(); 110406fb5685Smark } 110506fb5685Smark return; 110606fb5685Smark } 110706fb5685Smark 110806fb5685Smark ignore(skipwh()); 110906fb5685Smark for (p=lhs; ; ) { 1110c0a5300eSconrad c = ex_getchar(); 1111*2949201cSbostic if (c == CTRL('v')) { 1112c0a5300eSconrad c = ex_getchar(); 1113d4cba322Smark } else if (!un && any(c, " \t")) { 1114d4cba322Smark /* End of lhs */ 111506fb5685Smark break; 1116d4cba322Smark } else if (endcmd(c) && c!='"') { 111706fb5685Smark ungetchar(c); 111806fb5685Smark if (un) { 111906fb5685Smark newline(); 1120d4cba322Smark *p = 0; 1121aecfa433Smark addmac(lhs, NOSTR, NOSTR, mp); 112206fb5685Smark return; 112306fb5685Smark } else 112406fb5685Smark error("Missing rhs"); 112506fb5685Smark } 112606fb5685Smark *p++ = c; 112706fb5685Smark } 112806fb5685Smark *p = 0; 112906fb5685Smark 113006fb5685Smark if (skipend()) 113106fb5685Smark error("Missing rhs"); 113206fb5685Smark for (p=rhs; ; ) { 1133c0a5300eSconrad c = ex_getchar(); 1134*2949201cSbostic if (c == CTRL('v')) { 1135c0a5300eSconrad c = ex_getchar(); 1136d4cba322Smark } else if (endcmd(c) && c!='"') { 113706fb5685Smark ungetchar(c); 113806fb5685Smark break; 113906fb5685Smark } 114006fb5685Smark *p++ = c; 114106fb5685Smark } 114206fb5685Smark *p = 0; 114306fb5685Smark newline(); 114406fb5685Smark /* 114506fb5685Smark * Special hack for function keys: #1 means key f1, etc. 114606fb5685Smark * If the terminal doesn't have function keys, we just use #1. 114706fb5685Smark */ 114806fb5685Smark if (lhs[0] == '#') { 114906fb5685Smark char *fnkey; 115006fb5685Smark char *fkey(); 115106fb5685Smark char funkey[3]; 115206fb5685Smark 115306fb5685Smark fnkey = fkey(lhs[1] - '0'); 115406fb5685Smark funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0; 115506fb5685Smark if (fnkey) 115606fb5685Smark strcpy(lhs, fnkey); 115706fb5685Smark dname = funkey; 115806fb5685Smark } else { 115906fb5685Smark dname = lhs; 116006fb5685Smark } 1161aecfa433Smark addmac(lhs,rhs,dname,mp); 116206fb5685Smark } 116306fb5685Smark 116406fb5685Smark /* 116506fb5685Smark * Add a macro definition to those that already exist. The sequence of 116606fb5685Smark * chars "src" is mapped into "dest". If src is already mapped into something 116706fb5685Smark * this overrides the mapping. There is no recursion. Unmap is done by 1168aecfa433Smark * using NOSTR for dest. Dname is what to show in listings. mp is 1169aecfa433Smark * the structure to affect (arrows, etc). 117006fb5685Smark */ 1171aecfa433Smark addmac(src,dest,dname,mp) 117206fb5685Smark register char *src, *dest, *dname; 1173aecfa433Smark register struct maps *mp; 117406fb5685Smark { 117506fb5685Smark register int slot, zer; 117606fb5685Smark 1177d4cba322Smark #ifdef TRACE 1178d4cba322Smark if (trace) 1179d4cba322Smark fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp); 1180d4cba322Smark #endif 1181aecfa433Smark if (dest && mp==arrows) { 118206fb5685Smark /* Make sure user doesn't screw himself */ 118306fb5685Smark /* 1184f80bacf1Smark * Prevent tail recursion. We really should be 1185f80bacf1Smark * checking to see if src is a suffix of dest 1186aecfa433Smark * but this makes mapping involving escapes that 1187aecfa433Smark * is reasonable mess up. 118806fb5685Smark */ 1189f80bacf1Smark if (src[1] == 0 && src[0] == dest[strlen(dest)-1]) 1190f80bacf1Smark error("No tail recursion"); 119106fb5685Smark /* 119206fb5685Smark * We don't let the user rob himself of ":", and making 119306fb5685Smark * multi char words is a bad idea so we don't allow it. 119406fb5685Smark * Note that if user sets mapinput and maps all of return, 119506fb5685Smark * linefeed, and escape, he can screw himself. This is 119606fb5685Smark * so weird I don't bother to check for it. 119706fb5685Smark */ 119806fb5685Smark if (isalpha(src[0]) && src[1] || any(src[0],":")) 119906fb5685Smark error("Too dangerous to map that"); 1200aecfa433Smark } 1201aecfa433Smark else if (dest) { 1202aecfa433Smark /* check for tail recursion in input mode: fussier */ 1203aecfa433Smark if (eq(src, dest+strlen(dest)-strlen(src))) 1204aecfa433Smark error("No tail recursion"); 1205aecfa433Smark } 120606fb5685Smark /* 120706fb5685Smark * If the src were null it would cause the dest to 120806fb5685Smark * be mapped always forever. This is not good. 120906fb5685Smark */ 1210aecfa433Smark if (src == NOSTR || src[0] == 0) 1211aecfa433Smark error("Missing lhs"); 121206fb5685Smark 121306fb5685Smark /* see if we already have a def for src */ 121406fb5685Smark zer = -1; 1215aecfa433Smark for (slot=0; mp[slot].mapto; slot++) { 1216aecfa433Smark if (mp[slot].cap) { 1217d4cba322Smark if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto)) 121806fb5685Smark break; /* if so, reuse slot */ 121906fb5685Smark } else { 122006fb5685Smark zer = slot; /* remember an empty slot */ 122106fb5685Smark } 122206fb5685Smark } 122306fb5685Smark 122406fb5685Smark if (dest == NOSTR) { 122506fb5685Smark /* unmap */ 1226aecfa433Smark if (mp[slot].cap) { 1227aecfa433Smark mp[slot].cap = NOSTR; 1228aecfa433Smark mp[slot].descr = NOSTR; 122906fb5685Smark } else { 123006fb5685Smark error("Not mapped|That macro wasn't mapped"); 123106fb5685Smark } 123206fb5685Smark return; 123306fb5685Smark } 123406fb5685Smark 123506fb5685Smark /* reuse empty slot, if we found one and src isn't already defined */ 1236aecfa433Smark if (zer >= 0 && mp[slot].mapto == 0) 123706fb5685Smark slot = zer; 123806fb5685Smark 123906fb5685Smark /* if not, append to end */ 124006fb5685Smark if (slot >= MAXNOMACS) 124106fb5685Smark error("Too many macros"); 124206fb5685Smark if (msnext == 0) /* first time */ 124306fb5685Smark msnext = mapspace; 124406fb5685Smark /* Check is a bit conservative, we charge for dname even if reusing src */ 124506fb5685Smark if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS) 124606fb5685Smark error("Too much macro text"); 124706fb5685Smark CP(msnext, src); 1248aecfa433Smark mp[slot].cap = msnext; 124906fb5685Smark msnext += strlen(src) + 1; /* plus 1 for null on the end */ 125006fb5685Smark CP(msnext, dest); 1251aecfa433Smark mp[slot].mapto = msnext; 125206fb5685Smark msnext += strlen(dest) + 1; 125306fb5685Smark if (dname) { 125406fb5685Smark CP(msnext, dname); 1255aecfa433Smark mp[slot].descr = msnext; 125606fb5685Smark msnext += strlen(dname) + 1; 125706fb5685Smark } else { 125806fb5685Smark /* default descr to string user enters */ 1259aecfa433Smark mp[slot].descr = src; 126006fb5685Smark } 126106fb5685Smark } 126206fb5685Smark 126306fb5685Smark /* 126406fb5685Smark * Implements macros from command mode. c is the buffer to 126506fb5685Smark * get the macro from. 126606fb5685Smark */ 126706fb5685Smark cmdmac(c) 126806fb5685Smark char c; 126906fb5685Smark { 127006fb5685Smark char macbuf[BUFSIZ]; 127106fb5685Smark line *ad, *a1, *a2; 127206fb5685Smark char *oglobp; 1273269e4ba1Sdist short pk; 127406fb5685Smark bool oinglobal; 127506fb5685Smark 127606fb5685Smark lastmac = c; 127706fb5685Smark oglobp = globp; 127806fb5685Smark oinglobal = inglobal; 127906fb5685Smark pk = peekc; peekc = 0; 128006fb5685Smark if (inglobal < 2) 128106fb5685Smark inglobal = 1; 128206fb5685Smark regbuf(c, macbuf, sizeof(macbuf)); 128306fb5685Smark a1 = addr1; a2 = addr2; 128406fb5685Smark for (ad=a1; ad<=a2; ad++) { 128506fb5685Smark globp = macbuf; 128606fb5685Smark dot = ad; 128706fb5685Smark commands(1,1); 128806fb5685Smark } 128906fb5685Smark globp = oglobp; 129006fb5685Smark inglobal = oinglobal; 129106fb5685Smark peekc = pk; 129206fb5685Smark } 1293