home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / hl10osrc.lzh / Lib / Terminal.cc < prev    next >
Text File  |  1994-04-23  |  23KB  |  936 lines

  1. /* -*- Mode: C -*- */
  2. /* Terminal.cc - Terminal class implementation
  3.  *        Inspired by the User Interface Manager code in chapter
  4.  *        12 of "C++, a guide for C programmers",    by Sharam Hekmatpour,
  5.  *        (c) 1990 by Prentice Hall of Australia Pty Ltd.
  6.  * Created by Robert Heller on Sat Dec  7 22:05:49 1991
  7.  *
  8.  * ------------------------------------------------------------------
  9.  * Home Libarian by Deepwoods Software
  10.  * Common Class library implementation code
  11.  * ------------------------------------------------------------------
  12.  * Modification History:
  13.  * ------------------------------------------------------------------
  14.  * Contents:
  15.  * ------------------------------------------------------------------
  16.  * 
  17.  * 
  18.  * Copyright (c) 1991,1992 by Robert heller (D/B/A Deepwoods Software)
  19.  *        All Rights Reserved
  20.  * 
  21.  */
  22. #include <Terminal.h>
  23.  
  24. #ifndef MESSYDOS
  25. // Globals used by termcap library...
  26. // (Weirdness of OSK's termcap library...)
  27. #ifdef OSK
  28. char PC_;
  29. #else
  30. char PC;
  31. #endif
  32. char* BC;
  33. char* UP;
  34. short ospeed;
  35. #endif
  36.  
  37. short Terminal::cline;            // current line
  38. short Terminal::ccol;            // current column
  39. short Terminal::lines;            // screen lines
  40. short Terminal::colms;            // screen columns
  41. ErrFun Terminal::errFun;
  42. char   Terminal::termBuf[bufSize];
  43. Terminal* Terminal::term;
  44. #ifdef OSK
  45. sgbuf Terminal::ttym;
  46. sgbuf Terminal::xttym;
  47. #endif
  48. #ifndef MESSYDOS
  49. char Terminal::TermType[48];        // terminal type name
  50. char Terminal::TCapBuf[TCapsLen];        // termcap buffer
  51. char Terminal::tcapbuf[TCapsLen];        // copy
  52.     // termcap capabilities
  53. char* Terminal::BC;            // backspace character
  54. char* Terminal::UP;            // up line sequence
  55. char* Terminal::CL;            // clear screen
  56. char* Terminal::CM;            // cursor movement
  57. char* Terminal::CE;            // clear to eol
  58. char* Terminal::CD;            // clear to eos
  59. char* Terminal::SO;            // standout start
  60. char* Terminal::SE;            // standout end
  61. char* Terminal::HO;            // home cursor
  62. char* Terminal::KD;            // down arrow key
  63. char* Terminal::KU;            // up arrow key
  64. char* Terminal::KR;            // right arrow key
  65. char* Terminal::KL;            // left arrow key
  66. char* Terminal::KH;            // home key
  67. char* Terminal::KB;            // backspace key
  68. char* Terminal::TI;            // terminal init
  69. char* Terminal::TE;            // terminal de-init
  70. short Terminal::ospeed;            // baud rate (used for padding)
  71. char Terminal::PC;                // pad character
  72. #endif
  73.  
  74. // Interrupt handler...
  75. int Interrupt()
  76. {
  77.     Terminal::term->PlainPen();
  78.     Terminal::term->ExitChars();
  79. #ifdef OSK
  80.     _ss_opt(0, &(Terminal::ttym));
  81.     intercept(0);
  82. #endif
  83.     exit(1);
  84.     return(0);
  85. }
  86.  
  87.  
  88. #ifdef OSK
  89. // This is how signals are handled by OSK programs...
  90. static void Intercept(int sig)
  91. {
  92.     if (sig == SIGINT) Interrupt();
  93.     return;
  94. }
  95. #endif
  96.  
  97. // Constructor: fetch terminal type, termcap info, and initialize terminal
  98. Terminal::Terminal ()
  99. {
  100.     char* temp;
  101.  
  102.     // make sure called only once
  103.     if (term != 0) Error(termErr,"in Terminal");
  104.     term = this;        // self reference
  105. #ifdef MESSYDOS
  106.     gppconio_init();
  107.     text_info screen_info;
  108.     gettextinfo(&screen_info);
  109.     lines = screen_info.screenheight;
  110.     colms = screen_info.screenwidth;
  111.     cline = 0;
  112.     ccol  = 0;
  113.     PlainPen();
  114. #else
  115.     SE = "";        // (set here so Error won't crash)
  116. #ifdef OSK
  117.     // Get vanila terminal modes
  118.     if (_gs_opt(0, &ttym) < 0) Error(sysErr,"for _gs_opt");
  119. #endif
  120.     // Get terminal type
  121.     char *tt = getenv("TERM");
  122.     // Unknown??  give up...
  123.     if (tt == 0) Error(sysErr,"Env. TERM not set!");
  124.     // remember type...
  125.      strcpy(TermType,tt);
  126.      // Fetch termcap entry...
  127.     if (tgetent(TCapBuf,TermType) <= 0)
  128.         Error(sysErr,"Unknown Terminal Type!");
  129.     // Fetch needed caps.
  130.      char* ptr = tcapbuf;
  131.     // pad character:
  132. #ifdef OSK
  133.     if (temp = tgetstr("pc",&ptr)) {
  134.         PC = PC_ = *temp;
  135.     } else PC = PC_ = 0;
  136. #else
  137.     if (temp = tgetstr("pc",&ptr)) {
  138.         PC = PC = *temp;
  139.     } else PC = PC = 0;
  140. #endif
  141.     ::BC = BC = tgetstr("bc",&ptr);        // backspace:
  142.     if (BC == 0) ::BC = BC = "\010";    // make sure it is something
  143.     ::UP = UP = tgetstr("up",&ptr);        // up line
  144.     CL = tgetstr("cl",&ptr);        // clear display
  145.     CM = tgetstr("cm",&ptr);        // cursor movement
  146.     CE = tgetstr("ce",&ptr);        // clear to EOL
  147.     CD = tgetstr("cd",&ptr);        // clear to EOS
  148.     SO = tgetstr("so",&ptr);        // standout begin
  149.     SE = tgetstr("se",&ptr);        // standout end
  150.      HO = tgetstr("ho",&ptr);        // home cursor
  151.     KD = tgetstr("kd",&ptr);        // down arrow key
  152.     KU = tgetstr("ku",&ptr);        // up arrow key
  153.     KR = tgetstr("kr",&ptr);        // right arrow key
  154.     KL = tgetstr("kl",&ptr);        // left arrow key
  155.     KH = tgetstr("kh",&ptr);        // home key
  156.     KB = tgetstr("kb",&ptr);        // backspace key
  157.     TI = tgetstr("ti",&ptr);        // term init string
  158.     TE = tgetstr("te",&ptr);        // term de-init string
  159.     lines = tgetnum("li");            // lines
  160.     colms = tgetnum("co");            // columns
  161. #ifdef OSK
  162.     ::ospeed = ospeed = ttym.sg_baud;    // baud rate
  163. #endif
  164.     // handle missing features
  165.     if (CE == 0) CE = "\007";
  166.     if (SO == 0) SO = "";
  167.     if (SE == 0) SE = "";
  168.     if (TI == 0) TI = "";
  169.     if (TE == 0) TE = "";
  170.     if (KD == 0) KD = "\016";    // ^N
  171.     if (KU == 0) KU = "\020";    // ^P
  172.     if (KR == 0) KR = "\006";    // ^F
  173.     if (KL == 0) KL = "\002";    // ^B
  174.     if (KH == 0) {
  175.         if (KD[0] == '\033' || KU[0] == '\033' ||
  176.             KR[0] == '\033' || KL[0] == '\033')
  177.             KH = "\033\033";    // ESC-ESC (^[^[)
  178.         else KH = "\033";    // ESC (^[)
  179.     }
  180.     if (KB == 0) KB = "\010";
  181.     // check to be sure terminal is not too dumb...
  182.     if (HO == 0) Error(sysErr,"Need ho");
  183.     if (CD == 0 && CL == 0) Error(sysErr,"Need either cd or cl!");
  184.     if (CM == 0) Error(sysErr,"Need cm");
  185.     if (lines < 1 || colms < 1) {
  186.             Error(sysErr,"Incomplete termcap entry");
  187.     }
  188.     if (CD == 0) CD = "\007";
  189.     // make sure we didn't overflow buffer
  190.     if (ptr >= &tcapbuf[TCapsLen]) {
  191.         Error(sysErr,"Terminal description too big!");
  192.     }
  193. #ifdef OSK
  194.     // setup for raw terminal modes
  195.     xttym = ttym;        // make a copy of the vanila setup
  196.     xttym.sg_backsp = 0;    // disable editing
  197.     xttym.sg_delete = 0;
  198.     xttym.sg_echo   = 0;    // echoing
  199.     xttym.sg_alf    = 0;    // auto lf's
  200.     xttym.sg_nulls  = 0;    // nulls
  201.     xttym.sg_pause  = 0;    // pausing
  202.     xttym.sg_bspch  = 0;    // backspacing
  203.     xttym.sg_dlnch  = 0;    // delete line
  204.     xttym.sg_eorch  = 0;    // end of record
  205.     xttym.sg_eofch  = 0;    // end of file
  206.     xttym.sg_rlnch  = 0;    // redraw line
  207.     xttym.sg_dulnch = 0;    
  208.     xttym.sg_psch   = 0;
  209.     xttym.sg_kbich  = 0;    // interrupting
  210.     xttym.sg_kbach  = 0;
  211.     xttym.sg_xon    = 0;    // xon/xoff (might be a bad idea...)
  212.     xttym.sg_xoff   = 0;
  213.     xttym.sg_tabcr  = 0;    // tab handling
  214.     // set raw mode
  215.     if (_ss_opt(0, &xttym) < 0) Error(sysErr,"for _ss_opt");
  216.     // setup interceptor
  217.     if (intercept((int (*)(...))Intercept) < 0) Error(sysErr,"for intercept");
  218. #endif    
  219. #endif
  220.     Clear();        // clear screen
  221.     InitChars();        // initialize terminal
  222. }
  223.  
  224. // Destructor:  restore terminal state
  225. Terminal::~Terminal ()
  226. {
  227.     PlainPen();        // plain pen
  228.     PenPos(lines-1,0);    // bottom of screen
  229.     ExitChars();        // de-init
  230. #ifdef OSK
  231.     // restore modes
  232.     if (_ss_opt(0, &ttym) < 0) Error(sysErr,"for _ss_opt");
  233.     // can interception of signals
  234.     if (intercept(0) < 0) Error(sysErr,"for intercept");
  235. #endif
  236. }
  237. #ifdef MESSYDOS
  238. extern "C" {
  239.     void sound(int);
  240. }
  241. void Terminal::Bell()
  242. {
  243.     sound(440);
  244.     sleep(1);
  245.     sound(0);
  246. }
  247.  
  248. Terminal &Terminal::put(unsigned char ch)
  249. {
  250. #ifdef BARF1
  251.     gotoxy(ccol+1,cline+1);
  252. #endif
  253.     putch(ch);
  254. #ifdef BARF2
  255.     ccol++;
  256.     if (ccol >= colms)
  257.     {
  258.         ccol = 0;
  259.         cline++;
  260.         if (cline >= lines) cline = 0;
  261.     }
  262. #endif
  263. }
  264. // get a terminal key press. This function will hang and wait for a key press
  265. int Terminal::GetKey ()
  266. {
  267.     int ch;    // input key
  268.  
  269.     ch = kbd_getkey();
  270.     // check posible magic keys
  271.     switch (ch)
  272.     {
  273.         case K_ESC:
  274.         case K_HOME: return escCmd;
  275.         case K_CTLP:
  276.         case K_UP: return upCmd;
  277.         case K_CTLB:
  278.         case K_LEFT: return leftCmd;
  279.         case K_CTLF:
  280.         case K_RIGHT: return rightCmd;
  281.         case K_CTLN:
  282.         case K_DOWN: return downCmd; 
  283. //        case K_CTLH:
  284.         case K_BACK: return backCmd;
  285.         // plain key.
  286.         default: return ch;
  287.     }
  288. }
  289.  
  290. // check to see if a key has been pressed.  Does not hang.  Does not
  291. // actually read the key.
  292. Boolean Terminal::KeyAvailable()
  293. {
  294.     if ( kbd_ready() ) return true;
  295.     else return false;
  296. }
  297.  
  298. // Error handler
  299. void Terminal::Error (ErrKind err,const char *msg)
  300. {
  301.     if (errFun != 0) (*errFun)(err,msg);
  302.     else {
  303.         if (err == sysErr) {
  304.             // fetch system error message for system errors
  305.             printf("Error: %s %s\n",strerror(errno),msg);
  306.             Interrupt();
  307.         } else {
  308.             printf("Error: %s %s\n",
  309.                 (err == termErr ? "Terminal" : "Memory"),
  310.                 msg);
  311.             Interrupt();
  312.         }
  313.     }
  314. }
  315.  
  316. // Write a string to the message line (bottom terminal line) in standout
  317. // mode
  318. void Terminal::Message (const char* msg)
  319. {
  320.     int lastRow = lines;
  321.     int xcline = cline;
  322.     int xccol  = ccol;
  323.     RevsPen();
  324.     strncpy(termBuf,msg,colms);
  325.     register len = strlen(msg);
  326.     if (len >= colms) {
  327.         termBuf[len = (colms-1)] = '\0';
  328.         while (len >= (colms-1) - 3)  termBuf[--len] = '.';
  329.     } else {
  330.         while (len < (colms-1)) termBuf[len++] = ' ';
  331.         termBuf[len] = '\0';
  332.     }
  333.     PutStrAt(lastRow-1,0,termBuf);
  334.     PenPos(xcline,xccol); PlainPen();
  335. }
  336.  
  337. // read in a line of text. Editing is allowed and the input is echoed.
  338. int Terminal::GetLine (char* buffer,int bufsize,const char* terminators)
  339. {
  340.     int pos = 0;
  341.     int nchars = 0;
  342.     int ch;
  343.     // save base position
  344.     int xcline = cline;
  345.     int xccol  = ccol;
  346.     static char spaces[bufSize+2];
  347.  
  348.     memset(buffer,0,bufsize);    // clear out buffer
  349.     // determine max field width
  350.     int fieldwidth = (bufsize < (colms - xccol)
  351.              ? bufsize : (colms - xccol));
  352.     if (fieldwidth < 1) return(-1);    // field too small
  353.     // create a buffer of spaces (for clearing)
  354.     memset(spaces,' ',fieldwidth);
  355.     spaces[fieldwidth] = '\0';
  356.     // clear out field
  357.     PutStrAt(xcline,xccol,spaces);
  358.     PenPos(xcline,xccol);    // reset position
  359.     for (;;) {
  360.         ch = GetKey();    // get a key
  361.         switch (ch) {    // fan out based on key
  362.             case escCmd :     // Escape - flush buffer and start over
  363.                 memset(buffer,0,bufsize);
  364.                 PutStrAt(xcline,xccol,spaces);
  365.                 PenPos(xcline,xccol);
  366.                 nchars = 0;
  367.                 pos = 0;
  368.                 break;
  369.             case backCmd :    // backspace - delete character
  370.                 if (pos > 0) {
  371.                     strncpy(&buffer[pos-1],
  372.                         &buffer[pos],
  373.                         (nchars-pos)+1);
  374.                     PenPos(cline,ccol-1);
  375.                     put(&buffer[pos-1],
  376.                         (nchars-pos));
  377.                     put(' ');
  378.                     pos--; nchars--;
  379.                     PenPos(xcline,xccol+pos);
  380.                 } else Bell();
  381.                 break;
  382.             case deleCmd :    // delete line delete to BOF
  383.                 if (pos > 0) {
  384.                     strncpy(buffer,&buffer[pos],
  385.                         (nchars-pos)+1);
  386.                     PenPos(xcline,xccol);
  387.                     put(buffer,(nchars-pos));
  388.                     nchars -= pos;
  389.                     pos = 0;
  390.                     put(spaces,
  391.                         fieldwidth-nchars);
  392.                     PenPos(xcline,xccol+pos);
  393.                 } else Bell();
  394.                 break;
  395.             case rightCmd :    // right arrow - move right
  396.                 if (pos < nchars) {
  397.                     pos++;
  398.                     PenPos(xcline,xccol+pos);
  399.                 } else Bell();
  400.                 break;
  401.             case leftCmd :    // left arror - move left
  402.                 if (pos > 0) {
  403.                     PenPos(cline,ccol-1);
  404.                     pos--;
  405.                 } else Bell();
  406.                 break;
  407.             default :    // something else.
  408.                 // terminator??
  409.                 if (strchr(terminators,ch) != 0) {
  410.                     // yep.  reset pos and return
  411.                     PenPos(xcline,xccol);
  412.                     return(nchars);
  413.                 } else if (pos == nchars && // no room?
  414.                        nchars >= fieldwidth) Bell();
  415.                 else {
  416.                     if (ch < ' ') Bell();    // funny char?
  417.                     else {
  418.                         // normal charactor.  shove
  419.                         // into buffer and echo.
  420.                         buffer[pos] = ch;
  421.                         put(buffer[pos]);
  422.                         pos++;
  423.                         if (pos > nchars) nchars++;
  424.                     }
  425.                 }
  426.                 break;
  427.         }
  428.     }
  429. }
  430.  
  431. // put a character at some specified place
  432. void Terminal::PutCharAt (int row,int col,int ch)
  433. {
  434.     if (col >= colms) {
  435.         row++;
  436.         col = 0;
  437.     }
  438.     PenPos(row,col);
  439.     char cch = ch & charMask;
  440.     Mode m = (Mode) (ch & modeMask);
  441.     if (m == revsPen) RevsPen();
  442.     else if (m == defPen) PlainPen();
  443.     put(cch);
  444.     ccol++;
  445. }
  446.  
  447. // Put a plain string someplace
  448. void Terminal::PutStrAt (int row,int col,const char* str)
  449. {
  450.     int slen = strlen(str);
  451.     int mright = colms - (col+slen);
  452.     int right  = (mright < 0 ? -mright : 0);
  453.     int left   = slen - right;
  454.     if (left > 0) {
  455.         PenPos(row,col);
  456.         put((char*)str,left);
  457.         ccol += left;
  458.     }
  459.     if (right > 0) {
  460.         PenPos(row+1,0);
  461.         put((char*)(str+left),right);
  462.         ccol += right;
  463.     }
  464. }
  465.  
  466. // like above,  but this is a string of chars with attributes
  467. void Terminal::PutStrAt (int row,int col,const short* str)
  468. {
  469.     while (*str != 0) {
  470.         PutCharAt(row,col,(int)*str);
  471.         row = cline; col = ccol;
  472.         str++;
  473.     }
  474. }
  475.  
  476. //Put a plain string in a rectangluar area.  If the string is too long,
  477. //returns a pointer to the un-displayed tail of the string.  Returns
  478. //NULL (0) if the string fit.  Does dump wrapping (indicated with a '\'
  479. //and displayes non-printable characters with a '.'.
  480. char* Terminal::PutStrInBox(int row,int col,int width,int height,const char* str)
  481. {
  482.     int rrow = 0,rcol = 0;
  483.     PenPos(row,col);
  484.     for (char* p = (char*)str; *p != '\0'; p++) {
  485.         if (rcol == width) {
  486.             put('\\');
  487.             rcol = 0;
  488.             rrow++;
  489.             PenPos(row+rrow,col);
  490.         }
  491.         if (rrow >= height) return(p);
  492.         else if (*p == '\n') {
  493.             while (rcol++ < width) put(' ');
  494.             rcol = 0;
  495.             rrow++;
  496.             PenPos(row+rrow,col);
  497.         } else if (*p < ' ') {
  498.             put('.');
  499.             rcol++;
  500.             ccol++;
  501.         } else {
  502.             put(*p);
  503.             rcol++;
  504.             ccol++;
  505.         }
  506.     }
  507.     while (rrow < height) {
  508.         while (rcol < width) {
  509.             put(' ');
  510.             rcol++;
  511.             ccol++;
  512.         }
  513.         rcol = 0;
  514.         rrow++;
  515.         if (rrow < height) PenPos(row+rrow,col);
  516.     }
  517.     return(0);
  518. }
  519. #else
  520. // code output functions.
  521. static int tputc (char c) {return write(1,&c,1);}
  522. void Terminal::WriteCode(const char* code) {tputs(code,1,tputc);}
  523. void Terminal::WriteCodeLines(const char* code,int l) {tputs(code,l,tputc);}
  524.  
  525. // set cursor position
  526. void Terminal::PenPos (int row, int col)
  527. {
  528.     row = (row < 0 ? 0 : (row >= lines ? lines - 1 : row));
  529.     col = (col < 0 ? 0 : (col >= colms ? colms - 1 : col));
  530.     PosCode(termBuf,row+1,col+1);
  531.     WriteCode(termBuf);
  532.     cline = row; ccol = col;
  533. }
  534.  
  535. // get a terminal key press. This function will hang and wait for a key press
  536. int Terminal::GetKey ()
  537. {
  538.     char ch;    // input key
  539.     char buff[10];    // look ahead buffer
  540.  
  541.     for (int k = 0;;) {
  542.         // get a character
  543.         read(0,&ch,1);
  544.         buff[k++] = ch;        // save in buffer
  545.         // check posible magic keys
  546.         if (strncmp(buff,KD,k) == 0) {
  547.             if (KD[k] == 0) return downCmd;
  548.         } else if (strncmp(buff,KU,k) == 0) {
  549.             if (KU[k] == 0) return upCmd;
  550.         } else if (strncmp(buff,KR,k) == 0) {
  551.             if (KR[k] == 0) return rightCmd;
  552.         } else if (strncmp(buff,KL,k) == 0) {
  553.             if (KL[k] == 0) return leftCmd;
  554.         } else if (strncmp(buff,KH,k) == 0) {
  555.             if (KH[k] == 0) return escCmd;
  556.         } else if (strncmp(buff,KB,k) == 0) {
  557.             if (KB[k] == 0) return backCmd;
  558.         } else if (k > 1) { // no known prefix.  must be some other key
  559.             // sort of prefix - probably undefined function key
  560.             Bell();
  561.             k = 0;
  562.             continue;
  563.         // magic single character keys
  564.         } else if (ch == '\016') return downCmd;    // ^N
  565.         else if (ch == '\020') return upCmd;        // ^P
  566.         else if (ch == '\006') return rightCmd;        // ^F
  567.         else if (ch == '\002') return leftCmd;        // ^B
  568.         else if (ch == '\010') return backCmd;        // ^H
  569.         else if (ch == '\177') return backCmd;        // RUBOUT
  570.         else if (ch == '\025') return deleCmd;        // ^U
  571.         // plain key.
  572.         else return (int) ch;
  573.     }
  574. }
  575.  
  576. // check to see if a key has been pressed.  Does not hang.  Does not
  577. // actually read the key.
  578. Boolean Terminal::KeyAvailable()
  579. {
  580. #ifdef OSK
  581.     return(_gs_rdy(0) > 0);
  582. #endif
  583. }
  584.  
  585. // Error handler
  586. void Terminal::Error (ErrKind err,const char *msg)
  587. {
  588.     if (errFun != 0) (*errFun)(err,msg);
  589.     else {
  590.         if (err == sysErr) {
  591.             // fetch system error message for system errors
  592.             sprintf(termBuf,"Error: %s %s\n",strerror(errno),msg);
  593.             write(1,termBuf,strlen(termBuf));
  594.             Interrupt();
  595.         } else {
  596.             sprintf(termBuf,"Error: %s %s\n",
  597.                 (err == termErr ? "Terminal" : "Memory"),
  598.                 msg);
  599.             write(1,termBuf,strlen(termBuf));
  600.             Interrupt();
  601.         }
  602.     }
  603. }
  604.  
  605. // Write a string to the message line (bottom terminal line) in standout
  606. // mode
  607. void Terminal::Message (const char* msg)
  608. {
  609.     int lastRow = lines;
  610.     int xcline = cline;
  611.     int xccol  = ccol;
  612.     PenPos(lastRow,0); RevsPen();
  613.     strncpy(termBuf,msg,colms);
  614.     register len = strlen(msg);
  615.     if (len >= colms) {
  616.         termBuf[len = (colms-1)] = '\0';
  617.         while (len >= (colms-1) - 3)  termBuf[--len] = '.';
  618.     } else {
  619.         while (len < (colms-1)) termBuf[len++] = ' ';
  620.         termBuf[len] = '\0';
  621.     }
  622.     write(1,termBuf,len);
  623.     PenPos(xcline,xccol); PlainPen();
  624. }
  625.  
  626. // read in a line of text. Editing is allowed and the input is echoed.
  627. int Terminal::GetLine (char* buffer,int bufsize,const char* terminators)
  628. {
  629.     int pos = 0;
  630.     int nchars = 0;
  631.     int ch;
  632.     // save base position
  633.     int xcline = cline;
  634.     int xccol  = ccol;
  635.     static char spaces[bufSize];
  636.  
  637.     memset(buffer,0,bufsize);    // clear out buffer
  638.     // determine max field width
  639.     int fieldwidth = (bufsize < (colms - xccol)
  640.              ? bufsize : (colms - xccol));
  641.     if (fieldwidth < 1) return(-1);    // field too small
  642.     // create a buffer of spaces (for clearing)
  643.     memset(spaces,' ',fieldwidth);
  644.     // clear out field
  645.     write(1,spaces,fieldwidth);
  646.     PenPos(xcline,xccol);    // reset position
  647.     for (;;) {
  648.         ch = GetKey();    // get a key
  649.         switch (ch) {    // fan out based on key
  650.             case escCmd :     // Escape - flush buffer and start over
  651.                 memset(buffer,0,bufsize);
  652.                 PenPos(xcline,xccol);
  653.                 write(1,spaces,fieldwidth);
  654.                 PenPos(xcline,xccol);
  655.                 nchars = 0;
  656.                 pos = 0;
  657.                 break;
  658.             case backCmd :    // backspace - delete character
  659.                 if (pos > 0) {
  660.                     strncpy(&buffer[pos-1],
  661.                         &buffer[pos],
  662.                         (nchars-pos)+1);
  663.                     WriteCode(BC);
  664.                     write(1,&buffer[pos-1],
  665.                         (nchars-pos));
  666.                     write(1,spaces,1);
  667.                     pos--; nchars--;
  668.                     PenPos(xcline,xccol+pos);
  669.                 } else Bell();
  670.                 break;
  671.             case deleCmd :    // delete line delete to BOF
  672.                 if (pos > 0) {
  673.                     strncpy(buffer,&buffer[pos],
  674.                         (nchars-pos)+1);
  675.                     PenPos(xcline,xccol);
  676.                     write(1,buffer,(nchars-pos));
  677.                     nchars -= pos;
  678.                     pos = 0;
  679.                     write(1,spaces,
  680.                         fieldwidth-nchars);
  681.                     PenPos(xcline,xccol+pos);
  682.                 } else Bell();
  683.                 break;
  684.             case rightCmd :    // right arrow - move right
  685.                 if (pos < nchars) {
  686.                     pos++;
  687.                     PenPos(xcline,xccol+pos);
  688.                 } else Bell();
  689.                 break;
  690.             case leftCmd :    // left arror - move left
  691.                 if (pos > 0) {
  692.                     WriteCode(BC);
  693.                     pos--;
  694.                 } else Bell();
  695.                 break;
  696.             default :    // something else.
  697.                 // terminator??
  698.                 if (strchr(terminators,ch) != 0) {
  699.                     // yep.  reset pos and return
  700.                     PenPos(xcline,xccol);
  701.                     return(nchars);
  702.                 } else if (pos == nchars && // no room?
  703.                        nchars >= fieldwidth) Bell();
  704.                 else {
  705.                     if (ch < ' ') Bell();    // funny char?
  706.                     else {
  707.                         // normal charactor.  shove
  708.                         // into buffer and echo.
  709.                         buffer[pos] = ch;
  710.                         write(1,&buffer[pos],1);
  711.                         pos++;
  712.                         if (pos > nchars) nchars++;
  713.                     }
  714.                 }
  715.                 break;
  716.         }
  717.     }
  718. }
  719.  
  720. // put a character at some specified place
  721. void Terminal::PutCharAt (int row,int col,int ch)
  722. {
  723.     if (col >= colms) {
  724.         row++;
  725.         col = 0;
  726.     }
  727.     PenPos(row,col);
  728.     char cch = ch & charMask;
  729.     Mode m = (Mode) (ch & modeMask);
  730.     if (m == revsPen) RevsPen();
  731.     else if (m == defPen) PlainPen();
  732.     write(1,&cch,1);
  733.     ccol++;
  734. }
  735.  
  736. // Put a plain string someplace
  737. void Terminal::PutStrAt (int row,int col,const char* str)
  738. {
  739.     int slen = strlen(str);
  740.     int mright = colms - (col+slen);
  741.     int right  = (mright < 0 ? -mright : 0);
  742.     int left   = slen - right;
  743.     if (left > 0) {
  744.         PenPos(row,col);
  745.         write(1,(char*)str,left);
  746.         ccol += left;
  747.     }
  748.     if (right > 0) {
  749.         PenPos(row+1,0);
  750.         write(1,(char*)(str+left),right);
  751.         ccol += right;
  752.     }
  753. }
  754.  
  755. // like above,  but this is a string of chars with attributes
  756. void Terminal::PutStrAt (int row,int col,const short* str)
  757. {
  758.     while (*str != 0) {
  759.         PutCharAt(row,col,(int)*str);
  760.         row = cline; col = ccol;
  761.         str++;
  762.     }
  763. }
  764.  
  765. //Put a plain string in a rectangluar area.  If the string is too long,
  766. //returns a pointer to the un-displayed tail of the string.  Returns
  767. //NULL (0) if the string fit.  Does dump wrapping (indicated with a '\'
  768. //and displayes non-printable characters with a '.'.
  769. char* Terminal::PutStrInBox(int row,int col,int width,int height,const char* str)
  770. {
  771.     int rrow = 0,rcol = 0;
  772.     PenPos(row,col);
  773.     for (char* p = (char*)str; *p != '\0'; p++) {
  774.         if (rcol == width) {
  775.             write(1,"\\",1);
  776.             rcol = 0;
  777.             rrow++;
  778.             PenPos(row+rrow,col);
  779.         }
  780.         if (rrow >= height) return(p);
  781.         else if (*p == '\n') {
  782.             while (rcol++ < width) write(1," ",1);
  783.             rcol = 0;
  784.             rrow++;
  785.             PenPos(row+rrow,col);
  786.         } else if (*p < ' ') {
  787.             write(1,".",1);
  788.             rcol++;
  789.             ccol++;
  790.         } else {
  791.             write(1,p,1);
  792.             rcol++;
  793.             ccol++;
  794.         }
  795.     }
  796.     while (rrow < height) {
  797.         while (rcol < width) {
  798.             write(1," ",1);
  799.             rcol++;
  800.             ccol++;
  801.         }
  802.         rcol = 0;
  803.         rrow++;
  804.         if (rrow < height) PenPos(row+rrow,col);
  805.     }
  806.     return(0);
  807. }
  808. #endif    
  809.             
  810.             
  811.  
  812. // process forking - very O/S dependent and posible hairy...
  813. #ifdef OSK
  814.  
  815. extern char** environ;
  816.  
  817. typedef int (*ProcFunc)(const char* modname,int parmsize,const char *parmptr,
  818.              short type,short lang,int datasize,short prior,
  819.              short pathcnt);
  820.  
  821.  
  822. extern "MWC" {
  823.     int os9exec(ProcFunc procfunc,const char* modname,const char** argv,
  824.             const char** envp,unsigned stacksize,short priority,
  825.             short pathcnt);
  826. };
  827.  
  828.  
  829.  
  830.  
  831.  
  832. // fork a process
  833. int Terminal::forkprog (const char** argv)
  834. {
  835.     static char errorbuffer[512];
  836.     // remember where we are
  837.     int xcline = cline;
  838.     int xccol  = ccol;
  839.     // reset position and reset terminal
  840.     PenPos(lines-1,0);
  841.     PlainPen();
  842.     ExitChars();
  843.     _ss_opt(0, &ttym);
  844.     // try to fork the process
  845.     int pid = os9exec(os9forkc,argv[0],argv,environ,0,0,3);
  846.     // failure??
  847.     if (pid < 0) {
  848.         // yep.  revert terminal and spit out a message
  849.         int error = errno;
  850.         _ss_opt(0, &xttym);
  851.         InitChars();
  852.         cline = xcline; ccol = xccol;
  853.         sprintf(errorbuffer,"Error in fork: %s",strerror(error));
  854.         Message(errorbuffer);
  855.         errno = error;
  856.         return (-1);    // display not disturbed
  857.     } else {
  858.         // fork worked.  Wait for process to finish
  859.         int error = 0;
  860.         while (wait(&error) == 0) ;    // waiting...
  861.         error &= 0x00FFFF;        // completion status...
  862.         // revert terminal
  863.         _ss_opt(0, &xttym);
  864.         InitChars();
  865.         cline = xcline; ccol = xccol;
  866.         // did process crash??
  867.         if (error == 0) {
  868.             // nope
  869.             PenPos(xcline,xccol);
  870.             // success, but display might be trashed
  871.             return(1);
  872.         } else {
  873.             // child crashed.
  874.             // fetch error message
  875.             sprintf(errorbuffer,"Error in child: %s",
  876.                 strerror(error));
  877.             Message(errorbuffer);
  878.             // stash error code
  879.             errno = error;
  880.             // process crashed, but might have trashed the display
  881.             return (0);
  882.         }
  883.     }
  884. }
  885. #endif
  886. #ifdef MESSYDOS
  887. #include <process.h>
  888. #include <string.h>
  889. // fork a process
  890. int Terminal::forkprog (const char** argv)
  891. {
  892.     static char errorbuffer[512];
  893.     // remember where we are
  894.     int xcline = cline;
  895.     int xccol  = ccol;
  896.     // reset position and reset terminal
  897.     PenPos(lines-1,0);
  898.     PlainPen();
  899.     ExitChars();
  900.     // try to fork the process
  901.     int error = spawnvp(P_WAIT,argv[0],argv);
  902.     // failure??
  903.     error &= 0x00FFFF;        // completion status...
  904.     // revert terminal
  905.     InitChars();
  906.     cline = xcline; ccol = xccol;
  907.     // did process crash??
  908.     if (error == 0) {
  909.         // nope
  910.         PenPos(xcline,xccol);
  911.         // success, but display might be trashed
  912.         return(1);
  913.     } else if (error < 0) {
  914.         // child was stillborn
  915.         error = errno;
  916.         InitChars();
  917.         cline = xcline; ccol = xccol;
  918.         sprintf(errorbuffer,"Error in fork: %s",strerror(error));
  919.         Message(errorbuffer);
  920.         errno = error;
  921.         return (-1);    // display not disturbed
  922.     } else {
  923.         // child crashed.
  924.         // fetch error message
  925.         sprintf(errorbuffer,"Error in child: %s",
  926.             strerror(error));
  927.         Message(errorbuffer);
  928.         // stash error code
  929.         errno = error;
  930.         // process crashed, but might have trashed the display
  931.         return (0);
  932.     }
  933. }
  934.  
  935. #endif
  936.