home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / msdos / sysutl / history.arc / EDIT.C next >
Text File  |  1988-03-21  |  16KB  |  688 lines

  1. /* history -- command history mechanism, Copyright 1985-6  Michael M Rubenstein */
  2. /* Portions Copyright 1987, 1988, Russell Nelson */
  3. /* History:19,1 */
  4. /* 01-29-88 19:59:01 add C-A and C-E for home and end respectively. */
  5. /* 10-18-87 12:12:06 give an error if they compile with other than TINY. */
  6. /* 10-18-87 11:51:06 use msdos(8) to read keystrokes. */
  7. /* 07-05-87 13:57:09 lowercase filenames properly this time. */
  8. /* 07-04-87 23:49:11 don't do an automatic search. */
  9. /* 07-03-87 22:07:56 Use '\0' for the null character. */
  10. /* 07-03-87 21:22:23 expand_filename didn't put a trailing \ after directories. */
  11. /* 07-03-87 21:03:57 filename expansion didn't check 'len'. */
  12. /* 07-03-87 19:42:20 in filename completion, files with wildcards don't get wildcards added */
  13. /* 07-03-87 19:36:31 move argc, argv code out of history_init() */
  14. /* 07-03-87 15:49:27 change copyright message */
  15.  
  16. #include <ctype.h>
  17. #include <dos.h>
  18. #include <dir.h>
  19.  
  20. #define FALSE           0
  21. #define TRUE            1
  22. #define NULL            ((void *) 0)
  23.  
  24. /* some interesting characters */
  25. #define CTL             0x1f
  26. #define NUL             0
  27. #define CTLA        (CTL & 'A')
  28. #define CTLB        (CTL & 'B')
  29. #define CTLD        (CTL & 'D')
  30. #define CTLE        (CTL & 'E')
  31. #define CTLF        (CTL & 'F')
  32. #define BEL             (CTL & 'G')
  33. #define BS              (CTL & 'H')
  34. #define LF              (CTL & 'J')
  35. #define CR              (CTL & 'M')
  36. #define CTLL            (CTL & 'L')
  37. #define CTLQ            (CTL & 'Q')
  38. #define CTLT            (CTL & 'T')
  39. #define CTLU            (CTL & 'U')
  40. #define CTLW            (CTL & 'W')
  41. #define CTLX            (CTL & 'X')
  42. #define CTLY            (CTL & 'Y')
  43. #define ESC             (CTL & '[')
  44. #define STATUS        (CTL & ']')
  45. #define DEL             0x7f
  46.  
  47. /* extended characters                                                      */
  48. #define F1              256 + 59
  49. #define F2              256 + 60
  50. #define F3              256 + 61
  51. #define F4              256 + 62
  52. #define F5              256 + 63
  53. #define F6              256 + 64
  54. #define F7              256 + 65
  55. #define F8              256 + 66
  56. #define F9              256 + 67
  57. #define F10             256 + 68
  58. #define HOME            256 + 71
  59. #define UP              256 + 72
  60. #define LEFT            256 + 75
  61. #define RIGHT           256 + 77
  62. #define END             256 + 79
  63. #define DOWN            256 + 80
  64. #define INS             256 + 82
  65. #define KDEL            256 + 83
  66. #define CTLLEFT         256 + 115
  67. #define CTLRIGHT        256 + 116
  68.  
  69. int                     insert = FALSE;         /* insert mode switch */
  70. unsigned char           line[256];
  71.  
  72. unsigned        lineoff, lineseg;       /* offset and seg of line */
  73. unsigned char        *cur;                   /* current position in line */
  74. int            len;                    /* max length of line */
  75. unsigned        startpos, endpos, prevpos;   /* screen positions */
  76. int                     vpage,                  /* video page */
  77.                         maxcols,                /* cols on screen */
  78.                         cursor,                 /* standard cursor */
  79.                         icursor;                /* insert cursor */
  80.  
  81. extern void        setup();
  82.  
  83. #ifndef __TINY__
  84. #error Must be compiled with -mt
  85. #endif
  86.  
  87. void exit(int c)
  88. { _exit(c);}
  89.  
  90. void _setenvp(void){}        /* dummy out _setenvp */
  91.  
  92. void main(int argc, char *argv[])
  93. {
  94.  
  95.   unsigned    len, j;
  96.  
  97.   --argc;
  98.   for (j = 0; j <= 1; j++) {
  99.     if (argc) {
  100.       argc--;
  101.       if ((len = atoi(*argv++)) < 256) len = 256;
  102.       else if (len > 32767) len = 32767;
  103.       }
  104.     else len = 256;
  105.     init_history(j, len);
  106.   }
  107.   setup();
  108. }
  109.  
  110.  
  111. /* get a line with editing & history */
  112. getline()
  113. {
  114.   int                   c;
  115.   unsigned char        *p;
  116.   static unsigned       oldseg = 0;
  117.  
  118.  
  119.   /* the first time we're called it's from COMMAND.COM.  Save the segment */
  120.   /* so we can recognize calls from COMMAND.COM.  */
  121.   if (oldseg == 0) {
  122.     oldseg = lineseg;
  123.     }
  124.  
  125.   select_history( (lineseg == oldseg) ? 0 : 1);
  126.  
  127.   /* set up for input */
  128.   len = peekb(lineseg, lineoff) & 0xff;
  129.   memset(cur = line, 0, len);
  130.   insert = FALSE;
  131.   init_pos();
  132.   getvpage();
  133.   reset_history();
  134.  
  135.   /* main editing loop */
  136.   for (;;)
  137.   {
  138.     c = getcon();
  139.  
  140.     switch (c)
  141.     {
  142.       case CR:      /* done with line */
  143.                     setcursor(cursor);
  144.                     putch('\r');
  145.                     setpos(endpos);
  146.                     if (line[0] != '\0')
  147.                       puthist();
  148.                     storeline();
  149.                     return;
  150.  
  151.       case STATUS:  /* type out some info */
  152.                     cur = line;
  153.                     init_pos();
  154.                     showln();
  155.                     break;
  156.  
  157.       case LF:      /* expand filenames */
  158.                     findfiles();
  159.                     break;
  160.  
  161.       case CTLB:
  162.       case LEFT:    /* back one character */
  163.                     if (cur != line)
  164.                       backup();
  165.                     break;
  166.  
  167.       case CTLLEFT: /* back one word */
  168.                     backwd();
  169.                     break;
  170.  
  171.       case CTLRIGHT:
  172.                     /* forward one word */
  173.                     while (isalnum(*cur))
  174.                       forward();
  175.                     while (*cur != 0 && !isalnum(*cur))
  176.                       forward();
  177.                     break;
  178.  
  179.       case DEL:
  180.       case BS:      /* backspace and delete */
  181.                     if (cur == line)
  182.                       break;
  183.                     backup();           /* NOTE fall through */
  184.  
  185.       case CTLD:
  186.       case KDEL:    /* delete current char */
  187.                     if (*cur != '\0')
  188.                       delchr();
  189.                     break;
  190.  
  191.       case CTLW:
  192.       case F9:      /* delete word left */
  193.                     backwd();
  194.                     /* NOTE fall through */
  195.       case CTLT:
  196.       case F10:     /* delete word right */
  197.                     while (isalnum(*cur))
  198.                       delchr();
  199.                     while (*cur != 0 && !isalnum(*cur))
  200.                       delchr();
  201.                     break;
  202.  
  203.                     /* get pattern if at beginning else forward */
  204.       case F1:      if (cur == line && line[0] == '\0')
  205.                     {
  206.                       getpat();
  207.                       break;
  208.                     }
  209.       case CTLF:
  210.       case RIGHT:   /* forward one character */
  211.                     if (*cur != 0)
  212.                       forward();
  213.                     break;
  214.  
  215.       case INS:     /* insert char */
  216.                     insert = !insert;
  217.                     setcursor(insert? icursor : cursor);
  218.                     break;
  219.  
  220.       case CTLU:
  221.       case CTLX:
  222.       case ESC:     /* delete line */
  223.                     delln();
  224.                     reset_history();
  225.                     break;
  226.  
  227.       case CTLY:
  228.       case F8:      /* delete to end of line */
  229.                     while (*cur != 0)
  230.                       delchr();
  231.                     break;
  232.  
  233.       case CTLA:
  234.       case HOME:    home();
  235.                     break;
  236.  
  237.       case F3:      getpat();
  238.       case CTLE:
  239.       case END:     while (*cur != 0)
  240.                       forward();
  241.                     break;
  242.  
  243.       case UP:        prevhist();
  244.             break;
  245.  
  246.       case DOWN:    nexthist();
  247.                     break;
  248.  
  249.       case CTLL:
  250.       case F7:      search();
  251.                     break;
  252.  
  253.       case NUL:     /* ignore nulls */
  254.                     break;
  255.  
  256.       case F2:      getpat();
  257.                     c = getcon();
  258.                     if (*cur == '\0')
  259.                       break;
  260.                     for (p = cur + 1; *p != '\0' && *p != c; ++p)
  261.                       ;
  262.                     if (*p != '\0')
  263.                       while (cur < p)
  264.                         forward();
  265.                     break;
  266.  
  267.       case F4:      getpat();
  268.                     c = getcon();
  269.                     if (*cur == '\0')
  270.                       break;
  271.                     for (p = cur + 1; *p != '\0'&& *p != c; ++p)
  272.                       ;
  273.                     if (*p != '\0')
  274.                       while (*cur != c)
  275.                         delchr();
  276.                     break;
  277.  
  278.       case F5:      storeline();
  279.                     break;
  280.  
  281.       case F6:
  282.       case CTLQ:    c = getcon();
  283.                     if (c >= 256) break;
  284.                     /* NOTE fall through */
  285.       default:      if (c > 255)
  286.                       continue;
  287.                     if (cur - line == len - 1)
  288.                       bell();
  289.                     else
  290.                     {
  291.                       if (insert)
  292.                         inschr();
  293.                       *cur = c;
  294.                       forward();
  295.                       showln();
  296. #ifdef SEARCH
  297.               search();
  298. #endif
  299.                     }
  300.     }
  301.   }
  302. }
  303.  
  304. /* get extended character from console */
  305. getcon()
  306. {
  307.   int                   c;
  308.  
  309.   _AH = 8;
  310.   geninterrupt(0x21);
  311.   c = _AL;
  312.   if (c == '\0' && kbhit()) {
  313.     _AH = 8;
  314.     geninterrupt(0x21);
  315.     c = _AL;
  316.     c += 256;
  317.   }
  318.   return c;
  319. }
  320.  
  321. /* get pattern from line buffer */
  322. getpat()
  323. {
  324.   int                   i, n;
  325.  
  326.   if (cur == line && line[0] == '\0')
  327.   {
  328.     for (i = 0, n = peekb(lineseg, lineoff + 1) & 0xff; i < n; ++i)
  329.       line[i] = peekb(lineseg, lineoff + 2 + i);
  330.     line[i] = '\0';
  331.     home();
  332.     showln();
  333.   }
  334. }
  335.  
  336. /* store the line */
  337. storeline()
  338. {
  339.   register unsigned char
  340.                         *p;
  341.   register int          i;
  342.  
  343.   for (i = 0, p = line; *p != '\0'; ++i, ++p)
  344.     pokeb(lineseg, lineoff + 2 + i, *p);
  345.   pokeb(lineseg, lineoff + 2 + i, CR);
  346.   pokeb(lineseg, lineoff + 1, i);
  347. }
  348.  
  349. /* back one word */
  350. backwd()
  351. {
  352.   if (cur == line)
  353.     return;
  354.   backup();
  355.   while (cur != line && !isalnum(*cur))
  356.     backup();
  357.   while (cur != line && isalnum(*(cur - 1)))
  358.     backup();
  359. }
  360.  
  361. /* delete character */
  362. delchr()
  363. {
  364.   unsigned char         *p;
  365.   unsigned              pos;
  366.  
  367.   pos = getpos();
  368.  
  369.   for (p = cur; (*p = *(p + 1)) != '\0'; ++p)
  370.     pctl(*p);
  371.  
  372.   pctl(' ');
  373.   pctl(' ');
  374.   setpos(pos);
  375. }
  376.  
  377. /* insert a character in line */
  378. inschr(void)
  379. {
  380.   unsigned char         *p;
  381.  
  382.   for (p = cur; *p != '\0'; ++p)
  383.     ;
  384.   if (p > line + len - 2)
  385.     p = line + len - 2;
  386.  
  387.   *(p + 1) = '\0';
  388.  
  389.   while (p > cur)
  390.   {
  391.     *p = *(p - 1);
  392.     --p;
  393.   }
  394.   *cur = ' ';
  395. }
  396.  
  397. /* delete entire line */
  398. delln(void)
  399. {
  400.   home();
  401.   while (*cur != '\0')
  402.   {
  403.     if (*cur < ' ' || *cur == DEL)
  404.       putch(' ');
  405.     putch(' ');
  406.     ++cur;
  407.   }
  408.   home();
  409.   init_pos();
  410.   memset(cur = line, 0, len);
  411. }
  412.  
  413. /* move cursor to start of line */
  414. home(void)
  415. {
  416.   cur = line;
  417.   setpos(startpos);
  418. }
  419.  
  420. /* show line from current position */
  421. showln(void)
  422. {
  423.   unsigned char         *p;
  424.  
  425.   if (*cur != 0)
  426.   {
  427.     p = cur;
  428.     while (*cur != '\0')
  429.       forward();
  430.     home();
  431.     while (cur < p)
  432.       forward();
  433.   }
  434. }
  435.  
  436. /* back up one character */
  437. backup(void)
  438. {
  439.   unsigned              pos;
  440.   int                   row, col;
  441.  
  442.   --cur;
  443.   pos = getpos();
  444.   row = (pos >> 8) & 0xff;
  445.   col = pos & 0xff;
  446.   col -= (*cur < ' ' || *cur == DEL) ? 2 : 1;
  447.   if (col < 0)
  448.   {
  449.     --row;
  450.     col = 79;
  451.   }
  452.   setpos((row << 8) + col);
  453. }
  454.  
  455. /* forward one character */
  456. forward(void)
  457. {
  458.   pctl(*(cur++));
  459. }
  460.  
  461. /* insert a printable character */
  462. insprintable(char c)
  463. {
  464.     if (cur - line == len - 1) return;
  465.     inschr();
  466.     *cur = c;
  467.     forward();
  468. }
  469.  
  470.  
  471. findfiles(void)
  472. {
  473.     struct ffblk ourFF;
  474.     char far *oldDta;
  475.     char *oldCur, *scur;
  476.     char findname[64];
  477.     char *fn1, *fn2;
  478.     int foundChars;
  479.     int j;
  480.  
  481.         if (cur == line)
  482.           return;
  483.  
  484.     /* get the word to the left of the cursor into findname */
  485.     oldCur = cur;
  486.     for (;;) {
  487.         if (cur == line) break;
  488.         backup();
  489.         if (isspace(*cur)) {
  490.             forward();
  491.             break;
  492.         }
  493.     }
  494.  
  495.     scur = cur;
  496.     fn1 = fn2 = findname;
  497.     foundChars = 0;
  498.     while (scur != oldCur) {
  499.         switch(*fn1++ = *scur++) {
  500.             case '.':  /* remember if it has extension */
  501.                 foundChars |= 1;
  502.                 break;
  503.             case '*':  /* remember ambiguous names */
  504.             case '?':
  505.                 foundChars |= 2;
  506.                 break;
  507.             case '/':  /* remember where the real fn starts */
  508.             case '\\':  /* also don't remember dots in paths */
  509.             case ':':
  510.                 fn2 = fn1;
  511.                 foundChars &= ~1;
  512.                 break;
  513.             }
  514.         }
  515.     *fn1 = 0;  /* null terminate it */
  516.  
  517.     /* if no wildcards, tack on a star, or a star dot star */
  518.     if (foundChars == 1) strcat(findname, "*");
  519.     else if (foundChars == 0) strcat(findname, "*.*");
  520.  
  521.     oldDta = getdta();
  522.     if (foundChars & 2) {
  523.         j = oldCur - cur;        /* remove the word. */
  524.         while (j--) delchr();
  525.  
  526.         if (!findfirst(findname, &ourFF, FA_DIREC)) {
  527.             do {
  528.                 copyFn(findname, fn2, &ourFF.ff_name);
  529.                 insprintable(' ');
  530.             } while (!findnext(&ourFF));
  531.         }
  532.     } else {
  533.         if (expandFilename(findname, fn2)) {    /* only if a filename was found. */
  534.  
  535.             j = oldCur - cur;        /* remove the word. */
  536.             while (j--) delchr();
  537.  
  538.             copyFn(findname, fn2, fn2);        /* our filename starts here */
  539.         }
  540.         showln();
  541.         setdta(oldDta);
  542.     }
  543. }
  544.  
  545.  
  546. copyFn(char *findname, char *fn2, char *fn)
  547. {
  548.     char *fn1;
  549.  
  550.     while (findname != fn2) insprintable(*findname++);
  551.  
  552.     while (*fn != '\0') insprintable(tolower(*fn++));
  553. }
  554.  
  555.  
  556. int expandFilename(char *findname, char *fn2)
  557. {
  558.     struct ffblk ourFF;
  559.     char *fn0, *fn1;
  560.  
  561.     if (findfirst(findname, &ourFF, FA_DIREC)) {  /* no files match */
  562.         bell();
  563.         return (FALSE);
  564.     } else {  /* at least one file */
  565.         strcpy(fn2, &ourFF.ff_name);
  566.         if (ourFF.ff_attrib & FA_DIREC) strcat(fn2, "\\");
  567.         if (findnext(&ourFF)) return (TRUE);  /* one file matches */
  568.         else {
  569.             do {
  570.                 fn0 = fn2;
  571.                 fn1 = ourFF.ff_name;
  572.                 while (*fn0++ != '\0') {
  573.                     if (fn0[-1] != *fn1++) break;
  574.                     }
  575.                 fn0[-1] = 0;
  576.             } while (!findnext(&ourFF));
  577.             bell();
  578.             return (TRUE);
  579.         }
  580.     }
  581. }
  582.  
  583.  
  584. /* put character to console, converting controls to printables */
  585. pctl(int c)
  586. {
  587.   if (c == DEL)
  588.   {
  589.     pc('^');
  590.     pc('?');
  591.     return;
  592.   }
  593.  
  594.   if (c < ' ')
  595.   {
  596.     pc('^');
  597.     pc(c + '@');
  598.     return;
  599.   }
  600.  
  601.   pc(c);
  602. }
  603.  
  604. /* initialize positions */
  605. init_pos(void)
  606. {
  607.   prevpos = endpos = (startpos = getpos()) & 0xff00;
  608. }
  609.  
  610. /* put character to console */
  611. pc(int c)
  612. {
  613.   unsigned              pos;
  614.  
  615.   putch(c);
  616.   pos = getpos();
  617.   if (pos == endpos && prevpos > endpos)
  618.     startpos -= 0x0100;
  619.   else
  620.   if ((pos & 0x00ff) == 0 & pos > endpos)
  621.     endpos = pos;
  622.   prevpos = pos;
  623. }
  624.  
  625. /* audible alarm */
  626. bell(void)
  627. {
  628.   putch(BEL);
  629. }
  630.  
  631. /* put a character to the screen */
  632. putch(char ch)
  633. {
  634.     _DL = ch;
  635.     _AH = 2;
  636.     geninterrupt(0x21);
  637. }
  638.  
  639. /* get various screen & cursor characteristics */
  640. getvpage(void)
  641. {
  642.   struct REGS           r;
  643.  
  644.   r.x.ax = 0x0f00;
  645.   int86(0x10, &r, &r);
  646.   vpage = r.x.bx;
  647.   maxcols = r.x.ax >> 8;
  648.  
  649.   r.x.ax = 0x0300;
  650.   int86(0x10, &r, &r);
  651.   cursor = r.x.cx;
  652.   icursor = (((cursor & 0xff) - 4) << 8) + (cursor & 0xff);
  653. }
  654.  
  655. /* get current screen position */
  656. getpos(void)
  657. {
  658.   struct REGS           r;
  659.  
  660.   r.x.ax = 0x0300;
  661.   r.x.bx = vpage;
  662.   int86(0x10, &r, &r);
  663.   return r.x.dx;
  664. }
  665.  
  666. /* set screen position */
  667. setpos(p)
  668.   unsigned              p;
  669. {
  670.   struct REGS           r;
  671.  
  672.   r.x.ax = 0x0200;
  673.   r.x.bx = vpage;
  674.   r.x.dx = p;
  675.   int86(0x10, &r, &r);
  676.   prevpos = p;
  677. }
  678.  
  679. /* set cursor type */
  680. setcursor(unsigned c)
  681. {
  682.   struct REGS           r;
  683.  
  684.   r.x.ax = 0x0100;
  685.   r.x.cx = c;
  686.   int86(0x10, &r, &r);
  687. }
  688.