home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / OS2HIST.ZIP / HISTEDIT.C < prev    next >
C/C++ Source or Header  |  1988-02-19  |  15KB  |  574 lines

  1. /* +--------------------------------------------------------------------+
  2.    |  HISTORY - Command line editor for OS/2                |
  3.    |  Main module                File: EDIT.C        |
  4.    +--------------------------------------------------------------------+ */
  5.  
  6. #include "keydefs.h"
  7.  
  8. /* +--------------------------------------------------------------------+
  9.    |  Constants                                |
  10.    +--------------------------------------------------------------------+ */
  11.  
  12. #define TRUE 1
  13. #define FALSE 0
  14. #define NULL 0L
  15. #define BACKSPACE 8
  16. #define CTRLU 21
  17. #define CTRLB 2
  18. #define CTRLF 6
  19. #define MAXHIST 32
  20. #define MAXMACRO 32
  21. #define MACROMEM 1024
  22. #define CMDLENGTH 256
  23. #define BELLKEY 7
  24. #define TABKEY 9
  25. #define CTRLA 1
  26. #define CTRLD 4
  27. #define CTRLE 5
  28. #define CTRLK 11
  29.  
  30. #define CURSBOTTOM 0x0C
  31. #define CURSTOP1 0x0B
  32. #define CURSTOP2 3
  33. #define RAWMODE 4
  34.  
  35. #define max(a,b) (((a) > (b)) ? (a) : (b))
  36. #define min(a,b) (((a) < (b)) ? (a) : (b))
  37.  
  38. /* +--------------------------------------------------------------------+
  39.    |  Types                                |
  40.    +--------------------------------------------------------------------+ */
  41.  
  42. typedef struct CursorType {
  43.     unsigned int startline;
  44.     unsigned int endline;
  45.     unsigned int width;
  46.     unsigned int attribute;
  47.     } CURSORTYPE;
  48.  
  49. typedef struct KeyChar {
  50.     unsigned char asciicode;
  51.     unsigned char scancode;
  52.     unsigned char nlsstatus;
  53.     unsigned char nlsshifts;
  54.     unsigned int shiftstate;
  55.     unsigned long timestamp;
  56.     } KEYCHAR;
  57.  
  58. typedef struct BufLen {
  59.     unsigned int inlength;
  60.     unsigned int outlength;
  61.     } BUFLEN;
  62.  
  63. typedef struct HistRec {
  64.     char buffer[CMDLENGTH+1];
  65.     int buflen;
  66.     } HISTREC;
  67.  
  68. typedef struct HistMacro {
  69.     char *name;
  70.     char *expansion;
  71.     unsigned int flags;
  72.     } HISTMACRO;
  73.  
  74. typedef struct VideoMode {
  75.     unsigned int length;
  76.     unsigned char type;
  77.     unsigned char color;
  78.     unsigned int scrwidth;
  79.     unsigned int scrheight;
  80.     unsigned int horizres;
  81.     unsigned int vertres;
  82.     } VIDEOMODE;
  83.  
  84. typedef struct KbdStatus {
  85.     unsigned int length;
  86.     unsigned int mask;
  87.     unsigned int turnaroundchar;
  88.     unsigned int interimflags;
  89.     unsigned int shiftstate;
  90.     } KBDSTATUS;
  91.  
  92. /* +--------------------------------------------------------------------+
  93.    |  Externals                                |
  94.    +--------------------------------------------------------------------+ */
  95.  
  96. extern far pascal KBDCHARIN(struct KeyChar far *,int,int);
  97. extern far pascal VIOGETCURTYPE(CURSORTYPE far *, int);
  98. extern far pascal VIOSETCURTYPE(CURSORTYPE far *, int);
  99. extern far pascal VIOWRTTTY(char far *,int,int);
  100. extern far pascal VIOSETCURPOS(int,int,int);
  101. extern far pascal VIOGETCURPOS(int far *,int far *,int);
  102. extern far pascal DOSBEEP(int, int);
  103. extern far pascal VIOGETMODE(VIDEOMODE far *,int);
  104. extern far pascal KBDGETSTATUS(KBDSTATUS far *, int);
  105. extern far pascal DOSSETSIGHANDLER(long,long far *,int far *,int,int);
  106.  
  107. /* +--------------------------------------------------------------------+
  108.    |  Globals                                |
  109.    +--------------------------------------------------------------------+ */
  110.  
  111. HISTMACRO macros[MAXMACRO] = {0};        /* history macros */
  112. int maxmacro = 0;                /* current macro # */
  113. char macromem[MACROMEM] = {0};            /* memory for macros */
  114. char *freemem = macromem;            /* free memory pointer */
  115. HISTREC history[MAXHIST] = {0};            /* history records */
  116. char cmdline[CMDLENGTH+1];            /* input command line */
  117. int curhist = 0;                /* current position */
  118. int redisplay = FALSE;                /* if true, redisplay text */
  119. int dirty = FALSE;                /* if true, cmdline is dirty */
  120. int cursx,cursy;                /* current position */
  121. int insmode = FALSE;                /* TRUE if in insert mode */
  122. CURSORTYPE curstype;                /* cursor data */
  123. VIDEOMODE videomode;                /* current video mode */
  124. KBDSTATUS kbdstatus;                /* keyboard status */
  125. int twolines = 0;                /* true if two lines */
  126. int cursor;                    /* cursor for edit line */
  127. int maxlen;                    /* max length of edit line */
  128. int curlen;                    /* cur length of edit line */
  129.  
  130.  
  131. /* +--------------------------------------------------------------------+
  132.    |  strlen(x)                                |
  133.    |  returns length of string                        |
  134.    +--------------------------------------------------------------------+ */
  135. static int strlen(x)
  136. char *x;
  137. {
  138.     int len = 0;
  139.  
  140.     while (*x++) len++;
  141.     return len;
  142. }
  143.  
  144. /* +--------------------------------------------------------------------+
  145.    |  allocmem(nbytes)                            |
  146.    |  allocates memory or returns NULL if no memory available        |
  147.    +--------------------------------------------------------------------+ */
  148. static char *allocmem(nbytes)
  149. int nbytes;
  150. {
  151.     char *x;
  152.  
  153.     if ((freemem+nbytes) >= (¯omem[MACROMEM-1])) return NULL;
  154.     memset(freemem,0,nbytes);
  155.     x = freemem;
  156.     freemem += nbytes;
  157.     return x;
  158. }
  159.  
  160. /* +--------------------------------------------------------------------+
  161.    |  addmacro(name,expansion) -> 0 (success) or 1 (fail)        |
  162.    |  adds macro to list                        |
  163.    +--------------------------------------------------------------------+ */
  164. int far pascal addmacro(name,expansion)
  165. char *name;
  166. char *expansion;
  167. {
  168.     char *lname;
  169.     char *lexpand;
  170.     int namelen;
  171.     int explen;
  172.  
  173.     if (maxmacro >= MAXMACRO) return 1;
  174.  
  175.     namelen = strlen(name)+1;
  176.     explen = strlen(expansion)+1;
  177.     lname = allocmem(namelen);
  178.     lexpand = allocmem(explen);
  179.     if (!lname || !lexpand) return 1;
  180.  
  181.     memcpy(lname,name,namelen,0);
  182.     memcpy(lexpand,expansion,explen,0);
  183.     macros[maxmacro].name = lname;
  184.     macros[maxmacro].expansion = lexpand;
  185.     maxmacro++;
  186.     return 0;
  187.  
  188. }
  189.  
  190.  
  191. /* +--------------------------------------------------------------------+
  192.    |  memset(buf,c,cnt)                            |
  193.    |  clears buffer                            |
  194.    +--------------------------------------------------------------------+ */
  195. static memset(buf,c,cnt)
  196. char *buf;
  197. char c;
  198. int cnt;
  199. {
  200.     register int i;
  201.     register char *bp;
  202.  
  203.     i = cnt;
  204.     bp = buf;
  205.  
  206.     while (i) {*bp++ = c; i--;}
  207. }
  208.  
  209. /* +--------------------------------------------------------------------+
  210.    |  memcpy(dest,src,cnt,dir)                        |
  211.    |  Copies data from src to dest                     |
  212.    +--------------------------------------------------------------------+ */
  213. static memcpy(dest,src,cnt,dir)
  214. char *dest;
  215. char *src;
  216. int cnt;
  217. int dir;
  218. {
  219.     register char *in,*out;
  220.     register int i;
  221.  
  222.     i = cnt;
  223.     if (dir < 0) {
  224.         in = src + cnt - 1;
  225.         out = dest + cnt - 1;
  226.         while (i) {*out-- = *in--; i--;}
  227.         }
  228.     else {
  229.         in = src;
  230.         out = dest;
  231.         while (i) {*out++ = *in++; i--;}
  232.         }
  233. }
  234.  
  235. /* +--------------------------------------------------------------------+
  236.    |  showbuf(buf,buflength,cursor)                    |
  237.    |  Writes buffer out, expanding tabs and other special characters    |
  238.    +--------------------------------------------------------------------+ */
  239. showbuf(buf,buflength,cursor)
  240. char *buf;
  241. int buflength;
  242. int cursor;
  243. {
  244.     int xp,yp;
  245.     int newxp,newyp;
  246.     int i;
  247.     int oldatt;
  248.  
  249.     oldatt = curstype.attribute;
  250.     curstype.attribute = -1;
  251.     VIOSETCURTYPE((CURSORTYPE far *) &curstype,0);
  252.  
  253.     if (twolines && (videomode.scrheight-1 != cursy)) {
  254.         VIOSETCURPOS(cursy+1,0,0);
  255.         VIOWRTTTY("\033[K",3,0);
  256.         }
  257.  
  258.     VIOSETCURPOS(cursy,cursx,0);
  259.     VIOWRTTTY(buf,buflength,0);
  260.     VIOWRTTTY((char far *) "\033[K",3,0);    /* clear to EOL */
  261.     curspos(buf,buflength,curlen,cursor,&xp,&yp);
  262.     VIOGETCURPOS(&newyp,&newxp,0);        /* get new cursor pos */
  263.     curstype.attribute = oldatt;
  264.     VIOSETCURTYPE((CURSORTYPE far *) &curstype,0);
  265.     VIOSETCURPOS(yp,xp,0);
  266.  
  267. }
  268.  
  269.  
  270. /* +--------------------------------------------------------------------+
  271.    |  showcurs(buf,buflength,cursor,changeflg)                |
  272.    |  shows cursor at correct posn on screen                |
  273.    +--------------------------------------------------------------------+ */
  274. showcurs(buf,buflength,cursor)
  275. char *buf;
  276. int buflength;
  277. int cursor;
  278. {
  279.     int xp,yp;
  280.  
  281.     curspos(buf,buflength,curlen,cursor,&xp,&yp);
  282.  
  283.     VIOSETCURPOS(yp,xp,0);
  284. }
  285.  
  286. /* +--------------------------------------------------------------------+
  287.    |  curspos(buf,buflen,curlen,cursor,&xp,&yp)                |
  288.    |  returns the cursor's position if put on the character specified    |
  289.    +--------------------------------------------------------------------+ */
  290. curspos(buf,buflen,curlen,cursor,xcursp,ycursp)
  291. char *buf;
  292. int buflen;
  293. int curlen;
  294. int cursor;
  295. int *xcursp,*ycursp;
  296. {
  297.     char *cp;
  298.     int outxp,outyp;
  299.     int xp,yp;
  300.     int xtra = 0;
  301.  
  302.     xp = cursx;
  303.     yp = cursy;
  304.     cp = buf;
  305.  
  306.     outxp = xp;
  307.     outyp = yp;
  308.  
  309.     while (curlen) {
  310.         switch (*cp) {
  311.             case BELLKEY: break;
  312.             case TABKEY: if ((xp % 8) == 0) xp++;
  313.                 while (xp % 8) {
  314.                     xp++;
  315.                     }
  316.                 break;
  317.             default: xp++;
  318.             }
  319.  
  320.         if (xp >= videomode.scrwidth) {
  321.             xp = 0; yp++; xtra++;
  322.             if (cursor) outyp++;
  323.             if (yp >= videomode.scrheight) {
  324.                 yp = videomode.scrheight-1;
  325.                 cursy--;
  326.                 outyp--;
  327.                 }
  328.             }
  329.  
  330.         cp++;
  331.         if ((cp - buf) > buflen) break;
  332.         curlen--;
  333.         if (cursor) {
  334.             cursor--;
  335.             outxp = xp;
  336.             }
  337.         }
  338.     *xcursp = outxp;
  339.     *ycursp = outyp;
  340.     if (xtra) twolines = TRUE;
  341. }
  342.             
  343.  
  344. /* +--------------------------------------------------------------------+
  345.    |  findprev(cur)                            |
  346.    |  Locates previous good entry in the history list.            |
  347.    +--------------------------------------------------------------------+ */
  348. static int findprev(cur,orig)
  349. int cur;
  350. int orig;
  351. {
  352.     int pos;
  353.  
  354.     pos = ((cur + MAXHIST) - 1) % MAXHIST;
  355.     while (pos != cur) {
  356.         if ((pos == orig) || history[pos].buflen) return pos;
  357.         pos = ((pos + MAXHIST) - 1) % MAXHIST;
  358.         }
  359.     return -1;
  360. }
  361.  
  362. /* +--------------------------------------------------------------------+
  363.    |  findnext(cur)                            |
  364.    |  Locates previous good entry in the history list.            |
  365.    +--------------------------------------------------------------------+ */
  366. static int findnext(cur,orig)
  367. int cur;
  368. int orig;
  369. {
  370.     int pos;
  371.  
  372.     pos = (cur + 1) % MAXHIST;
  373.     while (pos != cur) {
  374.         if ((pos == orig) || history[pos].buflen) return pos;
  375.         pos = (pos + 1) % MAXHIST;
  376.         }
  377.     return -1;
  378. }
  379.  
  380.  
  381. /* +--------------------------------------------------------------------+
  382.    |  setedit(hist)                            |
  383.    |  Sets up edit line so we are editing history # "hist"        |
  384.    +--------------------------------------------------------------------+ */
  385. setedit(hist)
  386. int hist;
  387. {
  388.     curlen = history[hist].buflen;
  389.     curlen = min(curlen,maxlen);
  390.     curlen = min(CMDLENGTH,curlen);
  391.     memcpy(cmdline,history[hist].buffer,curlen,1);
  392.     redisplay = TRUE;
  393.     cursor = curlen;
  394. }
  395.  
  396.  
  397. /* +--------------------------------------------------------------------+
  398.    |  editstringin(buf,buflen,iowait,kbhandle)                |
  399.    |  will be the replacement for kbdstringin someday            |
  400.    +--------------------------------------------------------------------+ */
  401. int pascal editstringin(resbuf,buflen,iowait,kbdhandle)
  402. char far *resbuf;
  403. BUFLEN far *buflen;
  404. unsigned int iowait;
  405. unsigned int kbdhandle;
  406. {
  407.     int kchr;            /* char read from keyboard */
  408.     int res;
  409.     int i;
  410.     KEYCHAR kc;
  411.     char *buf;
  412.     int editline;            /* history # we're editing now */
  413.  
  414.     /* if we're in cooked mode or we don't want to wait for characters,
  415.           let the base keyboard subsystem do the work for us */
  416.     KBDGETSTATUS( (KBDSTATUS far *) &kbdstatus, 0);
  417.     kbdstatus.length = sizeof(KBDSTATUS);
  418.     kbdstatus.mask = 0;
  419.     if (iowait || (kbdstatus.mask & RAWMODE)) return -1;
  420.     if (buflen->inlength >= CMDLENGTH) return -1; /* request too big */
  421.  
  422.     curlen = 0;
  423.     cursor = 0;
  424.     redisplay = FALSE;
  425.     memset(cmdline,0,CMDLENGTH);
  426.     maxlen = min(CMDLENGTH,buflen->inlength);
  427.     twolines = FALSE;
  428.  
  429.     VIOGETCURPOS( (int far *) &cursy, (int far *) &cursx, 0);
  430.     VIOGETCURTYPE( (CURSORTYPE far *) &curstype, 0);
  431.     curstype.endline = CURSBOTTOM;
  432.     curstype.startline = CURSTOP1;
  433.     videomode.length = sizeof(VIDEOMODE);
  434.     VIOGETMODE((VIDEOMODE far *) &videomode,0);
  435.  
  436.     insmode = FALSE;
  437.     editline = curhist;
  438.     history[editline].buflen = 0;
  439.     memset(history[editline].buffer,0,CMDLENGTH);    /* clear out space */
  440.  
  441.     VIOSETCURTYPE(&curstype,0);            /* fix cursor type */
  442.  
  443.     for (;;) {
  444.         res = KBDCHARIN(&kc,iowait,kbdhandle);
  445.         if (res) {
  446.             return res;
  447.             }
  448.         if (kc.asciicode) kchr = kc.asciicode;
  449.         else kchr = 0x100 + kc.scancode;
  450.         switch (kchr) {
  451.             case BACKSPACE:
  452.                 if (cursor == curlen) {
  453.                     if (curlen > 0) {
  454.                         curlen--;
  455.                         cursor--;
  456.                         }
  457.                     }
  458.                 else {
  459.                     if (cursor > 0) {
  460.                         memcpy(&cmdline[cursor-1],
  461.                             &cmdline[cursor],
  462.                             curlen-cursor,1);
  463.                         cursor--;
  464.                         curlen--;
  465.                         }
  466.                     }
  467.                 redisplay = TRUE;    /* more than cursor */
  468.                 dirty = TRUE;
  469.                 break;
  470.             case CTRLA:
  471.             case HOMEKEY:
  472.                 cursor = 0;
  473.                 break;
  474.             case CTRLE:
  475.             case ENDKEY:
  476.                 cursor = curlen;
  477.                 break;
  478.             case CTRLK:
  479.                 curlen = cursor;
  480.                 dirty = TRUE;
  481.                 redisplay = TRUE;
  482.                 break;
  483.             case ENTERKEY:
  484.                 memcpy(resbuf,cmdline,curlen,1);
  485.                 *(resbuf+curlen) = '\r';
  486.                 buflen->outlength = curlen;
  487.                 if (dirty && curlen) {
  488.                     memcpy(history[curhist].buffer,cmdline,curlen,1);
  489.                     history[curhist].buflen = curlen;
  490.                     curhist = curhist + 1;
  491.                     if (curhist >= MAXHIST) curhist = 0;
  492.                     }
  493.                 curstype.startline = CURSTOP1;
  494.                 VIOSETCURTYPE(&curstype,0);
  495.                 showbuf(cmdline,curlen,curlen);
  496.                 VIOWRTTTY((char far *) "\r",1,0);
  497.                 return 0;
  498.             case UPKEY:
  499.             case DOWNKEY:
  500.                 if (kchr == UPKEY) res = findprev(editline,curhist);
  501.                 else res = findnext(editline,curhist);
  502.                 if (res == -1) break;        /* none there */
  503.                 setedit(res);
  504.                 editline = res;
  505.                 dirty = FALSE;
  506.                 break;
  507.             case LEFTKEY:
  508.             case CTRLB:    
  509.                 if (cursor > 0) cursor--;
  510.                 break;
  511.             case RIGHTKEY:
  512.             case CTRLF:
  513.                 if (cursor < curlen) cursor++;
  514.                 break;
  515.             case CTRLU:
  516.             case ESCKEY:
  517.                 curlen = 0;
  518.                 cursor = 0;
  519.                 VIOSETCURPOS(cursy,cursx,0);
  520.                 VIOWRTTTY((char far *) "\033[K",3,0);
  521.                 if (twolines && (videomode.scrheight-1 != cursy)) {
  522.                     VIOSETCURPOS(cursy+1,0,0);
  523.                     VIOWRTTTY((char far *) "\033[K",5,0);
  524.                     }
  525.                 VIOSETCURPOS(cursy,cursx,0);
  526.                 break;
  527.             case INSKEY:
  528.                 insmode = !insmode;
  529.                 curstype.startline = insmode ? CURSTOP2 : CURSTOP1;
  530.                 VIOSETCURTYPE(&curstype,0);
  531.                 break;
  532.             case CTRLD:
  533.             case DELKEY:
  534.                 if (cursor == curlen) break;
  535.                 if (curlen == 0) break;
  536.                 memcpy(&cmdline[cursor],&cmdline[cursor+1],
  537.                     curlen-cursor,1);
  538.                 curlen--;
  539.                 redisplay = TRUE;
  540.                 dirty = TRUE;
  541.                 break;
  542.             default:
  543.                 if (kchr & 0x100) break;
  544.                 if ((kchr < 32) && 
  545.                     (kchr != 26) && (kchr != TABKEY)) break;
  546.                 if ((insmode || (cursor == curlen)) 
  547.                      && (curlen >= maxlen)) {
  548.                     DOSBEEP(1000,100);
  549.                     break;
  550.                     }
  551.                 dirty = TRUE;
  552.                 if (!insmode) {
  553.                     cmdline[cursor] = kc.asciicode;
  554.                     cursor++;
  555.                     curlen = max(cursor,curlen);
  556.                     }
  557.                 else {
  558.                     memcpy(&cmdline[cursor+1],
  559.                         &cmdline[cursor],curlen-cursor,
  560.                         -1);
  561.                     curlen++;
  562.                     cmdline[cursor] = kc.asciicode;
  563.                     cursor++;
  564.                     }
  565.                 redisplay = TRUE;
  566.                 break;
  567.             }
  568.             if (redisplay) showbuf(cmdline,curlen,cursor);
  569.             else showcurs(cmdline,curlen,cursor);
  570.             redisplay = FALSE;
  571.         }
  572. }
  573.  
  574.