home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / texted / b084_1 / c / input < prev    next >
Text File  |  1991-04-20  |  30KB  |  890 lines

  1. /*      Input:  Various input routines for MicroEMACS
  2.                 written by Daniel Lawrence
  3.                 5/9/86                                          */
  4.  
  5. /*
  6.         Notes:
  7.  
  8.         MicroEMACS's kernel processes two distinct forms of
  9.         characters.  One of these is a standard unsigned character
  10.         which is used in the edited text.  The other form, called
  11.         an EMACS Extended Character is a 2 byte value which contains
  12.         both an ascii value, and flags for certain prefixes/events.
  13.  
  14.         Bit     Usage
  15.         ---     -----
  16.         0 = 7   Standard 8 bit ascii character
  17.         8       Control key flag
  18.         9       META prefix flag
  19.         10      ^X prefix flag
  20.         11      Function key flag
  21.         12      Mouse prefix
  22.         13      Shifted flag (not needed on alpha shifted characters)
  23.         14      Alterate prefix (ALT key on PCs)
  24.  
  25.         The machine dependent driver is responsible for returning
  26.         a byte stream from the various input devices with various
  27.         prefixes/events embedded as escape codes.  Zero is used as the
  28.         value indicating an escape sequence is next.  The format of
  29.         an escape sequence is as follows:
  30.  
  31.         0               Escape indicator
  32.         <prefix byte>   upper byte of extended character
  33.         {<col><row>}    col, row position if the prefix byte
  34.                         indicated a mouse event
  35.         <event code>    value of event
  36.  
  37.         Two successive zeroes are used to indicate an actual
  38.         null being input.  These values are then interpreted by
  39.         getkey() to construct the proper extended character
  40.         sequences to pass to the MicroEMACS kernel.
  41. */
  42.  
  43. #include        <stdio.h>
  44. #include        "estruct.h"
  45. #include        "etype.h"
  46. #include        "edef.h"
  47. #include        "elang.h"
  48.  
  49. #if USG | BSD | V7
  50. #include        <sys/types.h>
  51. #include        <pwd.h>
  52. #ifndef HELIOS
  53. extern  struct passwd *getpwnam();
  54. #endif
  55. #if USG
  56. #define index   strchr
  57. #endif
  58. #endif
  59.  
  60.  
  61. /*
  62.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  63.  * ABORT. The ABORT status is returned if the user bumps out of the question
  64.  * with a ^G. Used any time a confirmation is required.
  65.  */
  66.  
  67. PASCAL NEAR mlyesno(prompt)
  68.  
  69. char *prompt;
  70.  
  71. {
  72.         int  c;                 /* input character */
  73.         char buf[NPAT];         /* prompt to user */
  74.  
  75.         for (;;) {
  76.                 /* build and prompt the user */
  77.                 strcpy(buf, prompt);
  78.                 strcat(buf, TEXT162);
  79. /*                          " [y/n]? " */
  80.                 mlwrite(buf);
  81.  
  82.                 /* get the response */
  83.                 c = getcmd();   /* getcmd() lets us check for anything that might */
  84.                                 /* generate a 'y' or 'Y' in case use screws up */
  85.  
  86.                 if (c == ectoc(abortc))         /* Bail out! */
  87.                         return(ABORT);
  88.  
  89.                 if  ((c == 'n') || (c == 'N')
  90.                     || (c & (SPEC|ALTD|CTRL|META|CTLX|MOUS)))
  91.                         return(FALSE);  /* ONLY 'y' or 'Y' allowed!!! */
  92.  
  93. #if     FRENCH
  94.                 if (c=='o' || c=='O')
  95.                         return(TRUE);
  96. #endif
  97.  
  98.                 if (c=='y' || c=='Y')
  99.                         return(TRUE);
  100.  
  101.                 return(FALSE);
  102.         }
  103. }
  104.  
  105. /*
  106.  * Write a prompt into the message line, then read back a response. Keep
  107.  * track of the physical position of the cursor. If we are in a keyboard
  108.  * macro throw the prompt away, and return the remembered response. This
  109.  * lets macros run at full speed. The reply is always terminated by a carriage
  110.  * return. Handle erase, kill, and abort keys.
  111.  */
  112.  
  113. PASCAL NEAR mlreply(prompt, buf, nbuf)
  114.     char *prompt;
  115.     char *buf;
  116. {
  117.         return(nextarg(prompt, buf, nbuf, ctoec((int) '\r')));
  118. }
  119.  
  120. PASCAL NEAR mltreply(prompt, buf, nbuf, eolchar)
  121.  
  122. char *prompt;
  123. char *buf;
  124. int eolchar;
  125.  
  126. {
  127.         return(nextarg(prompt, buf, nbuf, eolchar));
  128. }
  129.  
  130. /*      ectoc:  expanded character to character
  131.                 collapse the CTRL and SPEC flags back into an ascii code   */
  132.  
  133. PASCAL NEAR ectoc(c)
  134.  
  135. int c;
  136.  
  137. {
  138.         if (c & CTRL)
  139.                 c = c & ~(CTRL | 0x40);
  140.         if (c & SPEC)
  141.                 c= c & 255;
  142.         return(c);
  143. }
  144.  
  145. /*      ctoec:  character to extended character
  146.                 pull out the CTRL and SPEC prefixes (if possible)       */
  147.  
  148. PASCAL NEAR ctoec(c)
  149.  
  150. int c;
  151.  
  152. {
  153.         if (c>=0x00 && c<=0x1F)
  154.                 c = CTRL | (c+'@');
  155.         return(c);
  156. }
  157.  
  158. /* get a command name from the command line. Command completion means
  159.    that pressing a <SPACE> will attempt to complete an unfinished command
  160.    name if it is unique.
  161. */
  162.  
  163. int (PASCAL NEAR *PASCAL NEAR getname(prompt))()
  164.  
  165. char *prompt;   /* string to prompt with */
  166.  
  167. {
  168.         char *sp;       /* ptr to the returned string */
  169.  
  170.         sp = complete(prompt, NULL, CMP_COMMAND, NSTRING);
  171.         if (sp == NULL)
  172.                 return(NULL);
  173.  
  174.         return(fncmatch(sp));
  175. }
  176.  
  177. /*      getcbuf:        get a completion from the user for a buffer name.
  178.  
  179.                         I was goaded into this by lots of other people's
  180.                         completion code.
  181. */
  182.  
  183. BUFFER *PASCAL NEAR getcbuf(prompt, defval, createflag)
  184.  
  185. char *prompt;           /* prompt to user on command line */
  186. char *defval;           /* default value to display to user */
  187. int createflag;         /* should this create a new buffer? */
  188.  
  189. {
  190.         char *sp;       /* ptr to the returned string */
  191.  
  192.         sp = complete(prompt, defval, CMP_BUFFER, NBUFN);
  193.         if (sp == NULL)
  194.                 return(NULL);
  195.  
  196.         return(bfind(sp, createflag, 0));
  197. }
  198.  
  199. char *PASCAL NEAR gtfilename(prompt)
  200.  
  201. char *prompt;           /* prompt to user on command line */
  202.  
  203. {
  204.         char *sp;       /* ptr to the returned string */
  205.  
  206.         sp = complete(prompt, NULL, CMP_FILENAME, NFILEN);
  207.         if (sp == NULL)
  208.                 return(NULL);
  209.  
  210.         return(sp);
  211. }
  212.  
  213. char *PASCAL NEAR complete(prompt, defval, type, maxlen)
  214.  
  215. char *prompt;           /* prompt to user on command line */
  216. char *defval;           /* default value to display to user */
  217. int type;               /* type of what we are completing */
  218. int maxlen;             /* maximum length of input field */
  219.  
  220. {
  221.         register int c;         /* current input character */
  222.         int cpos;               /* current column on screen output */
  223.         static char buf[NSTRING];/* buffer to hold tentative name */
  224. #if USG | BSD | V7
  225.         char *home;
  226.         struct passwd *pwd;
  227. #endif
  228.  
  229. #if     COMPLET == 0
  230.         int status;
  231. #endif
  232.  
  233.         /* if we are executing a command line get the next arg and match it */
  234.         if (clexec) {
  235.                 if (macarg(buf) != TRUE)
  236.                         return(NULL);
  237.                 return(buf);
  238.         }
  239.  
  240. #if     COMPLET == 0
  241.         strcpy(buf, prompt);
  242.         if (type != CMP_COMMAND) {
  243.                 if (defval) {
  244.                         strcat(buf, "[");
  245.                         strcat(buf, defval);
  246.                         strcat(buf, "]: ");
  247.                 } else {
  248.                         strcat(buf, ": ");
  249.                 }
  250.         }
  251.         status = mlreply(buf, buf, maxlen);
  252.         if (status == ABORT)
  253.                 return(NULL);
  254.         if (defval && (buf[0] == 0))
  255.                 return(defval);
  256.         return(buf);
  257. }
  258. #else
  259.         /* starting at the beginning of the string buffer */
  260.         cpos = 0;
  261.  
  262.         /* if it exists, prompt the user for a buffer name */
  263.         if (prompt)
  264.         {       if (type == CMP_COMMAND)
  265.                         mlwrite("%s", prompt);
  266.                 else if (defval)
  267.                         mlwrite("%s[%s]: ", prompt, defval);
  268.                 else
  269.                         mlwrite("%s: ", prompt);
  270.         }
  271.         /* build a name string from the keyboard */
  272.         while (TRUE) {
  273.                 c = tgetc();
  274.  
  275.                 /* if we are at the end, just match it */
  276.                 if (c == '\n'  ||  c == '\r') {
  277.                         if (defval && cpos==0)
  278.                                 return(defval);
  279.                         else {
  280.                                 buf[cpos] = 0;
  281.                                 return(buf);
  282.                         }
  283.  
  284.                 } else if (c == ectoc(abortc)) {        /* Bell, abort */
  285.                         ctrlg(FALSE, 0);
  286.                         TTflush();
  287.                         return(NULL);
  288.  
  289.                 } else if (c == 0x7F || c == 0x08) {    /* rubout/erase */
  290.                         if (cpos != 0) {
  291.                                 mlout('\b');
  292.                                 mlout(' ');
  293.                                 mlout('\b');
  294.                                 --ttcol;
  295.                                 --cpos;
  296.                                 TTflush();
  297.                         }
  298.  
  299.                 } else if (c == 0x15) { /* C-U, kill */
  300.                         while (cpos != 0) {
  301.                                 mlout('\b');
  302.                                 mlout(' ');
  303.                                 mlout('\b');
  304.                                 --cpos;
  305.                                 --ttcol;
  306.                         }
  307.                         TTflush();
  308.  
  309. #if USG | BSD | V7
  310.                 } else if (c == '/' && type == CMP_FILENAME && buf[0] == '~') {
  311.                         int i;
  312.  
  313.                         if (cpos == 1) {
  314.                                 if (home = (char *)getenv("HOME")) {
  315.  
  316.                                         mlout('\b');    /* backup over ~ */
  317.                                         mlout(' ');
  318.                                         mlout('\b');
  319.                                         ttcol--;
  320.                                         TTflush();
  321.                                         strcpy(buf, home);
  322.                                         cpos = strlen(buf);
  323.                                         buf[cpos++] = '/';
  324.                                         for (i = 0; i < cpos; i++) {
  325.                                                 mlout(buf[i]);
  326.                                                 ttcol++;
  327.                                         }
  328.                                         TTflush();
  329.                                 } else
  330.                                         goto nextc;
  331.                         } else {
  332.                                 buf[cpos] = '\0';
  333.                                 if (pwd = getpwnam(&buf[1])) {
  334.                                         while (cpos != 0) {     /* kill */
  335.                                                 mlout('\b');    /* line */
  336.                                                 mlout(' ');
  337.                                                 mlout('\b');
  338.                                                 --cpos;
  339.                                                 --ttcol;
  340.                                         }
  341.                                         TTflush();
  342.                                         strcpy(buf, pwd->pw_dir);
  343.                                         cpos = strlen(buf);
  344.                                         buf[cpos++] = '/';
  345.                                         for (i = 0; i < cpos; i++) {
  346.                                         mlout(buf[i]);
  347.                                                 ttcol++;
  348.                                         }
  349.                                         TTflush();
  350.                                 } else
  351.                                         goto nextc;
  352.                         }
  353.                 } else if (((c == ' ') || (c == ectoc(sterm)) || (c == '\t') || (c == '/')) &&
  354.                           type == CMP_FILENAME && buf[0] == '$') {
  355.                         int i;
  356.  
  357.                         buf[cpos] = '\0';
  358.                         if (home = (char *)getenv(&buf[1])) {
  359.                                 while (cpos != 0) {     /* kill */
  360.                                         mlout('\b');    /* line */
  361.                                         mlout(' ');
  362.                                         mlout('\b');
  363.                                         --cpos;
  364.                                         --ttcol;
  365.                                 }
  366.                                 TTflush();
  367.                                 strcpy(buf, home);
  368.                                 cpos = strlen(buf);
  369.                                 if (c == '/') buf[cpos++] = '/';
  370.                                 for (i = 0; i < cpos; i++) {
  371.                                         mlout(buf[i]);
  372.                                         ttcol++;
  373.                                 }
  374.                                 TTflush();
  375.                         } else
  376.                                 goto nextc;
  377. #endif
  378.                 } else if ((c == ' ') || (c == ectoc(sterm)) || (c == '\t')) {  
  379.                         /* attempt a completion */
  380.                         switch (type) {
  381.                                 case CMP_BUFFER:
  382.                                         comp_buffer(buf, &cpos);
  383.                                         break;
  384.                                 case CMP_COMMAND:
  385.                                         comp_command(buf, &cpos);
  386.                                         break;
  387.                                 case CMP_FILENAME:
  388.                                         comp_file(buf, &cpos);
  389.                                         break;
  390.                         }
  391.  
  392.                         TTflush();
  393.                         if (buf[cpos - 1] == 0)
  394.                                 return(buf);
  395.                 } else {
  396. nextc:                  if (cpos < maxlen && c > ' ') {
  397.                                 buf[cpos++] = c;
  398.                                 mlout(c);
  399.                                 ++ttcol;
  400.                                 TTflush();
  401.                         }
  402.                 }
  403.         }
  404. }
  405.  
  406. /*      comp_command:   Attempt a completion on a command name  */
  407.  
  408. comp_command(name, cpos)
  409.  
  410. char *name;     /* command containing the current name to complete */
  411. int *cpos;      /* ptr to position of next character to insert */
  412.  
  413. {
  414.         register NBIND *bp;     /* trial command to complete */
  415.         register int index;     /* index into strings to compare */
  416.         register int curbind;   /* index into the names[] array */
  417.         register NBIND *match;  /* last command that matches string */
  418.         register int matchflag; /* did this command name match? */
  419.         register int comflag;   /* was there a completion at all? */
  420.  
  421.         /* start attempting completions, one character at a time */
  422.         comflag = FALSE;
  423.         curbind = 0;
  424.         while (*cpos < NSTRING) {
  425.  
  426.                 /* first, we start at the first command and scan the list */
  427.                 match = NULL;
  428.                 curbind = 0;
  429.                 while (curbind <= numfunc) {
  430.  
  431.                         /* is this a match? */
  432.                         bp = &names[curbind];
  433.                         matchflag = TRUE;
  434.                         for (index = 0; index < *cpos; index++)
  435.                                 if (name[index] != bp->n_name[index]) {
  436.                                         matchflag = FALSE;
  437.                                         break;
  438.                                 }
  439.  
  440.                         /* if it is a match */
  441.                         if (matchflag) {
  442.  
  443.                                 /* if this is the first match, simply record it */
  444.                                 if (match == NULL) {
  445.                                         match = bp;
  446.                                         name[*cpos] = bp->n_name[*cpos];
  447.                                 } else {
  448.                                         /* if there's a difference, stop here */
  449.                                         if (name[*cpos] != bp->n_name[*cpos])
  450.                                                 return;
  451.                                 }
  452.                         }
  453.  
  454.                         /* on to the next command */
  455.                         curbind++;
  456.                 }
  457.  
  458.                 /* with no match, we are done */
  459.                 if (match == NULL) {
  460.                         /* beep if we never matched */
  461.                         if (comflag == FALSE)
  462.                                 TTbeep();
  463.                         return;
  464.                 }
  465.  
  466.                 /* if we have completed all the way... go back */
  467.                 if (name[*cpos] == 0) {
  468.                         (*cpos)++;
  469.                         return;
  470.                 }
  471.  
  472.                 /* remember we matched, and complete one character */
  473.                 comflag = TRUE;
  474.                 TTputc(name[(*cpos)++]);
  475.                 TTflush();
  476.         }
  477.  
  478.         /* don't allow a completion past the end of the max command name length */
  479.         return;
  480. }
  481.  
  482. /*      comp_buffer:    Attempt a completion on a buffer name   */
  483.  
  484. comp_buffer(name, cpos)
  485.  
  486. char *name;     /* buffer containing the current name to complete */
  487. int *cpos;      /* ptr to position of next character to insert */
  488.  
  489. {
  490.         register BUFFER *bp;    /* trial buffer to complete */
  491.         register int index;     /* index into strings to compare */
  492.         register BUFFER *match; /* last buffer that matches string */
  493.         register int matchflag; /* did this buffer name match? */
  494.         register int comflag;   /* was there a completion at all? */
  495.  
  496.         /* start attempting completions, one character at a time */
  497.         comflag = FALSE;
  498.         while (*cpos < NBUFN) {
  499.  
  500.                 /* first, we start at the first buffer and scan the list */
  501.                 match = NULL;
  502.                 bp = bheadp;
  503.                 while (bp) {
  504.  
  505.                         /* is this a match? */
  506.                         matchflag = TRUE;
  507.                         for (index = 0; index < *cpos; index++)
  508.                                 if (name[index] != bp->b_bname[index]) {
  509.                                         matchflag = FALSE;
  510.                                         break;
  511.                                 }
  512.  
  513.                         /* if it is a match */
  514.                         if (matchflag) {
  515.  
  516.                                 /* if this is the first match, simply record it */
  517.                                 if (match == NULL) {
  518.                                         match = bp;
  519.                                         name[*cpos] = bp->b_bname[*cpos];
  520.                                 } else {
  521.                                         /* if there's a difference, stop here */
  522.                                         if (name[*cpos] != bp->b_bname[*cpos])
  523.                                                 return;
  524.                                 }
  525.                         }
  526.  
  527.                         /* on to the next buffer */
  528.                         bp = bp->b_bufp;
  529.                 }
  530.  
  531.                 /* with no match, we are done */
  532.                 if (match == NULL) {
  533.                         /* beep if we never matched */
  534.                         if (comflag == FALSE)
  535.                                 TTbeep();
  536.                         return;
  537.                 }
  538.  
  539.                 /* if we have completed all the way... go back */
  540.                 if (name[*cpos] == 0) {
  541.                         (*cpos)++;
  542.                         return;
  543.                 }
  544.  
  545.                 /* remember we matched, and complete one character */
  546.                 comflag = TRUE;
  547.                 TTputc(name[(*cpos)++]);
  548.                 TTflush();
  549.         }
  550.  
  551.         /* don't allow a completion past the end of the max buffer name length */
  552.         return;
  553. }
  554.  
  555. /*      comp_file:      Attempt a completion on a file name     */
  556.  
  557. comp_file(name, cpos)
  558.  
  559. char *name;     /* file containing the current name to complete */
  560. int *cpos;      /* ptr to position of next character to insert */
  561.  
  562. {
  563.         register char *fname;   /* trial file to complete */
  564.         register int index;     /* index into strings to compare */
  565.         register int matches;   /* number of matches for name */
  566.         char longestmatch[NSTRING]; /* temp buffer for longest match */
  567.         int longestlen;         /* length of longest match (always > *cpos) */
  568.   
  569.         /* everything (or nothing) matches an empty string */
  570.         if (*cpos == 0)
  571.                 return;
  572.  
  573.         /* first, we start at the first file and scan the list */
  574.         matches = 0;
  575.         name[*cpos] = 0;
  576.         fname = getffile(name);
  577.         while (fname) {
  578.  
  579.                 /* is this a match? */
  580.                 if (strncmp(name,fname,*cpos) == 0) {
  581.  
  582.                         /* count the number of matches */
  583.                         matches++;
  584.  
  585.                         /* if this is the first match, simply record it */
  586.                         if (matches == 1) {
  587.                                 strcpy(longestmatch,fname);
  588.                                 longestlen = strlen(longestmatch);
  589.                         } else {
  590.                                 
  591.                                 /* if there's a difference, stop here */
  592.                                 if (longestmatch[*cpos] != fname[*cpos])
  593.                                         return;
  594.  
  595.                                 for (index = (*cpos) + 1; index < longestlen; index++)
  596.                                         if (longestmatch[index] != fname[index]) {
  597.                                                 longestlen = index;
  598.                                                 longestmatch[longestlen] = 0;
  599.                                         }
  600.                         }
  601.                 }
  602.  
  603.                 /* on to the next file */
  604.                 fname = getnfile();
  605.         }
  606.  
  607.         /* beep if we never matched */
  608.         if (matches == 0) {
  609.                 TTbeep();
  610.                 return;
  611.         }
  612.  
  613.         /* the longestmatch array contains the longest match so copy and print it */
  614.         for ( ; (*cpos < (NSTRING-1)) && (*cpos < longestlen); (*cpos)++) {
  615.                 name[*cpos] = longestmatch[*cpos];
  616.                 TTputc(name[*cpos]);
  617.         }
  618.  
  619.         name[*cpos] = 0;
  620.  
  621.         /* if only one file matched then increment cpos to signal complete() */
  622.         /* that this was a complete match.  If a directory was matched then */
  623.         /* last character will be the DIRSEPCHAR.  In this case we do NOT */
  624.         /* want to signal a complete match. */
  625.         if ((matches == 1) && (name[(*cpos)-1] != DIRSEPCHAR))
  626.                 (*cpos)++;
  627.   
  628.         TTflush();
  629.         return;
  630. }
  631. #endif
  632.  
  633. /*      tgetc:  Get a key from the terminal driver, resolve any keyboard
  634.                 macro action                                    */
  635.  
  636. int PASCAL NEAR tgetc()
  637.  
  638. {
  639.         int c;  /* fetched character */
  640.  
  641.         /* if we are playing a keyboard macro back, */
  642.         if (kbdmode == PLAY) {
  643.  
  644.                 /* if there is some left... */
  645.                 if (kbdptr < kbdend)
  646.                         return((int)*kbdptr++);
  647.  
  648.                 /* at the end of last repitition? */
  649.                 if (--kbdrep < 1) {
  650.                         kbdmode = STOP;
  651. #if     VISMAC == 0
  652.                         /* force a screen update after all is done */
  653.                         update(FALSE);
  654. #endif
  655.                 } else {
  656.  
  657.                         /* reset the macro to the begining for the next rep */
  658.                         kbdptr = &kbdm[0];
  659.                         return((int)*kbdptr++);
  660.                 }
  661.         }
  662.  
  663.         /* fetch a character from the terminal driver */
  664.         c = TTgetc();
  665.  
  666.         /* record it for $lastkey */
  667.         lastkey = c;
  668.  
  669.         /* save it if we need to */
  670.         if (kbdmode == RECORD) {
  671.                 *kbdptr++ = c;
  672.                 kbdend = kbdptr;
  673.  
  674.                 /* don't overrun the buffer */
  675.                 if (kbdptr == &kbdm[NKBDM - 1]) {
  676.                         kbdmode = STOP;
  677.                         TTbeep();
  678.                 }
  679.         }
  680.  
  681.         /* and finally give the char back */
  682.         return(c);
  683. }
  684.  
  685. /*      getkey: Get one keystroke. The only prefixs legal here
  686.                         are the SPEC and CTRL prefixes.
  687. */
  688.  
  689. PASCAL NEAR getkey()
  690.  
  691. {
  692.         int c;          /* next input character */
  693.         int upper;      /* upper byte of the extended sequence */
  694.  
  695.         /* get a keystroke */
  696.         c = tgetc();
  697.  
  698.         /* if it exists, process an escape sequence */
  699.         if (c == 0) {
  700.                 /* get the event type */
  701.                 upper = tgetc();
  702.  
  703.                 /* mouse events need us to read in the row/col */
  704.                 if (upper & (MOUS >> 8)) {
  705.                         /* grab the x/y position of the mouse */
  706.                         xpos = tgetc();
  707.                         ypos = tgetc();
  708.                 }
  709.  
  710.                 /* get the event code */
  711.                 c = tgetc();
  712.  
  713.                 /* if it is a function key... map it */
  714. #if     0       /* this should be here...fix it for MSDOS dan! */
  715.                 if (upper & (SPEC >> 8))
  716.                         c = (upper << 8) | extcode(c & 255);
  717.                 else
  718. #endif
  719.                         c = (upper << 8) | c;
  720.  
  721.         }
  722.  
  723.         /* yank out the control prefix */
  724.         if ((c & 255) >=0x00 && (c & 255) <= 0x1F)
  725.                 c = CTRL | (c+'@');
  726.  
  727.         /* return the character */
  728.         return(c);
  729. }
  730.  
  731. /*      GETCMD: Get a command from the keyboard. Process all applicable
  732.                 prefix keys
  733.                                                         */
  734. PASCAL NEAR getcmd()
  735.  
  736. {
  737.         int c;          /* fetched keystroke */
  738.         KEYTAB *key;    /* ptr to a key entry */
  739.  
  740.         /* get initial character */
  741.         c = getkey();
  742.         key = getbind(c);
  743.  
  744.         /* resolve META and CTLX prefixes */
  745.         if (key) {
  746.                 if (key->k_ptr.fp == meta) {
  747.                         c = getkey();
  748.                         c = upperc(c) | (c & ~255);     /* Force to upper */
  749.                         c |= META;
  750.                 } else if (key->k_ptr.fp == cex) {
  751.                         c = getkey();
  752.                         c = upperc(c) | (c & ~255);     /* Force to upper */
  753.                         c |= CTLX;
  754.                 }
  755.         }
  756.  
  757.         /* return it */
  758.         return(c);
  759. }
  760.  
  761. /*      A more generalized prompt/reply function allowing the caller
  762.         to specify the proper terminator. If the terminator is not
  763.         a return('\r'), return will echo as "<NL>"
  764.                                                         */
  765. PASCAL NEAR getstring(prompt, buf, nbuf, eolchar)
  766.  
  767. char *prompt;
  768. char *buf;
  769. int eolchar;
  770.  
  771. {
  772.         register int cpos;      /* current character position in string */
  773.         register int c;         /* current input character */
  774.         register int quotef;    /* are we quoting the next char? */
  775.  
  776.         cpos = 0;
  777.         quotef = FALSE;
  778.  
  779.         /* prompt the user for the input string */
  780.         if (discmd)
  781.                 mlwrite(prompt);
  782.         else
  783.                 movecursor(term.t_nrow, 0);
  784.  
  785.         for (;;) {
  786.                 /* get a character from the user */
  787.                 c = getkey();
  788.  
  789.                 /* if they hit the line terminate, wrap it up */
  790.                 if (c == eolchar && quotef == FALSE) {
  791.                         buf[cpos++] = 0;
  792.  
  793.                         /* clear the message line */
  794.                         mlwrite("");
  795.                         TTflush();
  796.  
  797.                         /* if we default the buffer, return FALSE */
  798.                         if (buf[0] == 0)
  799.                                 return(FALSE);
  800.  
  801.                         return(TRUE);
  802.                 }
  803.  
  804.                 /* change from command form back to character form */
  805.                 c = ectoc(c);
  806.  
  807.                 if (c == ectoc(abortc) && quotef == FALSE) {
  808.                         /* Abort the input? */
  809.                         ctrlg(FALSE, 0);
  810.                         TTflush();
  811.                         return(ABORT);
  812.                 } else if ((c==0x7F || c==0x08) && quotef==FALSE) {
  813.                         /* rubout/erase */
  814.                         if (cpos != 0) {
  815.                                 outstring("\b \b");
  816.                                 --ttcol;
  817.  
  818.                                 if (buf[--cpos] < 0x20) {
  819.                                         outstring("\b \b");
  820.                                         --ttcol;
  821.                                 }
  822.  
  823.                                 if (buf[cpos] == '\r') {
  824.                                         outstring("\b\b  \b\b");
  825.                                         ttcol -= 2;
  826.                                 }
  827.                                 TTflush();
  828.                         }
  829.  
  830.                 } else if (c == 0x15 && quotef == FALSE) {
  831.                         /* C-U, kill */
  832.                         while (cpos != 0) {
  833.                                 outstring("\b \b");
  834.                                 --ttcol;
  835.  
  836.                                 if (buf[--cpos] < 0x20) {
  837.                                         outstring("\b \b");
  838.                                         --ttcol;
  839.                                 }
  840.                         }
  841.                         TTflush();
  842.  
  843.                 } else if (c == quotec && quotef == FALSE) {
  844.                         quotef = TRUE;
  845.                 } else {
  846.                         quotef = FALSE;
  847.                         if (cpos < nbuf-1) {
  848.                                 buf[cpos++] = c;
  849.  
  850.                                 if ((c < ' ') && (c != '\r')) {
  851.                                         outstring("^");
  852.                                         ++ttcol;
  853.                                         c ^= 0x40;
  854.                                 }
  855.  
  856.                                 if (c != '\r') {
  857.                                         if (disinp)
  858.                                                 mlout(c);
  859.                                 } else {        /* put out <NL> for <ret> */
  860.                                         outstring("<NL>");
  861.                                         ttcol += 3;
  862.                                 }
  863.                                 ++ttcol;
  864.                                 TTflush();
  865.                         }
  866.                 }
  867.         }
  868. }
  869.  
  870. PASCAL NEAR outstring(s) /* output a string of input characters */
  871.  
  872. char *s;        /* string to output */
  873.  
  874. {
  875.         if (disinp)
  876.                 while (*s)
  877.                         mlout(*s++);
  878. }
  879.  
  880. PASCAL NEAR ostring(s)  /* output a string of output characters */
  881.  
  882. char *s;        /* string to output */
  883.  
  884. {
  885.         if (discmd)
  886.                 while (*s)
  887.                         mlout(*s++);
  888. }
  889.  
  890.