home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / astrnomy / ephem421.zip / IO.C < prev    next >
C/C++ Source or Header  |  1990-09-13  |  21KB  |  753 lines

  1. /* this file (in principle) contains all the device-dependent code for
  2.  * handling screen movement and reading the keyboard. public routines are:
  3.  *   c_pos(r,c), c_erase(), c_eol();
  4.  *   chk_char(), read_char(), read_line (buf, max); and
  5.  *   byetty().
  6.  * N.B. we assume output may be performed by printf(), putchar() and
  7.  *   fputs(stdout). since these are buffered we flush first in read_char().
  8.  */
  9.  
  10. /* explanation of various conditional #define options:
  11.  * UNIX: uses termcap for screen management.
  12.  *   USE_NDELAY: does non-blocking tty reads with fcntl(O_NDELAY); otherwise
  13.  *     this is done with ioctl(..,FIONREAD..). Use which ever works on your
  14.  *     system.
  15.  *   USE_TERMIO: use termio.h instead of older generic sgtty.h.
  16.  * TURBO_C: compiles for Turbo C 2.0. I'm told it works for Lattice and
  17.  *     Microsoft too.
  18.  *   USE_ANSISYS: default PC cursor control uses direct BIOS calls (thanks to
  19.  *     Mr. Doug McDonald). If your PC does not work with this, however, add
  20.  *     "device ANSI.SYS" to your config.sys file and build ephem with
  21.  *     USE_ANSISYS.
  22.  * VMS: uses QIO for input, TERMTABLE info for output. This code uses only
  23.  *     standard VMS calls, i.e. it does not rely on any third-vendor termcap
  24.  *     package or the like. The code includes recoqnition of arrow keys, it
  25.  *     is easy to extend it to recoqnize other function keys. also, you don't
  26.  *     really need to #define VMS since it is inherent in the compiler.
  27.  *     (thanks to Mr. Karsten Spang, NBI, Copenhagen, spang@nbivax.nbi.dk)
  28.  */
  29.  
  30. /* define one of these... */
  31. #define UNIX
  32. /* #define VMS */
  33. /* #define TURBO_C */
  34.  
  35. /* then if you defined UNIX you want this too if you don't have FIONREAD */
  36. /* #define USE_NDELAY */
  37.  
  38. /* and then if you defined UNIX you want this too if using sgtty.h won't compile
  39.  * for you.
  40.  */
  41. /* #define USE_TERMIO */
  42.  
  43. /* if you defined TURBO_C you might want this too if screen io looks garbled */
  44. /* #define USE_ANSISYS */
  45.  
  46. #include <stdio.h>
  47. #include "screen.h"
  48.  
  49. #ifdef UNIX
  50. #include <signal.h>
  51. #ifdef USE_TERMIO
  52. #include <termio.h>
  53. #else
  54. #include <sgtty.h>
  55. #endif
  56. #ifdef USE_NDELAY
  57. #include <fcntl.h>
  58. #endif
  59.  
  60. extern char *tgoto();
  61. static char *cm, *ce, *cl, *kl, *kr, *ku, *kd; /* curses sequences */
  62. static int tloaded;
  63. static int ttysetup;
  64. #ifdef USE_TERMIO
  65. static struct termio orig_tio;
  66. #else
  67. static struct sgttyb orig_sgtty;
  68. #endif
  69.  
  70. /* move cursor to row, col, 1-based.
  71.  * we assume this also moves a visible cursor to this location.
  72.  */
  73. c_pos (r, c)
  74. int r, c;
  75. {
  76.     if (!tloaded) tload();
  77.     fputs (tgoto (cm, c-1, r-1), stdout);
  78. }
  79.  
  80. /* erase entire screen. */
  81. c_erase()
  82. {
  83.     if (!tloaded) tload();
  84.     fputs (cl, stdout);
  85. }
  86.  
  87. /* erase to end of line */
  88. c_eol()
  89. {
  90.     if (!tloaded) tload();
  91.     fputs (ce, stdout);
  92. }
  93.  
  94. #ifdef USE_NDELAY
  95. static char sav_char;    /* one character read-ahead for chk_char() */
  96. #endif
  97.  
  98. /* return 0 if there is a char that may be read without blocking, else -1 */
  99. chk_char()
  100. {
  101. #ifdef USE_NDELAY
  102.     if (!ttysetup) setuptty();
  103.     if (sav_char)
  104.         return (0);
  105.     fcntl (0, F_SETFL, O_NDELAY);    /* non-blocking read. FNDELAY on BSD */
  106.     if (read (0, &sav_char, 1) != 1)
  107.         sav_char = 0;
  108.     return (sav_char ? 0 : -1);
  109. #else
  110.     long n;
  111.     if (!ttysetup) setuptty();
  112.     ioctl (0, FIONREAD, &n);
  113.     return (n > 0 ? 0 : -1);
  114. #endif
  115. }
  116.  
  117. /* read the next char, blocking if necessary, and return it. don't echo.
  118.  * map the arrow keys if we can too into hjkl
  119.  */
  120. read_char()
  121. {
  122.     char c;
  123.     if (!ttysetup) setuptty();
  124.     fflush (stdout);
  125. #ifdef USE_NDELAY
  126.     fcntl (0, F_SETFL, 0);    /* blocking read */
  127.     if (sav_char) {
  128.         c = sav_char;
  129.         sav_char = 0;
  130.     } else
  131. #endif
  132.         read (0, &c, 1);
  133.     c = chk_arrow (c & 0177); /* just ASCII, please */
  134.     return (c);
  135. }
  136.  
  137. /* used to time out of a read */
  138. static got_alrm;
  139. static
  140. on_alrm()
  141. {
  142.     got_alrm = 1;
  143. }
  144.  
  145. /* see if c is the first of any of the curses arrow key sequences.
  146.  * if it is, read the rest of the sequence, and return the hjkl code
  147.  * that corresponds.
  148.  * if no match, just return c.
  149.  */
  150. static 
  151. chk_arrow (c)
  152. register char c;
  153. {
  154.     register char *seq;
  155.  
  156.     if (c == *(seq = kl) || c == *(seq = kd) || c == *(seq = ku)
  157.                          || c == *(seq = kr)) {
  158.         char seqa[32]; /* maximum arrow escape sequence ever expected */
  159.         unsigned l = strlen(seq);
  160.         seqa[0] = c;
  161.         if (l > 1) {
  162.         extern unsigned alarm();
  163.         /* cautiously read rest of arrow sequence */
  164.         got_alrm = 0;
  165.         (void) signal (SIGALRM, on_alrm);
  166.         alarm(2);
  167.         read (0, seqa+1, l-1);
  168.         alarm(0);
  169.         if (got_alrm)
  170.             return (c);
  171.         }
  172.         seqa[l] = '\0';
  173.         if (strcmp (seqa, kl) == 0)
  174.         return ('h');
  175.         if (strcmp (seqa, kd) == 0)
  176.         return ('j');
  177.         if (strcmp (seqa, ku) == 0)
  178.         return ('k');
  179.         if (strcmp (seqa, kr) == 0)
  180.         return ('l');
  181.     }
  182.     return (c);
  183. }
  184.  
  185. /* do whatever might be necessary to get the screen and/or tty back into shape.
  186.  */
  187. byetty()
  188. {
  189. #ifdef USE_TERMIO
  190.     ioctl (0, TCSETA, &orig_tio);
  191. #else
  192.     ioctl (0, TIOCSETP, &orig_sgtty);
  193. #endif
  194. #ifdef USE_NDELAY
  195.     fcntl (0, F_SETFL, 0);    /* be sure to go back to blocking read */
  196. #endif
  197.     ttysetup = 0;
  198. }
  199.  
  200. static 
  201. tload()
  202. {
  203.     extern char *getenv(), *tgetstr();
  204.     extern char *UP, *BC;
  205.     char *egetstr();
  206.     static char tbuf[512];
  207.     char rawtbuf[1024];
  208.     char *tp;
  209.     char *ptr;
  210.  
  211.     if (!(tp = getenv ("TERM"))) {
  212.         printf ("no TERM\n");
  213.         exit(1);
  214.     }
  215.  
  216.     if (!ttysetup) setuptty();
  217.     if (tgetent (rawtbuf, tp) != 1) {
  218.         printf ("Can't find termcap for %s\n", tp);
  219.         exit (1);
  220.     }
  221.     ptr = tbuf;
  222.     ku = egetstr ("ku", &ptr);
  223.     kd = egetstr ("kd", &ptr);
  224.     kl = egetstr ("kl", &ptr);
  225.     kr = egetstr ("kr", &ptr);
  226.     cm = egetstr ("cm", &ptr);
  227.     ce = egetstr ("ce", &ptr);
  228.     cl = egetstr ("cl", &ptr);
  229.     UP = egetstr ("up", &ptr);
  230.     if (!tgetflag ("bs"))
  231.         BC = egetstr ("bc", &ptr);
  232.     tloaded = 1;
  233. }
  234.  
  235. /* like tgetstr() but discard curses delay codes, for now anyways */
  236. static char *
  237. egetstr (name, sptr)
  238. char *name;
  239. char **sptr;
  240. {
  241.     extern char *tgetstr();
  242.     register char c, *s;
  243.  
  244.     s = tgetstr (name, sptr);
  245.     while (((c = *s) >= '0' && c <= '9') || c == '*')
  246.         s += 1;
  247.     return (s);
  248. }
  249.  
  250. /* set up tty for char-by-char read, non-blocking  */
  251. static
  252. setuptty()
  253. {
  254. #ifdef USE_TERMIO
  255.     struct termio tio;
  256.  
  257.     ioctl (0, TCGETA, &orig_tio);
  258.     tio = orig_tio;
  259.     tio.c_iflag &= ~ICRNL;    /* leave CR unchanged */
  260.     tio.c_oflag &= ~OPOST;    /* no output processing */
  261.     tio.c_lflag &= ~(ICANON|ECHO); /* no input processing, no echo */
  262.     tio.c_cc[VMIN] = 1;    /* return after each char */
  263.     tio.c_cc[VTIME] = 0;    /* no read timeout */
  264.     ioctl (0, TCSETA, &tio);
  265. #else
  266.     struct sgttyb sg;
  267.  
  268.     ioctl (0, TIOCGETP, &orig_sgtty);
  269.     sg = orig_sgtty;
  270.     sg.sg_flags &= ~ECHO;    /* do our own echoing */
  271.     sg.sg_flags &= ~CRMOD;    /* leave CR and LF unchanged */
  272.     sg.sg_flags |= XTABS;    /* no tabs with termcap */
  273.     sg.sg_flags |= CBREAK;    /* wake up on each char but can still kill */
  274.     ioctl (0, TIOCSETP, &sg);
  275. #endif
  276.     ttysetup = 1;
  277. }
  278. /* end of #ifdef UNIX */
  279. #endif
  280.  
  281. #ifdef TURBO_C
  282. #ifdef USE_ANSISYS
  283. #define    ESC    '\033'
  284. /* position cursor.
  285.  * (ANSI: ESC [ r ; c f) (r/c are numbers given in ASCII digits)
  286.  */
  287. c_pos (r, c)
  288. int r, c;
  289. {
  290.     printf ("%c[%d;%df", ESC, r, c);
  291. }
  292.  
  293. /* erase entire screen. (ANSI: ESC [ 2 J) */
  294. c_erase()
  295. {
  296.     printf ("%c[2J", ESC);
  297. }
  298.  
  299. /* erase to end of line. (ANSI: ESC [ K) */
  300. c_eol()
  301. {
  302.     printf ("%c[K", ESC);
  303. }
  304. #else
  305. #include <dos.h>   
  306. union REGS rg;
  307.  
  308. /* position cursor.
  309.  */
  310. c_pos (r, c)
  311. int r, c;
  312. {
  313.         rg.h.ah = 2;
  314.         rg.h.bh = 0;
  315.         rg.h.dh = r-1;
  316.         rg.h.dl = c-1;
  317.         int86(16,&rg,&rg);
  318. }
  319.  
  320. /* erase entire screen.  */
  321. c_erase()
  322. {
  323.         int cur_cursor, i;
  324.         rg.h.ah = 3;
  325.         rg.h.bh = 0;
  326.         int86(16,&rg,&rg);
  327.         cur_cursor = rg.x.dx;
  328.         for(i = 0; i < 25; i++){
  329.             c_pos(i+1,1);
  330.             rg.h.ah = 10;
  331.             rg.h.bh = 0;
  332.             rg.h.al = 32;
  333.             rg.x.cx = 80;
  334.             int86(16,&rg,&rg);
  335.         }
  336.         rg.h.ah = 2;
  337.         rg.h.bh = 0;
  338.         rg.x.dx = cur_cursor;
  339.         int86(16,&rg,&rg);
  340.         
  341. }
  342.  
  343. /* erase to end of line.*/
  344. c_eol()
  345. {
  346.         int cur_cursor, i;
  347.         rg.h.ah = 3;
  348.         rg.h.bh = 0;
  349.         int86(16,&rg,&rg);
  350.         cur_cursor = rg.x.dx;
  351.         rg.h.ah = 10;
  352.         rg.h.bh = 0;
  353.         rg.h.al = 32;
  354.         rg.x.cx = 80 - rg.h.dl;
  355.         int86(16,&rg,&rg);
  356.         rg.h.ah = 2;
  357.         rg.h.bh = 0;
  358.         rg.x.dx = cur_cursor;
  359.         int86(16,&rg,&rg);
  360.  
  361. }
  362. #endif
  363.  
  364. /* return 0 if there is a char that may be read without blocking, else -1 */
  365. chk_char()
  366. {
  367.     return (kbhit() == 0 ? -1 : 0);
  368. }
  369.  
  370. /* read the next char, blocking if necessary, and return it. don't echo.
  371.  * map the arrow keys if we can too into hjkl
  372.  */
  373. read_char()
  374. {
  375.     int c;
  376.     fflush (stdout);
  377.     c = getch();
  378.     if (c == 0) {
  379.         /* get scan code; convert to direction hjkl if possible */
  380.         c = getch();
  381.         switch (c) {
  382.         case 0x4b: c = 'h'; break;
  383.         case 0x50: c = 'j'; break;
  384.         case 0x48: c = 'k'; break;
  385.         case 0x4d: c = 'l'; break;
  386.         }
  387.     }
  388.     return (c);
  389. }
  390.  
  391. /* do whatever might be necessary to get the screen and/or tty back into shape.
  392.  */
  393. byetty()
  394. {
  395. }
  396. /* end of #ifdef TURBO_C */
  397. #endif
  398.  
  399. #ifdef VMS
  400. #include <string.h>
  401. #include <iodef.h>
  402. #include <descrip.h>
  403. #include <dvidef.h>
  404. #include <smgtrmptr.h>
  405. #include <starlet.h>
  406. #include <lib$routines.h>
  407. #include <smg$routines.h>
  408.  
  409. /* Structured types for use in system calls */
  410. typedef struct{
  411.     unsigned short status;
  412.     unsigned short count;
  413.     unsigned int info;
  414. } io_status_block;
  415. typedef struct{
  416.     unsigned short buffer_length;
  417.     unsigned short item_code;
  418.     void *buffer_address;
  419.     unsigned short *return_length_address;
  420.     unsigned long terminator;
  421. } item_list;
  422.  
  423. static unsigned short ttchan = 0; /* channel number for terminal    */
  424. volatile static io_status_block iosb; /* I/O status block for operation */
  425.                                       /* currently in progress          */
  426. volatile static unsigned char input_buf; /* buffer to recieve input charac-*/
  427.                                          /* ter when operation completes   */
  428. static void *term_entry;          /* pointer to TERMTABLE entry     */
  429. #define MAXCAP 10
  430. static char ce[MAXCAP];           /* ce and cl capability strings for  */
  431. static char cl[MAXCAP];           /* this terminal type                */
  432.  
  433. /* Declaration of special keys to be recoqnized on input */
  434. /* Number of special keys defined */
  435. #define MAXKEY 4
  436. /* TERMTABLE capability codes for the keys */
  437. static long capcode[MAXKEY] = {SMG$K_KEY_UP_ARROW,SMG$K_KEY_DOWN_ARROW,
  438.     SMG$K_KEY_RIGHT_ARROW,SMG$K_KEY_LEFT_ARROW};
  439. /* character codes to be returned by read_char when a special key is presssed */
  440. static int retcode[MAXKEY] = {'k','j','l','h'};
  441. /* the actual capability strings from the key */
  442. static char keycap[MAXKEY][MAXCAP];
  443.  
  444. static char special_buffer[MAXCAP];   /* buffer for reading special key */
  445. static int chars_in_buffer;           /* number of characters in buffer */
  446.  
  447. /* set up the structures for this I/O module */
  448. inittt()
  449. {
  450.     unsigned int status;   /* system routine return status */
  451.     $DESCRIPTOR(tt,"TT");  /* terminal name */
  452.     item_list itmlst;      /* item list for $getdvi obtaining term type */
  453.     unsigned long devtype; /* terminal type returned form $getdvi */
  454.     unsigned short retlen; /* return length from $getdvi */
  455.     unsigned long lenret;  /* return length from smg$get_term_data */
  456.     unsigned long maxlen;  /* maximum return length */
  457.     unsigned long cap_code;/* capability code */
  458. #define MAXINIT 20
  459.     char init_string[MAXINIT];/* string to initialize terminal */
  460.     int key;
  461.  
  462.     /* Assign a channel to the terminal */
  463.     if (!((status = sys$assign(&tt,&ttchan,0,0))&1)) lib$signal(status);
  464.  
  465.     /* Get terminal type. Note that it is possible to use the same
  466.      * iosb at this stage, because no I/O is initiated yet.
  467.      */
  468.     itmlst.buffer_length = 4;
  469.     itmlst.item_code = DVI$_DEVTYPE;
  470.     itmlst.buffer_address = &devtype;
  471.     itmlst.return_length_address = &retlen;
  472.     itmlst.terminator = 0;
  473.     if (!((status = sys$getdviw(0,ttchan,0,&itmlst,&iosb,0,0,0))&1))
  474.         lib$signal(status);
  475.     if (!(iosb.status&1)) lib$signal(iosb.status);
  476.  
  477.     /* Get the TERMTABLE entry corresponding to the terminal type */
  478.     if (!((status = smg$init_term_table_by_type(&devtype,
  479.         &term_entry))&1)) lib$signal(status);
  480.  
  481.     /* Get the initialisation string and initialize terminal */
  482.     cap_code = SMG$K_INIT_STRING;
  483.     maxlen = MAXINIT - 1;
  484.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  485.         &lenret,init_string))&1)) lib$signal(status);
  486.     init_string[lenret] = '\0';
  487.     fputs(init_string,stdout);
  488.     fflush(stdout);
  489.  
  490.     /* Get ce and cl capabilities, these are static */
  491.     cap_code = SMG$K_ERASE_TO_END_LINE;
  492.     maxlen = MAXCAP-1;
  493.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  494.         &lenret,ce))&1)) lib$signal(status);
  495.     ce[lenret] = '\0';
  496.  
  497.     cap_code = SMG$K_ERASE_WHOLE_DISPLAY;
  498.     maxlen = MAXCAP-1;
  499.     if (!((status = smg$get_term_data(&term_entry,&cap_code,&maxlen,
  500.         &lenret,cl))&1)) lib$signal(status);
  501.     cl[lenret] = '\0';
  502.  
  503.     /* Here one could obtain line drawing sequences, please feel free
  504.        to implement it ... */
  505.  
  506.     /* Get special keys to be recoqnized on input */
  507.     for (key = 0;key<MAXKEY;key++)
  508.     {
  509.         maxlen = MAXCAP-1;
  510.         if (!((status = smg$get_term_data(&term_entry,&capcode[key],
  511.             &maxlen,&lenret,keycap[key]))&1)) lib$signal(status);
  512.         keycap[key][lenret] = '\0';
  513.     }
  514.  
  515.     /* Initiate first input operation, NOECHO.
  516.      * NOFILTR allows any character to get through, this makes it
  517.      * possible to implement arrow recoqnition, and also makes
  518.      * DEL and BS get through.
  519.      * We don't wait for the operation to complete.
  520.      * Note that stdout has already been fflush'ed above.
  521.      */
  522.     if (!((status = sys$qio(0,ttchan,
  523.         IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  524.         &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  525.  
  526.     /* Initialise special key buffer */
  527.     chars_in_buffer = 0;
  528. } /* inittt */
  529.  
  530.  
  531. /* return 0 if there is a char that may be read without blocking, else -1 */
  532. chk_char()
  533. {
  534.     if (!ttchan) inittt();
  535.  
  536.         return ( chars_in_buffer != 0 ? 0 :(iosb.status == 0 ? -1 : 0));
  537. }
  538.  
  539. /* read the next char, blocking if necessary, and return it. don't echo.
  540.  * map the arrow keys if we can too into hjkl
  541.  */
  542. read_char()
  543. {
  544.     unsigned int status;
  545.     int buf;
  546.     int i;
  547.     int found_key;
  548.     int key;
  549.     int this_len;
  550.     int match;
  551.  
  552.     if (!ttchan) inittt();
  553.  
  554.     /* If we attempted to read an special key previously, there are characters
  555.      * left in the buffer, return these before doing more I/O
  556.      */
  557.     if (chars_in_buffer!=0){
  558.         buf = special_buffer[0];
  559.         chars_in_buffer--;
  560.         for (i = 0;i<chars_in_buffer;i++)
  561.         {
  562.             special_buffer[i] = special_buffer[i+1];
  563.         }
  564.         special_buffer[chars_in_buffer] = '\0';
  565.     }
  566.     else {
  567.  
  568.         /* Loop over characters read, the loop is terminated when the
  569.          * characters read so far do not match any of the special keys
  570.          * or when the characters read so far is identical to one of
  571.          * the special keys.
  572.          */
  573.  
  574.         do
  575.         {
  576.             /* Wait for I/O to complete */
  577.             if (!((status = sys$synch(0,&iosb))&1)) lib$signal(status);
  578.             special_buffer[chars_in_buffer] = input_buf;
  579.             chars_in_buffer++;
  580.             special_buffer[chars_in_buffer] = '\0';
  581.  
  582.             /* Initiate next input operation */
  583.             fflush (stdout);
  584.             if (!((status = sys$qio(0,ttchan,
  585.                 IO$_READVBLK|IO$M_NOECHO|IO$M_NOFILTR,
  586.                 &iosb,0,0,&input_buf,1,0,0,0,0))&1)) lib$signal(status);
  587.  
  588.  
  589.             /* Check for match with all special strings */
  590.             match = 0;
  591.             found_key = MAXKEY;
  592.             for (key = 0;key<MAXKEY;key++)
  593.             {
  594.                 this_len = strlen(keycap[key]);
  595.                 if (this_len<chars_in_buffer) continue;
  596.                 if (!strncmp(keycap[key],special_buffer,chars_in_buffer)){
  597.                     match = -1;
  598.                     if (this_len == chars_in_buffer){
  599.                         found_key = key;
  600.                         break;
  601.                     }
  602.                 }
  603.             }
  604.         }
  605.         while (match && (found_key == MAXKEY));
  606.  
  607.         /* If one of the keys matches the input string, return the
  608.          * corresponding  key code
  609.          */
  610.         if (found_key != MAXKEY)
  611.         {
  612.             buf = retcode[found_key];
  613.             chars_in_buffer = 0;
  614.         }
  615.         else /* return first character and store the rest in the buffer */
  616.         {
  617.             buf = special_buffer[0];
  618.             chars_in_buffer--;
  619.             for (i = 0;i<chars_in_buffer;i++)
  620.             {
  621.                 special_buffer[i] = special_buffer[i+1];
  622.             }
  623.         }
  624.         special_buffer[chars_in_buffer] = '\0';
  625.     }
  626.     return(buf);
  627. }
  628.  
  629. /* do whatever might be necessary to get the screen and/or tty back into shape.
  630.  */
  631. byetty()
  632. {
  633.     unsigned int status;
  634.  
  635.     if (ttchan)
  636.     {
  637.         /* There is no string in SMG to send to the terminal when
  638.          * terminating, one could clear the screen, move the cursor to
  639.          * the last line, or whatever. This program clears the screen
  640.          * anyway before calling this routine, so we do nothing.
  641.          */
  642.  
  643.  
  644.  
  645.         /* The following is not really neccessary, it will be done at program
  646.          * termination anyway, but if someone tries to use the I/O routines agai
  647.    n
  648.          * it might prove useful...
  649.          */
  650.         if (!((status = smg$del_term_table())&1)) lib$signal(status);
  651.         if (!((status = sys$dassgn(ttchan))&1)) lib$signal(status);
  652.         /* This also cancels any outstanding I/O on the channel */
  653.         ttchan = 0; /* marks terminal I/O as not initialized */
  654.     }
  655. }
  656.  
  657. /* position cursor. */
  658. c_pos (r, c)
  659. int r, c;
  660. {
  661.     unsigned long vector[3]; /* argument vector (position)   */
  662.     unsigned long status;    /* system service return status */
  663.     long lenret;             /* length of returned string    */
  664.     long maxlen;             /* maximum return length        */
  665.     unsigned long capcode;   /* capability code              */
  666.     char seq[2*MAXCAP];      /* returned string              */
  667.  
  668.     if (!ttchan) inittt();
  669.  
  670.     /* Set cursor depends on the position, therefore we have to call
  671.      * get_term_data for each operation
  672.      */
  673.     vector[0] = 2;
  674.     vector[1] = r;
  675.     vector[2] = c;
  676.     capcode = SMG$K_SET_CURSOR_ABS;
  677.     maxlen = 2*MAXCAP-1;
  678.     if (!((status = smg$get_term_data(&term_entry,&capcode,&maxlen,
  679.         &lenret,seq,vector))&1)) lib$signal(status);
  680.     seq[lenret] = '\0';
  681.  
  682.     fputs(seq,stdout);
  683. }
  684.  
  685. /* erase entire screen. */
  686. c_erase()
  687. {
  688.     if (!ttchan) inittt();
  689.  
  690.     fputs(cl,stdout);
  691. }
  692.  
  693. /* erase to end of line. */
  694. c_eol()
  695. {
  696.     if (!ttchan) inittt();
  697.  
  698.     fputs(ce,stdout);
  699. }
  700. /* end of #ifdef VMS */
  701. #endif
  702.  
  703. /* read up to max chars into buf, with cannonization.
  704.  * add trailing '\0' (buf is really max+1 chars long).
  705.  * return count of chars read (not counting '\0').
  706.  * assume cursor is already positioned as desired.
  707.  * if type END when n==0 then return -1.
  708.  */
  709. read_line (buf, max)
  710. char buf[];
  711. int max;
  712. {
  713.     static char erase[] = "\b \b";
  714.     int n, c;
  715.     int done;
  716.  
  717. #ifdef UNIX
  718.     if (!ttysetup) setuptty();
  719. #endif
  720.  
  721.     for (done = 0, n = 0; !done; )
  722.         switch (c = read_char()) {    /* does not echo */
  723.         case cntrl('h'):    /* backspace or */
  724.         case 0177:        /* delete are each char erase */
  725.         if (n > 0) {
  726.             fputs (erase, stdout);
  727.             n -= 1;
  728.         }
  729.         break;
  730.         case cntrl('u'):        /* line erase */
  731.         while (n > 0) {
  732.             fputs (erase, stdout);
  733.             n -= 1;
  734.         }
  735.         break;
  736.         case '\r':    /* EOL */
  737.         done++;
  738.         break;
  739.         default:            /* echo and store, if ok */
  740.         if (n == 0 && c == END)
  741.             return (-1);
  742.         if (n >= max)
  743.             putchar (cntrl('g'));
  744.         else if (c >= ' ') {
  745.             putchar (c);
  746.             buf[n++] = c;
  747.         }
  748.         }
  749.  
  750.     buf[n] = '\0';
  751.     return (n);
  752. }
  753.