home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 531.lha / Less_v1.4Z / src.LZH / src / io.c < prev    next >
C/C++ Source or Header  |  1991-07-03  |  18KB  |  613 lines

  1. /*  io.c */
  2.  
  3. #ifdef AMIGA
  4. /* Compile with -HPreHeader.q to get "less.h"! */
  5. #else
  6. #include "less.h"
  7. #endif
  8.  
  9. #include <ctype.h>
  10. #include <string.h>
  11. #include <intuition/intuition.h>
  12. #include <dos.h>
  13.  
  14.  
  15. #undef TRUE
  16. #undef FALSE
  17.  
  18. extern int sigs;
  19.  
  20. static struct  ConCom     *tty = NULL;
  21.  
  22. public int     nrow = 0;         /* Terminal size, rows.         */
  23. public int     ncol;             /* Terminal size, columns.      */
  24.  
  25. static struct Window *LessWindow;
  26.  
  27. int Wind_Spec[4] = { 0, 0, 0, 0 };
  28. /* User-supplied option specifying window size/position:
  29.  *
  30.  * [0]: Left edge X - coord.
  31.  * [1]: Top edge Y - coord.
  32.  * [2]: Right edge X - coord.
  33.  * [3]: Bottom edge Y - coord.
  34.  *
  35.  * If element 2 or 3 is negative, it is taken to be a relative value to be
  36.  * subtracted from the maximum screen size.  If the resulting window
  37.  * would be smaller than the minimum, it is quietly enlarged to the
  38.  * minimum, by expanding to the lower right as far as possible, and then
  39.  * moving the window toward the upper left if necessary.
  40.  */
  41. #define W_MINWIDTH 200
  42. #define W_MINHEIGHT 60
  43.  
  44. extern char version[];
  45.  
  46. /* Prototypes for functions defined in io.c */
  47.  
  48. static struct ConCom *OpenConsole __PROTO((struct Window *window));
  49. static void CloseConsole __PROTO((struct ConCom *tty));
  50. static void StartTtyTimer __PROTO((void));
  51. static void StartConRead __PROTO((struct ConCom *tty));
  52. static int ConGetC __PROTO((struct ConCom *tty));
  53. static int ConLookC __PROTO((struct ConCom *tty));
  54. static void ConWrite __PROTO((struct ConCom *tty,
  55.                               char *data,
  56.                               int length));
  57.  
  58.  
  59.  
  60. /****************************************************************/
  61. /* Code to open a console on an existing Intuition window.
  62.    See RKM Libraries & Devices, pp 277 ff
  63. */
  64.  
  65. #include <exec/memory.h>
  66. #include <devices/timer.h>
  67.  
  68. extern struct GfxBase *GfxBase;
  69. extern struct IOStdReq * CreateStdIO();
  70. extern struct MsgPort *CreatePort();
  71.  
  72.  
  73. struct ConCom
  74. {
  75.     struct IOStdReq *ConWriteReq; /* I/O write request */
  76.     struct IOStdReq *ConReadReq;  /* I/O read request */
  77.     struct MsgPort *RdReplyPort;  /* pointer to ReplyPort for console read */
  78.     struct timerequest *TimerMsg; /* If != NULL, timer is open */
  79. };
  80.  
  81.  
  82. static struct ConCom *OpenConsole ( struct Window *window );
  83. static void CloseConsole ( struct ConCom *tty );
  84. static void StartConRead ( struct ConCom *tty );
  85. static int ConGetC ( struct ConCom *tty );
  86. static int ConLookC ( struct ConCom *tty );
  87. static void ConWrite ( struct ConCom *c, char *data, int length );
  88.  
  89. static struct ConCom *OpenConsole (struct Window *window)
  90. {
  91.     struct ConCom *tty;
  92.     struct MsgPort *WrReplyPort, *TimerPort;
  93.  
  94.     if ( tty = (struct ConCom *)
  95.         AllocMem(sizeof(struct ConCom), MEMF_CLEAR | MEMF_PUBLIC) )
  96.     {
  97.  
  98.         if ( (WrReplyPort = CreatePort(0, 0)) /* reply port for write */
  99.  
  100.             && (tty->RdReplyPort = CreatePort(0, 0)) /* reply port for read */
  101.  
  102.             && (tty->ConWriteReq = CreateStdIO ( WrReplyPort ))
  103.  
  104.             && (tty->ConReadReq = CreateStdIO ( tty->RdReplyPort)) )
  105.         {
  106.             tty->ConWriteReq->io_Data = (APTR) window;
  107.             tty->ConWriteReq->io_Length = sizeof(struct Window);
  108.  
  109.             if ( !(OpenDevice ( "console.device", 0, tty->ConWriteReq, 0 )) )
  110.             {
  111.                 tty->ConReadReq->io_Device = tty->ConWriteReq->io_Device;
  112.                 tty->ConReadReq->io_Unit = tty->ConWriteReq->io_Unit;
  113.  
  114.                 /* Try to set up regular interrupts for ^C check */
  115.                 TimerPort = CreatePort(NULL, 0);
  116.                 if (
  117.                     TimerPort
  118.                     && ( tty->TimerMsg = (struct timerequest *)
  119.                         CreateExtIO(TimerPort, sizeof(struct timerequest)) )
  120.                     && !OpenDevice (TIMERNAME, UNIT_VBLANK,
  121.                         (struct IORequest *)(tty->TimerMsg), 0)
  122.                    )
  123.                     return tty; /* All ok! */
  124.                 /* Main console ports ok, but no timer.  We'll live
  125.                    without it...
  126.                 */
  127.                 if ( TimerPort ) DeletePort ( TimerPort );
  128.                 if ( tty->TimerMsg )
  129.                     DeleteExtIO( (struct IORequest *)(tty->TimerMsg),
  130.                         (long)sizeof(struct timerequest) );
  131.                 tty->TimerMsg = NULL;
  132.                 return tty;
  133.             }
  134.         }
  135.  
  136.         /* If get here, something went wrong */
  137.         if ( tty->ConReadReq ) DeleteStdIO(tty->ConReadReq);
  138.         if ( tty->RdReplyPort ) DeletePort(tty->RdReplyPort);
  139.         if ( tty->ConWriteReq ) DeleteStdIO(tty->ConWriteReq);
  140.         if ( WrReplyPort ) DeletePort(WrReplyPort);
  141.     }
  142.     if ( tty )
  143.         FreeMem(tty, sizeof(struct ConCom));
  144.     return NULL;
  145.  
  146. }
  147.  
  148. static void CloseConsole (struct ConCom *tty)
  149. {
  150.     struct MsgPort *mp;
  151.  
  152.     AbortIO(tty->ConReadReq);    /* Abort any read in progress */
  153.     CloseDevice(tty->ConWriteReq);   /* close console device */
  154.  
  155.     mp = tty->ConWriteReq->io_Message.mn_ReplyPort;
  156.  
  157.     DeleteStdIO(tty->ConWriteReq);
  158.     DeletePort(mp);
  159.  
  160.     mp = tty->ConReadReq->io_Message.mn_ReplyPort;
  161.  
  162.     DeleteStdIO(tty->ConReadReq);
  163.     DeletePort(mp);
  164.  
  165.     if (tty->TimerMsg)
  166.     {
  167.         AbortIO((struct IORequest *)(tty->TimerMsg));
  168.         CloseDevice((struct IORequest *)(tty->TimerMsg));
  169.         DeletePort ( tty->TimerMsg->tr_node.io_Message.mn_ReplyPort );
  170.         DeleteExtIO ( (struct IORequest *)(tty->TimerMsg),
  171.             (long)sizeof(struct timerequest) );
  172.     }
  173.  
  174.     FreeMem(tty, sizeof(struct ConCom));
  175.     return;
  176. }
  177.  
  178. /*  Request a timer interrupt in 0.25 seconds.  Use this to poll for ^C */
  179.  
  180. static void StartTtyTimer (void)
  181. {
  182.     if ( !tty->TimerMsg ) return;
  183.     tty->TimerMsg->tr_node.io_Command = TR_ADDREQUEST;
  184.     tty->TimerMsg->tr_time.tv_secs = 0;
  185.     tty->TimerMsg->tr_time.tv_micro = 250000L;
  186.     SendIO ( (struct IORequest *)(tty->TimerMsg) );
  187. }
  188.  
  189. static char buffer; /* buffer for incoming console character */
  190.  
  191. /* asynchronous console read request--must be called once before
  192.    any ConGetC requests
  193. */
  194.  
  195. static void StartConRead (struct ConCom *tty)
  196. {
  197.     struct IOStdReq *conr;
  198.  
  199.     conr = tty->ConReadReq;
  200.     conr->io_Command = CMD_READ;
  201.     conr->io_Length = 1;
  202.     conr->io_Data = (APTR) &buffer;
  203.     SendIO(conr);   /* asynchronous posting of a read request */
  204. }
  205.  
  206. /* Get a character from the console.
  207. */
  208. static int ConGetC (struct ConCom *tty)
  209. {
  210.     struct MsgPort *mp, *tp;
  211.     struct IOStdReq *rddata;
  212.     int temp;
  213.     ULONG ReadMsgBit, ClockMsgBit, MsgBits;
  214.  
  215.     mp = tty->RdReplyPort;    /* get the read reply port */
  216.     ReadMsgBit = 1 << mp->mp_SigBit;
  217.     if ( tty->TimerMsg )
  218.     {
  219.         tp = tty->TimerMsg->tr_node.io_Message.mn_ReplyPort;
  220.         ClockMsgBit = 1 << tp->mp_SigBit;
  221.     }
  222.     else ClockMsgBit = 0;
  223.     rddata = NULL;
  224.     do /* Wait for a character.  Wake up periodically so that ^C works */
  225.     {
  226.         MsgBits = Wait(ReadMsgBit | ClockMsgBit);
  227.         if ( MsgBits & ReadMsgBit )
  228.             rddata = (struct IOStdReq *) GetMsg ( mp );
  229.         if ( MsgBits & ClockMsgBit )
  230.             if ( GetMsg (tp) )
  231.             {
  232.                 StartTtyTimer();
  233.                 chkabort();
  234.             }
  235.     } while ( !rddata );
  236.  
  237.     /* We've got a character... */
  238.     temp = buffer;   /* get the character */
  239.     StartConRead ( tty ); /* set up next read */
  240.     return temp;
  241. }
  242.  
  243. /* See if a character is available at the console.
  244.    If so, get it, else return -1.
  245. */
  246. static int ConLookC (struct ConCom *tty)
  247. {
  248.     struct MsgPort *mp;
  249.     struct IOStdReq *rddata;
  250.     int temp;
  251.  
  252.     mp = tty->RdReplyPort;    /* get the read reply port */
  253.     rddata = (struct IOStdReq *) GetMsg ( mp );
  254.     if ( !rddata ) return -1;
  255.  
  256.     /* We've got a character... */
  257.     temp = buffer;   /* get the character */
  258.     StartConRead ( tty ); /* set up next read */
  259.     return temp;
  260. }
  261.  
  262. /* write a specified number of characters from a buffer to console device
  263. */
  264. static void ConWrite (struct ConCom *tty, char *data, int length)
  265. {
  266.     struct IOStdReq *wrdata;
  267.  
  268.     wrdata = tty->ConWriteReq;
  269.     wrdata->io_Command = CMD_WRITE;
  270.     wrdata->io_Length = length;
  271.     wrdata->io_Data = (APTR) data;
  272.     DoIO(wrdata); /* waits until write completes before continuing */
  273. }
  274.  
  275. /****************************************************************/
  276.  
  277.  
  278.  
  279. /*
  280.  * This routine gets called once, to set up the
  281.  * terminal channel.
  282.  */
  283. void ttopen (void)
  284. {
  285.         int wl, wt;   /* window upper left corner specification */
  286.         static struct Screen __aligned WBScreen, *wbsdata;
  287.         static int IsV2;
  288.         static struct NewWindow NewLessWindow =
  289.         {
  290.             0, 0, 640, 200,     /* position & size (may be changed) */
  291.             -1, -1,             /* pens */
  292.             0,                  /* IDCMP */
  293.             WINDOWDEPTH | WINDOWSIZING | WINDOWDRAG | WINDOWCLOSE | ACTIVATE
  294.                 | SMART_REFRESH | NOCAREREFRESH,
  295.             NULL, NULL,         /* Gadgets, Checkmark */
  296.             "Less 1.4Z:  h for help",
  297.             NULL, NULL,         /* Use WB screen */
  298.             W_MINWIDTH, W_MINHEIGHT, -1, -1,
  299.             WBENCHSCREEN
  300.         };
  301.  
  302.         if (tty) return;
  303.  
  304.  
  305.         if (GfxBase =  /* V2.0 + ? */
  306.             (struct GfxBase *)OpenLibrary("graphics.library", 36) )
  307.         {
  308.             CloseLibrary ( GfxBase );
  309.             IsV2 = 1;
  310.             if ( !(wbsdata = LockPubScreen("Workbench")) )
  311.             {
  312.                 error ( "Can't find Workbench screen" );
  313.                 quit();
  314.             }
  315.         }
  316.         else
  317.         {
  318.             IsV2 = 0;
  319.             if ( !GetScreenData ( &WBScreen, sizeof(struct Screen),
  320.                 WBENCHSCREEN, NULL ) )
  321.             {
  322.                 error ( "Can't find Workbench screen" );
  323.                 quit();
  324.             }
  325.             wbsdata = &WBScreen;
  326.         }
  327.         if ( (wl = Wind_Spec[0]) < 0 ) wl += wbsdata->Width;
  328.         if ( wl < 0 ) wl = 0;
  329.         if ( wl > wbsdata->Width )
  330.             wl = wbsdata->Width - W_MINWIDTH;
  331.         if ( (wt = Wind_Spec[1]) < 0 ) wt += wbsdata->Height;
  332.         if ( wt < 0 ) wt = 0;
  333.         if ( wt > wbsdata->Height )
  334.             wt = wbsdata->Height - W_MINHEIGHT;
  335.         if ( wl < 0 || wt < 0 )
  336.         {
  337.             error ( "Window won't fit on screen" );
  338.             quit();
  339.         }
  340.         NewLessWindow.LeftEdge = wl;
  341.         NewLessWindow.TopEdge = wt;
  342.  
  343.         NewLessWindow.Width = Wind_Spec[2];
  344.         if ( NewLessWindow.Width <= 0 )
  345.             NewLessWindow.Width += wbsdata->Width;
  346.         if ( NewLessWindow.Width <= 0 )
  347.             NewLessWindow.Width = 0;
  348.         NewLessWindow.Height = Wind_Spec[3];
  349.         if ( NewLessWindow.Height <= 0 )
  350.             NewLessWindow.Height += wbsdata->Height;
  351.         if ( NewLessWindow.Height <= 0 )
  352.             NewLessWindow.Height = 0;
  353.  
  354.         if ( NewLessWindow.Width < W_MINWIDTH )
  355.             NewLessWindow.Width = W_MINWIDTH;
  356.         if ( NewLessWindow.Height < W_MINHEIGHT )
  357.             NewLessWindow.Height = W_MINHEIGHT;
  358.  
  359.         if ( NewLessWindow.LeftEdge + NewLessWindow.Width > wbsdata->Width )
  360.             NewLessWindow.Width = wbsdata->Width - NewLessWindow.LeftEdge;
  361.         if ( NewLessWindow.TopEdge + NewLessWindow.Height > wbsdata->Height )
  362.             NewLessWindow.Height = wbsdata->Height - NewLessWindow.TopEdge;
  363.         if ( NewLessWindow.Width < W_MINWIDTH )
  364.             NewLessWindow.LeftEdge = wbsdata->Width - W_MINWIDTH;
  365.         if ( NewLessWindow.Height < W_MINHEIGHT )
  366.             NewLessWindow.TopEdge = wbsdata->Height - W_MINHEIGHT;
  367.         if ( NewLessWindow.LeftEdge < 0 || NewLessWindow.TopEdge < 0 )
  368.         {
  369.             error ( "Window won't fit on screen" );
  370.             quit();
  371.         }
  372.  
  373.         if (IsV2)
  374.             UnlockPubScreen(NULL, wbsdata);
  375.         if (!(LessWindow = (struct Window *) OpenWindow (&NewLessWindow)) )
  376.         {
  377.             error ( "Can't open Less window" );
  378.             quit();
  379.         }
  380.         if (!(tty = OpenConsole(LessWindow)))
  381.         {
  382.             error ( "Can't open console device" );
  383.             quit();
  384.         }
  385.         StartConRead ( tty );
  386.         if ( tty->TimerMsg ) StartTtyTimer();
  387.         /* enable report of window resizeing and close gadget */
  388.         ConWrite(tty, "\x9b\x31\x32;11{", 7);
  389. }
  390.  
  391. /* Request size of window information
  392. */
  393. void getrowcol (void)
  394. {
  395.     static unsigned char buf[16], *p;
  396.  
  397.     if ( !tty) ttopen();
  398.     ConWrite(tty, "\x9b\x30\x20\x71", 4); /* request current window size */
  399.     p = buf;
  400.     while ( (*p = ConGetC(tty)) != 0x9b ) /* nothing */;
  401.     p++;
  402.     do
  403.     {
  404.         *p = ConGetC(tty);
  405.     } while ( p < &(buf[15]) && *p++ != '\x72' );
  406.     *p = '\0';
  407.     /* buf has "En;n;n;n|", where E is 0x9b, n is a decimal number */
  408.  
  409.     p = buf+1;
  410.     do
  411.     {
  412.         if ( p = strchr ( p, ';' ) ) p++; /* skip window position */
  413.         else break;
  414.         if ( p = strchr ( p, ';' ) ) p++;
  415.         else break;
  416.         nrow = atoi ( p );   /* window height */
  417.         if ( p = strchr ( p, ';' ) ) p++;
  418.         else break;
  419.         ncol = atoi ( p );   /* window width */
  420.     }   while (0); /* just once! */
  421.  
  422.     /* arbitrary data integrity checks: */
  423.     if ( nrow < 2 || nrow > 99 || ncol < 10 || ncol > 200 )
  424.     {
  425.         /* Window probably too small for the font chosen.  We may not
  426.            be able to even write an error message in the window! */
  427.         MyRequester ( "Screen/Font mismatch" );
  428.         quit();
  429.     }
  430. }
  431.  
  432.  
  433.  
  434. /*
  435.  * This function gets called just
  436.  * before we go back home to the command interpreter.
  437.  * On the Amiga it closes up the virtual terminal window.
  438.  */
  439. void ttclose (void)
  440. {
  441.         if (tty != (struct ConCom *) 0L) {
  442.                 CloseConsole(tty);
  443.                 CloseWindow(LessWindow);
  444.         }
  445.         tty = NULL;
  446.         nrow = 0;
  447. }
  448.  
  449.  
  450. /*
  451.  * Read a character from the terminal,
  452.  * performing no editing and doing conditional echo
  453.  * but interpretting window resize and close gadget reports
  454.  */
  455. int do_echo = 1; /* echo flag */
  456.  
  457. int ttgetc (void)
  458. {
  459.         unsigned char c;        /* must be unsigned! */
  460.  
  461.  
  462.         while ( (c = ConGetC(tty)) == '\x9b' )
  463.         {
  464.             switch (c = ConGetC(tty))
  465.             {
  466.             case '1': /* raw input event */
  467.                 if ( (c = ConGetC(tty)) == '1' )
  468.                     quit();  /* close gadget */
  469.                 else if ( c == '2' )  /* window resize */
  470.                 {
  471.                     while ( (c = ConGetC(tty)) != '|') /* nothing */;
  472.                     winch();
  473.                 }
  474.                 break;
  475.             case '?': /* Help key */
  476.                 if ( (c = ConGetC(tty)) == '~' ) return 'h';
  477.                 break;
  478.             case 'A':
  479.             case 'T': /* Arrow up */
  480.                 c = 'b';
  481.                 break;
  482.             case 'B':
  483.             case 'S': /* Arrow down */
  484.                 c = ' ';
  485.                 break;
  486.             case 'D': /* Arrow left */
  487.                 c = 'k';
  488.                 break;
  489.             case 'C': /* Arrow right */
  490.                 c = '\r';
  491.                 break;
  492.             case ' ': /* Shifted left or right */
  493.                 if ( (c = ConGetC(tty)) == 'A' ) c = 'u';
  494.                 else c = 'd';
  495.                 break;
  496.             default:
  497.                 continue;
  498.             }
  499.             break;
  500.         }
  501.         if ( c == 3 )
  502.         {
  503.             sigs |= 01;
  504.             psignals();
  505.             /* no return */
  506.         }
  507.         if (do_echo)
  508.                 ttwrite(&c, 1);
  509.  
  510.         return ((int) c);
  511. }
  512.  
  513. /*
  514.  * Check for ^C in the tty window.  This routine will throw
  515.  * away any characters waiting in the tty input buffer.  Returns when
  516.  * there's nothing in the input queue or one of the following has been
  517.  * recognized:
  518.  *
  519.  *  Close gadget    (exits)
  520.  *  Window resize   (resets window and returns)
  521.  *  ^C              (sets sigs and returns)
  522.  */
  523. int chk_sigs (void)
  524. {
  525.     int c;
  526.  
  527.     for (;;)
  528.     {
  529.         if ( (c = ConLookC(tty)) < 0 ) return sigs;
  530.  
  531.         switch ( c )
  532.         {
  533.         case '\x9b': /* raw input event */
  534.             if ( (c = ConGetC(tty)) != '1' ) break; /* unexpected raw input */
  535.             if ( (c = ConGetC(tty)) == '1' )
  536.                 quit();  /* close gadget */
  537.             else if ( c == '2' )  /* window resize */
  538.             {
  539.                 while ( (c = ConGetC(tty)) != '|') /* nothing */;
  540.                 winch();
  541.                 return sigs;
  542.             }
  543.             break;
  544.         case 3:
  545.             sigs |= 01;
  546.             return sigs;
  547.         }
  548.     }
  549. }
  550.  
  551.  
  552.  
  553. /*
  554.  * Write a buffer of characters to the display.
  555.  */
  556. void ttwrite (char *buffer, int length)
  557. {
  558.         ConWrite ( tty, buffer, length );
  559. }
  560.  
  561. /*
  562.  * Write a string to the terminal
  563.  */
  564. void ttputs (char *s)
  565. {
  566.         ConWrite ( tty, s, strlen(s) );
  567. }
  568.  
  569. /* fake termcap output */
  570. /* This takes the place of the original tputs(x,y,z), using only the
  571.    first parameter
  572.  */
  573. void Tputs (char *s)
  574. {
  575.         flush();
  576.         if ( s ) ConWrite ( tty, s, strlen(s) );
  577. }
  578.  
  579. /*
  580.  * We may not have a window: display a message in a requester
  581.  */
  582. int MyRequester (char *s)
  583. {
  584.     static struct IntuiText __aligned
  585.     Ok =
  586.     {
  587.         AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
  588.         AUTOLEFTEDGE, AUTOTOPEDGE,
  589.         AUTOITEXTFONT,
  590.         "Ok!",
  591.         AUTONEXTTEXT
  592.     },
  593.     IMsg =
  594.     {
  595.         AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
  596.         AUTOLEFTEDGE, AUTOTOPEDGE + 20, /* Hope this is enough for default font */
  597.         AUTOITEXTFONT,
  598.         "",
  599.         AUTONEXTTEXT
  600.     },
  601.     LessMsg =
  602.     {
  603.         AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
  604.         AUTOLEFTEDGE, AUTOTOPEDGE,
  605.         AUTOITEXTFONT,
  606.         "Less 1.4Z:",
  607.         &IMsg
  608.     };
  609.  
  610.     IMsg.IText = s;
  611.     return (int)AutoRequest(NULL, &LessMsg, &Ok, &Ok, NULL, NULL, 250, 80);
  612. }
  613.