home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / ex / ex_cmdsub.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  16KB  |  907 lines

  1. /* Copyright (c) 1979 Regents of the University of California */
  2. #include "ex.h"
  3. #include "ex_argv.h"
  4. #include "ex_temp.h"
  5. #include "ex_tty.h"
  6.  
  7. /*
  8.  * Command mode subroutines implementing
  9.  *    append, args, copy, delete, join, move, put,
  10.  *    shift, tag, yank, z and undo
  11.  */
  12.  
  13. bool    endline = 1;
  14. line    *tad1;
  15.  
  16. /*
  17.  * Append after line a lines returned by function f.
  18.  * Be careful about intermediate states to avoid scramble
  19.  * if an interrupt comes in.
  20.  */
  21. append(f, a)
  22.     int (*f)();
  23.     line *a;
  24. {
  25.     register line *a1, *a2, *rdot;
  26.     int nline;
  27.  
  28.     nline = 0;
  29.     dot = a;
  30.     if (!inglobal && !inopen && f != getsub) {
  31.         undap1 = undap2 = dot + 1;
  32.         undkind = UNDCHANGE;
  33.     }
  34.     while ((*f)() == 0) {
  35.         if (truedol >= endcore) {
  36.             if (morelines() < 0) {
  37.                 if (!inglobal && f == getsub) {
  38.                     undap1 = addr1;
  39.                     undap2 = addr2 + 1;
  40.                 }
  41.                 error("Out of memory@- too many lines in file");
  42.             }
  43.         }
  44.         nline++;
  45.         a1 = truedol + 1;
  46.         a2 = a1 + 1;
  47.         dot++;
  48.         undap2++;
  49.         dol++;
  50.         unddol++;
  51.         truedol++;
  52.         for (rdot = dot; a1 > rdot;)
  53.             *--a2 = *--a1;
  54.         *rdot = 0;
  55.         putmark(rdot);
  56.         if (f == gettty) {
  57.             dirtcnt++;
  58.             TSYNC();
  59.         }
  60.     }
  61.     return (nline);
  62. }
  63.  
  64. appendnone()
  65. {
  66.  
  67.     if (!inglobal || inopen > 0) {
  68.         undkind = UNDCHANGE;
  69.         undap1 = undap2 = addr1;
  70.     }
  71. }
  72.  
  73. /*
  74.  * Print out the argument list, with []'s around the current name.
  75.  */
  76. pargs()
  77. {
  78.     register char **av = argv0, *as = args0;
  79.     register int ac;
  80.  
  81.     for (ac = 0; ac < argc0; ac++) {
  82.         if (ac != 0)
  83.             putchar(' ');
  84.         if (ac + argc == argc0 - 1)
  85.             printf("[");
  86.         lprintf("%s", as);
  87.         if (ac + argc == argc0 - 1)
  88.             printf("]");
  89.         as = av ? *++av : strend(as) + 1;
  90.     }
  91.     noonl();
  92. }
  93.  
  94. /*
  95.  * Delete lines; two cases are if we are really deleting,
  96.  * more commonly we are just moving lines to the undo save area.
  97.  */
  98. delete(hush)
  99.     bool hush;
  100. {
  101.     register line *a1, *a2;
  102.  
  103.     nonzero();
  104.     if (!inglobal || inopen > 0) {
  105.         register int (*dsavint)();
  106.  
  107.         change();
  108.         dsavint = signal(SIGINT, SIG_IGN);
  109.         undkind = UNDCHANGE;
  110.         a1 = addr1;
  111.         squish();
  112.         a2 = addr2;
  113.         if (a2++ != dol) {
  114.             reverse(a1, a2);
  115.             reverse(a2, dol + 1);
  116.             reverse(a1, dol + 1);
  117.         }
  118.         dol -= a2 - a1;
  119.         unddel = a1 - 1;
  120.         if (a1 > dol)
  121.             a1 = dol;
  122.         dot = a1;
  123.         pkill[0] = pkill[1] = 0;
  124.         signal(SIGINT, dsavint);
  125.     } else {
  126.         register line *a3;
  127.         register int i;
  128.  
  129.         change();
  130.         a1 = addr1;
  131.         a2 = addr2 + 1;
  132.         a3 = truedol;
  133.         i = a2 - a1;
  134.         unddol -= i;
  135.         undap2 -= i;
  136.         dol -= i;
  137.         truedol -= i;
  138.         do
  139.             *a1++ = *a2++;
  140.         while (a2 <= a3);
  141.         a1 = addr1;
  142.         if (a1 > dol)
  143.             a1 = dol;
  144.         dot = a1;
  145.     }
  146.     if (!hush)
  147.         killed();
  148. }
  149.  
  150. deletenone()
  151. {
  152.  
  153.     if (!inglobal || inopen > 0) {
  154.         undkind = UNDCHANGE;
  155.         squish();
  156.         unddel = addr1;
  157.     }
  158. }
  159.  
  160. /*
  161.  * Crush out the undo save area, moving the open/visual
  162.  * save area down in its place.
  163.  */
  164. squish()
  165. {
  166.     register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
  167.  
  168.     if (a1 < a2 && a2 < a3)
  169.         do
  170.             *a1++ = *a2++;
  171.         while (a2 < a3);
  172.     truedol -= unddol - dol;
  173.     unddol = dol;
  174. }
  175.  
  176. /*
  177.  * Join lines.  Special hacks put in spaces, two spaces if
  178.  * preceding line ends with '.', or no spaces if next line starts with ).
  179.  */
  180. static    int jcount, jnoop();
  181.  
  182. join(c)
  183.     int c;
  184. {
  185.     register line *a1;
  186.     register char *cp, *cp1;
  187.  
  188.     cp = genbuf;
  189.     *cp = 0;
  190.     for (a1 = addr1; a1 <= addr2; a1++) {
  191.         getline(*a1);
  192.         cp1 = linebuf;
  193.         if (a1 != addr1 && c == 0) {
  194.             while (*cp1 == ' ' || *cp1 == '\t')
  195.                 cp1++;
  196.             if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
  197.                 if (*cp1 != ')') {
  198.                     *cp++ = ' ';
  199.                     if (cp[-2] == '.')
  200.                         *cp++ = ' ';
  201.                 }
  202.             }
  203.         }
  204.         while (*cp++ = *cp1++)
  205.             if (cp > &genbuf[LBSIZE-2])
  206.                 error("Line overflow|Result line of join would be too long");
  207.         cp--;
  208.     }
  209.     strcLIN(genbuf);
  210.     delete(0);
  211.     jcount = 1;
  212.     ignore(append(jnoop, --addr1));
  213. }
  214.  
  215. static
  216. jnoop()
  217. {
  218.  
  219.     return(--jcount);
  220. }
  221.  
  222. /*
  223.  * Move and copy lines.  Hard work is done by move1 which
  224.  * is also called by undo.
  225.  */
  226. int    getcopy();
  227.  
  228. move()
  229. {
  230.     register line *adt;
  231.     bool iscopy = 0;
  232.  
  233.     if (Command[0] == 'm') {
  234.         setdot1();
  235.         markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
  236.     } else {
  237.         iscopy++;
  238.         setdot();
  239.     }
  240.     nonzero();
  241.     adt = address();
  242.     if (adt == 0)
  243.         serror("%s where?|%s requires a trailing address", Command);
  244.     newline();
  245.     move1(iscopy, adt);
  246.     killed();
  247. }
  248.  
  249. move1(cflag, addrt)
  250.     int cflag;
  251.     line *addrt;
  252. {
  253.     register line *adt, *ad1, *ad2;
  254.     int lines;
  255.  
  256.     adt = addrt;
  257.     lines = (addr2 - addr1) + 1;
  258.     if (cflag) {
  259.         tad1 = addr1;
  260.         ad1 = dol;
  261.         ignore(append(getcopy, ad1++));
  262.         ad2 = dol;
  263.     } else {
  264.         ad2 = addr2;
  265.         for (ad1 = addr1; ad1 <= ad2;)
  266.             *ad1++ &= ~01;
  267.         ad1 = addr1;
  268.     }
  269.     ad2++;
  270.     if (adt < ad1) {
  271.         if (adt + 1 == ad1 && !cflag && !inglobal)
  272.             error("That move would do nothing!");
  273.         dot = adt + (ad2 - ad1);
  274.         if (++adt != ad1) {
  275.             reverse(adt, ad1);
  276.             reverse(ad1, ad2);
  277.             reverse(adt, ad2);
  278.         }
  279.     } else if (adt >= ad2) {
  280.         dot = adt++;
  281.         reverse(ad1, ad2);
  282.         reverse(ad2, adt);
  283.         reverse(ad1, adt);
  284.     } else
  285.         error("Move to a moved line");
  286.     change();
  287.     if (!inglobal)
  288.         if (cflag) {
  289.             undap1 = addrt + 1;
  290.             undap2 = undap1 + lines;
  291.             deletenone();
  292.         } else {
  293.             undkind = UNDMOVE;
  294.             undap1 = addr1;
  295.             undap2 = addr2;
  296.             unddel = addrt;
  297.             squish();
  298.         }
  299. }
  300.  
  301. getcopy()
  302. {
  303.  
  304.     if (tad1 > addr2)
  305.         return (EOF);
  306.     getline(*tad1++);
  307.     return (0);
  308. }
  309.  
  310. /*
  311.  * Put lines in the buffer from the undo save area.
  312.  */
  313. getput()
  314. {
  315.  
  316.     if (tad1 > unddol)
  317.         return (EOF);
  318.     getline(*tad1++);
  319.     tad1++;
  320.     return (0);
  321. }
  322.  
  323. put()
  324. {
  325.     register int cnt;
  326.  
  327.     cnt = unddol - dol;
  328.     if (cnt && inopen && pkill[0] && pkill[1]) {
  329.         pragged(1);
  330.         return;
  331.     }
  332.     tad1 = dol + 1;
  333.     ignore(append(getput, addr2));
  334.     undkind = UNDPUT;
  335.     notecnt = cnt;
  336.     netchange(cnt);
  337. }
  338.  
  339. /*
  340.  * A tricky put, of a group of lines in the middle
  341.  * of an existing line.  Only from open/visual.
  342.  * Argument says pkills have meaning, e.g. called from
  343.  * put; it is 0 on calls from putreg.
  344.  */
  345. pragged(kill)
  346.     bool kill;
  347. {
  348.     extern char *cursor;
  349.     register char *gp = &genbuf[cursor - linebuf];
  350.  
  351.     /*
  352.      * This kind of stuff is TECO's forte.
  353.      * We just grunge along, since it cuts
  354.      * across our line-oriented model of the world
  355.      * almost scrambling our addled brain.
  356.      */
  357.     if (!kill)
  358.         getDOT();
  359.     strcpy(genbuf, linebuf);
  360.     getline(*unddol);
  361.     if (kill)
  362.         *pkill[1] = 0;
  363.     strcat(linebuf, gp);
  364.     putmark(unddol);
  365.     getline(dol[1]);
  366.     if (kill)
  367.         strcLIN(pkill[0]);
  368.     strcpy(gp, linebuf);
  369.     strcLIN(genbuf);
  370.     putmark(dol+1);
  371.     undkind = UNDCHANGE;
  372.     undap1 = dot;
  373.     undap2 = dot + 1;
  374.     unddel = dot - 1;
  375.     undo(1);
  376. }
  377.  
  378. /*
  379.  * Shift lines, based on c.
  380.  * If c is neither < nor >, then this is a lisp aligning =.
  381.  */
  382. shift(c, cnt)
  383.     int c;
  384.     int cnt;
  385. {
  386.     register line *addr;
  387.     register char *cp;
  388.     char *dp;
  389.     register int i;
  390.  
  391.     if (!inglobal)
  392.         save12(), undkind = UNDCHANGE;
  393.     cnt *= value(SHIFTWIDTH);
  394.     for (addr = addr1; addr <= addr2; addr++) {
  395.         dot = addr;
  396. #ifdef LISP
  397.         if (c == '=' && addr == addr1 && addr != addr2)
  398.             continue;
  399. #endif
  400.         getDOT();
  401.         i = whitecnt(linebuf);
  402.         switch (c) {
  403.  
  404.         case '>':
  405.             if (linebuf[0] == 0)
  406.                 continue;
  407.             cp = genindent(i + cnt);
  408.             break;
  409.  
  410.         case '<':
  411.             if (i == 0)
  412.                 continue;
  413.             i -= cnt;
  414.             cp = i > 0 ? genindent(i) : genbuf;
  415.             break;
  416.  
  417. #ifdef LISP
  418.         default:
  419.             i = lindent(addr);
  420.             getDOT();
  421.             cp = genindent(i);
  422.             break;
  423. #endif
  424.         }
  425.         if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
  426.             error("Line too long|Result line after shift would be too long");
  427.         CP(cp, dp);
  428.         strcLIN(genbuf);
  429.         putmark(addr);
  430.     }
  431.     killed();
  432. }
  433.  
  434. /*
  435.  * Find a tag in the tags file.
  436.  * Most work here is in parsing the tags file itself.
  437.  */
  438. tagfind(quick)
  439.     bool quick;
  440. {
  441.     char cmdbuf[BUFSIZ];
  442.     register int c, d;
  443.     bool samef = 1;
  444.     short master = -1;
  445.  
  446.     intag = 1;
  447.     if (!skipend()) {
  448.         register char *lp = lasttag;
  449.  
  450.         while (!iswhite(peekchar()) && !endcmd(peekchar()))
  451.             if (lp < &lasttag[sizeof lasttag - 2])
  452.                 *lp++ = getchar();
  453.             else
  454.                 ignchar();
  455.         *lp++ = 0;
  456.         if (!endcmd(peekchar()))
  457. badtag:
  458.             error("Bad tag|Give one tag per line");
  459.     } else if (lasttag[0] == 0)
  460.         error("No previous tag");
  461.     c = getchar();
  462.     if (!endcmd(c))
  463.         goto badtag;
  464.     if (c == EOF)
  465.         ungetchar(c);
  466.     clrstats();
  467.     do {
  468.         io = open(master ? "tags" : MASTERTAGS, 0);
  469.         while (getfile() == 0) {
  470.             register char *cp = linebuf;
  471.             register char *lp = lasttag;
  472.             char *oglobp;
  473.  
  474.             while (*cp && *lp == *cp)
  475.                 cp++, lp++;
  476.             if (*lp || *cp != '\t')
  477.                 continue;
  478.             close(io);
  479.             while (*cp && iswhite(*cp))
  480.                 cp++;
  481.             if (!*cp)
  482. badtags:
  483.                 serror("%s: Bad tags file entry", lasttag);
  484.             lp = file;
  485.             while (*cp && *cp != ' ' && *cp != '\t') {
  486.                 if (lp < &file[sizeof file - 2])
  487.                     *lp++ = *cp;
  488.                 cp++;
  489.             }
  490.             *lp++ = 0;
  491.             if (*cp == 0)
  492.                 goto badtags;
  493.             if (dol != zero) {
  494.                 /*
  495.                  * Save current position in 't for ^^ in visual.
  496.                  */
  497.                 names['t'-'a'] = *dot &~ 01;
  498.                 if (inopen) {
  499.                     extern char *ncols['z'-'a'+1];
  500.                     extern char *cursor;
  501.  
  502.                     ncols['t'-'a'] = cursor;
  503.                 }
  504.             }
  505.             strcpy(cmdbuf, cp);
  506.             if (strcmp(file, savedfile) || !edited) {
  507.                 char cmdbuf2[sizeof file + 10];
  508.  
  509.                 if (!quick && chng)
  510.                     error("No write@since last change (tag! overrides)");
  511.                 oglobp = globp;
  512.                 strcpy(cmdbuf2, "e! ");
  513.                 strcat(cmdbuf2, file);
  514.                 globp = cmdbuf2;
  515.                 d = peekc; ungetchar(0);
  516.                 commands(1, 1);
  517.                 peekc = d;
  518.                 globp = oglobp;
  519.                 samef = 0;
  520.             }
  521.             oglobp = globp;
  522.             globp = cmdbuf;
  523.             d = peekc; ungetchar(0);
  524.             if (samef)
  525.                 markpr(dot);
  526.             commands(1, 1);
  527.             peekc = d;
  528.             globp = oglobp;
  529.             intag = 0;
  530.             return;
  531.         }
  532.     } while (++master == 0);
  533.     serror("%s: No such tag@in tags file", lasttag);
  534. }
  535.  
  536. /*
  537.  * Save lines from addr1 thru addr2 as though
  538.  * they had been deleted.
  539.  */
  540. yank()
  541. {
  542.  
  543.     save12();
  544.     undkind = UNDNONE;
  545.     killcnt(addr2 - addr1 + 1);
  546. }
  547.  
  548. /*
  549.  * z command; print windows of text in the file.
  550.  *
  551.  * If this seems unreasonably arcane, the reasons
  552.  * are historical.  This is one of the first commands
  553.  * added to the first ex (then called en) and the
  554.  * number of facilities here were the major advantage
  555.  * of en over ed since they allowed more use to be
  556.  * made of fast terminals w/o typing .,.22p all the time.
  557.  */
  558. bool    zhadpr;
  559. bool    znoclear;
  560. short    zweight;
  561.  
  562. zop(hadpr)
  563.     int hadpr;
  564. {
  565.     register int c, lines, op;
  566.     bool excl;
  567.  
  568.     zhadpr = hadpr;
  569.     notempty();
  570.     znoclear = 0;
  571.     zweight = 0;
  572.     excl = exclam();
  573.     switch (c = op = getchar()) {
  574.  
  575.     case '^':
  576.         zweight = 1;
  577.     case '-':
  578.     case '+':
  579.         while (peekchar() == op) {
  580.             ignchar();
  581.             zweight++;
  582.         }
  583.     case '=':
  584.     case '.':
  585.         c = getchar();
  586.         break;
  587.  
  588.     case EOF:
  589.         znoclear++;
  590.         break;
  591.  
  592.     default:
  593.         op = 0;
  594.         break;
  595.     }
  596.     if (isdigit(c)) {
  597.         lines = c - '0';
  598.         for(;;) {
  599.             c = getchar();
  600.             if (!isdigit(c))
  601.                 break;
  602.             lines *= 10;
  603.             lines += c - '0';
  604.         }
  605.         if (lines < value(WINDOW))
  606.             znoclear++;
  607.         if (op == '=')
  608.             lines += 2;
  609.     } else
  610.         lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : value(WINDOW);
  611.     if (inopen || c != EOF) {
  612.         ungetchar(c);
  613.         newline();
  614.     }
  615.     addr1 = addr2;
  616.     setdot();
  617.     zop2(lines, op);
  618. }
  619.  
  620. zop2(lines, op)
  621.     register int lines;
  622.     register int op;
  623. {
  624.     register line *split;
  625.  
  626.     split = NULL;
  627.     switch (op) {
  628.  
  629.     case EOF:
  630.         if (addr2 == dol)
  631.             error("\nAt EOF");
  632.     case '+':
  633.         if (addr2 == dol)
  634.             error("At EOF");
  635.         addr2 += lines * zweight;
  636.         if (addr2 > dol)
  637.             error("Hit BOTTOM");
  638.         addr2++;
  639.     default:
  640.         addr1 = addr2;
  641.         addr2 += lines-1;
  642.         dot = addr2;
  643.         break;
  644.  
  645.     case '=':
  646.     case '.':
  647.         znoclear = 0;
  648.         lines--;
  649.         lines >>= 1;
  650.         if (op == '=')
  651.             lines--;
  652.         addr1 = addr2 - lines;
  653.         if (op == '=')
  654.             dot = split = addr2;
  655.         addr2 += lines;
  656.         if (op == '.') {
  657.             markDOT();
  658.             dot = addr2;
  659.         }
  660.         break;
  661.  
  662.     case '^':
  663.     case '-':
  664.         addr2 -= lines * zweight;
  665.         if (addr2 < one)
  666.             error("Hit TOP");
  667.         lines--;
  668.         addr1 = addr2 - lines;
  669.         dot = addr2;
  670.         break;
  671.     }
  672.     if (addr1 <= zero)
  673.         addr1 = one;
  674.     if (addr2 > dol)
  675.         addr2 = dol;
  676.     if (dot > dol)
  677.         dot = dol;
  678.     if (addr1 > addr2)
  679.         return;
  680.     if (op == EOF && zhadpr) {
  681.         getline(*addr1);
  682.         putchar('\r' | QUOTE);
  683.         shudclob = 1;
  684.     } else if (znoclear == 0 && CL != NOSTR && !inopen) {
  685.         flush1();
  686.         vclear();
  687.     }
  688.     if (addr2 - addr1 > 1)
  689.         pstart();
  690.     if (split) {
  691.         plines(addr1, split - 1, 0);
  692.         splitit();
  693.         plines(split, split, 0);
  694.         splitit();
  695.         addr1 = split + 1;
  696.     }
  697.     plines(addr1, addr2, 0);
  698. }
  699.  
  700. static
  701. splitit()
  702. {
  703.     register int l;
  704.  
  705.     for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
  706.         putchar('-');
  707.     putnl();
  708. }
  709.  
  710. plines(adr1, adr2, movedot)
  711.     line *adr1;
  712.     register line *adr2;
  713.     bool movedot;
  714. {
  715.     register line *addr;
  716.  
  717.     pofix();
  718.     for (addr = adr1; addr <= adr2; addr++) {
  719.         getline(*addr);
  720.         pline(lineno(addr));
  721.         if (inopen)
  722.             putchar('\n' | QUOTE);
  723.         if (movedot)
  724.             dot = addr;
  725.     }
  726. }
  727.  
  728. pofix()
  729. {
  730.  
  731.     if (inopen && Outchar != termchar) {
  732.         vnfl();
  733.         setoutt();
  734.     }
  735. }
  736.  
  737. /*
  738.  * Dudley doright to the rescue.
  739.  * Undo saves the day again.
  740.  * A tip of the hatlo hat to Warren Teitleman
  741.  * who made undo as useful as do.
  742.  *
  743.  * Command level undo works easily because
  744.  * the editor has a unique temporary file
  745.  * index for every line which ever existed.
  746.  * We don't have to save large blocks of text,
  747.  * only the indices which are small.  We do this
  748.  * by moving them to after the last line in the
  749.  * line buffer array, and marking down info
  750.  * about whence they came.
  751.  *
  752.  * Undo is its own inverse.
  753.  */
  754. undo(c)
  755.     bool c;
  756. {
  757.     register int i;
  758.     register line *jp, *kp;
  759.     line *dolp1, *newdol, *newadot;
  760.  
  761.     if (inglobal && inopen <= 0)
  762.         error("Can't undo in global@commands");
  763.     if (!c)
  764.         somechange();
  765.     pkill[0] = pkill[1] = 0;
  766.     change();
  767.     if (undkind == UNDMOVE) {
  768.          /*
  769.          * Command to be undone is a move command.
  770.          * This is handled as a special case by noting that
  771.          * a move "a,b m c" can be inverted by another move.
  772.          */
  773.         if ((i = (jp = unddel) - undap2) > 0) {
  774.             /*
  775.              * when c > b inverse is a+(c-b),c m a-1
  776.              */
  777.             addr2 = jp;
  778.             addr1 = (jp = undap1) + i;
  779.             unddel = jp-1;
  780.         } else {
  781.             /*
  782.              * when b > c inverse is  c+1,c+1+(b-a) m b
  783.              */
  784.             addr1 = ++jp;
  785.             addr2 = jp + ((unddel = undap2) - undap1);
  786.         }
  787.         kp = undap1;
  788.         move1(0, unddel);
  789.         dot = kp;
  790.         Command = "move";
  791.         killed();
  792.     } else {
  793.         int cnt;
  794.  
  795.         newadot = dot;
  796.         cnt = lineDOL();
  797.         newdol = dol;
  798.         dolp1 = dol + 1;
  799.         /*
  800.          * Command to be undone is a non-move.
  801.          * All such commands are treated as a combination of
  802.          * a delete command and a append command.
  803.          * We first move the lines appended by the last command
  804.          * from undap1 to undap2-1 so that they are just before the
  805.          * saved deleted lines.
  806.          */
  807.         if ((i = (kp = undap2) - (jp = undap1)) > 0) {
  808.             if (kp != dolp1) {
  809.                 reverse(jp, kp);
  810.                 reverse(kp, dolp1);
  811.                 reverse(jp, dolp1);
  812.             }
  813.             /*
  814.              * Account for possible backward motion of target
  815.              * for restoration of saved deleted lines.
  816.              */
  817.             if (unddel >= jp)
  818.                 unddel -= i;
  819.             newdol -= i;
  820.             /*
  821.              * For the case where no lines are restored, dot
  822.              * is the line before the first line deleted.
  823.              */
  824.             dot = jp-1;
  825.         }
  826.         /*
  827.          * Now put the deleted lines, if any, back where they were.
  828.          * Basic operation is: dol+1,unddol m unddel
  829.          */
  830.         if (undkind == UNDPUT) {
  831.             unddel = undap1 - 1;
  832.             squish();
  833.         }
  834.         jp = unddel + 1;
  835.         if ((i = (kp = unddol) - dol) > 0) {
  836.             if (jp != dolp1) {
  837.                 reverse(jp, dolp1);
  838.                 reverse(dolp1, ++kp);
  839.                 reverse(jp, kp);
  840.             }
  841.             /*
  842.              * Account for possible forward motion of the target
  843.              * for restoration of the deleted lines.
  844.              */
  845.             if (undap1 >= jp)
  846.                 undap1 += i;
  847.             /*
  848.              * Dot is the first resurrected line.
  849.              */
  850.             dot = jp;
  851.             newdol += i;
  852.         }
  853.         /*
  854.          * Clean up so we are invertible
  855.          */
  856.         unddel = undap1 - 1;
  857.         undap1 = jp;
  858.         undap2 = jp + i;
  859.         dol = newdol;
  860.         netchHAD(cnt);
  861.         if (undkind == UNDALL) {
  862.             dot = undadot;
  863.             undadot = newadot;
  864.         }
  865.         undkind = UNDCHANGE;
  866.     }
  867.     if (dot == zero && dot != dol)
  868.         dot = one;
  869. }
  870.  
  871. /*
  872.  * Be (almost completely) sure there really
  873.  * was a change, before claiming to undo.
  874.  */
  875. somechange()
  876. {
  877.     register line *ip, *jp;
  878.  
  879.     switch (undkind) {
  880.  
  881.     case UNDMOVE:
  882.         return;
  883.  
  884.     case UNDCHANGE:
  885.         if (undap1 == undap2 && dol == unddol)
  886.             break;
  887.         return;
  888.  
  889.     case UNDPUT:
  890.         if (undap1 != undap2)
  891.             return;
  892.         break;
  893.  
  894.     case UNDALL:
  895.         if (unddol - dol != lineDOL())
  896.             return;
  897.         for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
  898.             if ((*ip &~ 01) != (*jp &~ 01))
  899.                 return;
  900.         break;
  901.  
  902.     case UNDNONE:
  903.         error("Nothing to undo");
  904.     }
  905.     error("Nothing changed|Last undoable command didn't change anything");
  906. }
  907.