home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff225.lzh / AmigaTCP / src / newtelnetp.c < prev    next >
C/C++ Source or Header  |  1989-06-24  |  15KB  |  742 lines

  1. #include <stdio.h>
  2. #include <exec/types.h>
  3. #include <exec/nodes.h>
  4. #include <exec/lists.h>
  5. #include <exec/tasks.h>
  6. #include <exec/ports.h>
  7. #include <exec/libraries.h>
  8. #include <exec/io.h>
  9. #include <exec/devices.h>
  10. #include <exec/errors.h>
  11. #include <proto/exec.h>
  12. #include <devices/console.h>
  13. #include <libraries/dos.h>
  14. #include <libraries/dosextens.h>
  15. #include <intuition/intuition.h>
  16. #include <dos.h>
  17. #include "machdep.h"
  18. #include "mbuf.h"
  19. #include "timer.h"
  20. #include "internet.h"
  21. #include "icmp.h"
  22. #include "netuser.h"
  23. #include "tcp.h"
  24. #include "telnet.h"
  25. #include "session.h"
  26. #include "inetdev.h"
  27. #include "inetlib.h"
  28. #define DEBUG
  29. struct Process *mytask;
  30. APTR    oldwindowptr;
  31. struct IntuitionBase *IntuitionBase;
  32. char banner[80] = "telnet window";
  33. static struct NewWindow nw = {
  34.     0, 0, 640, 200,        /* left, top, (max) width, (max) height */
  35.     0, 1,            /* detail pen, block pen */
  36.     0,            /* IDCMP flags */
  37.     SMART_REFRESH | WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING |
  38.         SIZEBBOTTOM | ACTIVATE | NOCAREREFRESH,    /* window flags */
  39.     NULL, NULL,        /* gadget, checkmark */
  40.     (UBYTE *)&banner[0],    /* title of window */
  41.     NULL, NULL,        /* screen, bitmap */
  42.     200, 50, -1, -1,    /* sizing limits */
  43.     WBENCHSCREEN,        /* on the workbench */
  44. };
  45. struct Window *win;
  46. struct MsgPort *keyboard, *consinp, *consoutp, *tcpinp, *tcpoutp;
  47. struct IOStdReq consin, consout;
  48. char InputCharacter;
  49. int deviceopened = 0;
  50. struct IOINETReq tnreq, tninreq, tnoutreq;
  51. char recv[512], snd[512], recvbuf[512], tcpinbuf[512];
  52. struct telnet *tn;
  53. #ifdef LATTICE
  54. extern struct { short error; char *msg; } os_errlist[];
  55. extern int _OSERR, os_nerr;
  56. #endif
  57. static
  58. /*
  59.  * wait for something to happen
  60.  */
  61. eihalt()
  62. {
  63.     register struct IntuiMessage *msg;
  64.     static ULONG mask = 0;
  65.  
  66.     if (mask == 0L)
  67.         mask =     1L << consinp->mp_SigBit |
  68.             1L << tcpinp->mp_SigBit;
  69.  
  70.     (void) Wait(mask);
  71. }
  72. clean(why)
  73.     char *why;
  74. {
  75.     int i;
  76.  
  77.     if (win)
  78.         CloseWindow(win);
  79.     if (consinp)
  80.         DeletePort(consinp);
  81.     if (consoutp)
  82.         DeletePort(consoutp);
  83.     if (tcpinp)
  84.         DeletePort(tcpinp);
  85.     if (tcpoutp)
  86.         DeletePort(tcpoutp);
  87.     if (deviceopened)
  88.         CloseDevice(&tnreq);
  89.     mytask->pr_WindowPtr = oldwindowptr;
  90.     if (why) {
  91.            myoserr(why);
  92.     }
  93.     exit(0);
  94. }
  95. printlist(l)
  96. struct List *l;
  97. {
  98.   printf("head %x tail %x tailpred %x\n", l->lh_Head, l->lh_Tail, 
  99.         l->lh_TailPred);
  100. }
  101. myoserr(why)
  102. char *why;
  103. {
  104.   int i;
  105.         fprintf(stderr, "%s: ", why); 
  106. #ifdef LATTICE
  107.         fprintf(stderr, "%d: ", _OSERR);
  108.  
  109.         for(i = 0; os_errlist[i].error < os_nerr; i++)
  110.           if (os_errlist[i].error == _OSERR)
  111.             fprintf(stderr, os_errlist[i].msg);
  112. #endif
  113.         fprintf(stderr, "\r\n");
  114. }
  115. /* Called at startup time to set up console I/O, memory heap */
  116. ioinit()
  117. {
  118.     struct Screen *scr;
  119.  
  120.     mytask = (struct Process *) FindTask((char *) NULL);
  121.     oldwindowptr = mytask->pr_WindowPtr;
  122.     mytask->pr_WindowPtr = (APTR) -1;    /* disable DOS requestors */
  123.  
  124.     if ((IntuitionBase = (struct IntuitionBase *)
  125.        OpenLibrary("intuition.library", 33L)) == NULL)
  126.         clean("No intuition: Version 1.2 of Amiga Systems Software required");
  127.     /*
  128.      *  Try to determine the size of the workbench screen
  129.      */
  130.     scr = malloc(sizeof(struct Screen));
  131.     if (scr==NULL)
  132.         clean("Can't alloc screen");
  133.  
  134.     if (GetScreenData(scr, (ULONG) sizeof(struct Screen),
  135.               WBENCHSCREEN, NULL) == TRUE) {
  136.         nw.Width = scr->Width;
  137.         nw.Height = scr->Height-20;
  138.         nw.TopEdge = 19;
  139.     } else
  140.         fprintf(stderr, "Can't GetScreenData()\n");
  141.  
  142.     free((char *)scr);
  143.     if ((win = OpenWindow(&nw)) == NULL)
  144.         clean("Can't open window");
  145.     if ((consinp = CreatePort("telnet:console in", 0L)) == NULL)
  146.         clean("Can't create console port");
  147.     if ((tcpinp = CreatePort("telnet:tcp in", 0L)) == NULL)
  148.         clean("Can't create telnet tcp input port");
  149.     if ((tcpoutp = CreatePort("telnet:tcp out", 0L)) == NULL)
  150.         clean("Can't create telnet tcp output port");
  151.  
  152.     consin.io_Data = (APTR) win;
  153.     consin.io_Length = sizeof(struct Window);
  154.  
  155.     _OSERR = OpenDevice("console.device", 0L, &consin, 0L);
  156.     if (_OSERR != 0L){
  157.         printf("opendevice returned %d\n", _OSERR);
  158.         myoserr("could not get console");
  159.         clean("Can't open console device");
  160.     }
  161.     consout = consin;
  162.  
  163.     consin.io_Message.mn_ReplyPort = consinp;
  164.     consin.io_Length = 512;
  165.     consin.io_Data = (APTR) recvbuf;
  166.     consin.io_Command = CMD_READ;
  167.     SendIO(&consin);
  168.     consout.io_Message.mn_ReplyPort = consoutp;
  169.     consout.io_Command = CMD_WRITE;
  170.  
  171.  
  172. }
  173. /* Read characters from the keyboard, translating them to "real" ASCII
  174.  * If none are ready, return the -1 from kbraw()
  175.  */
  176. kbread()
  177. {
  178.     char c;
  179.     int amount;
  180.  
  181.     if (CheckIO(&consin)) {
  182.         WaitIO(&consin);
  183.         amount = consin.io_Actual;
  184.         if (amount > 512)
  185.           amount = 511;
  186.         strncpy(recv, recvbuf, amount);
  187.         SendIO(&consin);
  188.         return (amount);
  189.     }
  190.  
  191.     return -1;        /* nuthin here */
  192. }
  193. extern char nospace[];
  194. int refuse_echo = 0;
  195. int unix_line_mode = 0;    /* if true turn <cr> to <nl> when in line mode */
  196.  
  197. #ifdef    DEBUG
  198. char *t_options[] = {
  199.     "Transmit Binary",
  200.     "Echo",
  201.     "",
  202.     "Suppress Go Ahead",
  203.     "",
  204.     "Status",
  205.     "Timing Mark"
  206. };
  207. #endif
  208. /* Telnet receiver upcall routine */
  209. void
  210. rcv_char()
  211. {
  212.     tel_input(tn,tninreq.io_Data, tninreq.io_Actual);
  213.     fflush(stdout);
  214. }
  215. brk()
  216. {
  217.   clean("ok i iwll quit\n");
  218. }
  219. /* TCP connection states */
  220. char *tcpstates[] = {
  221.     "Closed",
  222.     "Listen",
  223.     "SYN sent",
  224.     "SYN received",
  225.     "Established",
  226.     "FIN wait 1",
  227.     "FIN wait 2",
  228.     "Close wait",
  229.     "Closing",
  230.     "Last ACK",
  231.     "Time wait"
  232. };
  233. /* TCP segment header flags */
  234. char *tcpflags[] = {
  235.     "FIN",    /* 0x01 */
  236.     "SYN",    /* 0x02 */
  237.     "RST",    /* 0x04 */
  238.     "PSH",    /* 0x08 */
  239.     "ACK",    /* 0x10 */
  240.     "URG"    /* 0x20 */
  241. };
  242.  
  243. /* TCP closing reasons */
  244. char *reasons[] = {
  245.     "Normal",
  246.     "Reset",
  247.     "Timeout",
  248.     "ICMP"
  249. };
  250. char old = LISTEN;
  251. int done = 0;
  252. char *hostname="";
  253. int hostport=0;
  254. char *bannerfmt = "telnet %10s %4d %10s";
  255. void
  256. showstate(old, new)
  257.     char old, new;
  258. {
  259.  
  260.  
  261.     /* Can't add a check for unknown connection here, it would loop
  262.      * on a close upcall! We're just careful later on.
  263.      */
  264.  
  265.     sprintf(banner, bannerfmt, hostname, hostport, tcpstates[new]);
  266.     SetWindowTitles(win, banner, -1);
  267.     switch(new){
  268.     case CLOSE_WAIT:
  269.         done = 1;
  270.         break;
  271.     case CLOSED:    /* court adjourned */
  272.         done = 1;
  273.         break;
  274.     default:
  275.         break;
  276.     }
  277.     fflush(stdout);
  278.  
  279. }
  280.  
  281. /* Execute user telnet command */
  282. main(argc,argv)
  283. int argc;
  284. char *argv[];
  285. {
  286.     extern int _OSERR;
  287.     struct InternetBase *InternetBase;
  288.     int send_tel();
  289.         int unix_send_tel();
  290.     struct session *s;
  291.     int amount;
  292.     struct socket lsocket,fsocket;
  293.     ioinit();
  294.     hostname = argv[1];
  295.     old = LISTEN;
  296.     showstate(old, LISTEN);
  297.     tnreq.io_fsocket.address = aton(argv[1]);
  298.     if(argc < 3)
  299.         tnreq.io_fsocket.port = TELNET_PORT;
  300.     else
  301.         tnreq.io_fsocket.port = atoi(argv[2]);
  302.     tnreq.io_Device = NULL;
  303.     tnreq.io_Unit = NULL;
  304.     tnreq.io_Flags = 0;
  305.     tnreq.io_Error = 0;
  306.     InternetBase = (struct InternetBase *) OpenDevice("internet.device",
  307.                 (long) INET_UNIT_TCP, &tnreq, 0L);
  308.     if (InternetBase != 0L){
  309.       printf("it did not open %d\n",InternetBase);
  310.       clean("i quit");
  311.     }
  312.     hostport = tnreq.io_lsocket.port;
  313.     deviceopened = 1;
  314.     tninreq = tnreq; /* possible lettuce bug  ?*/
  315.     tnoutreq = tnreq;
  316.     tninreq.io_Length = 512;
  317.     tnoutreq.io_Length = 1;
  318.     tninreq.io_Data = tcpinbuf;
  319.     tnoutreq.io_Data = snd;
  320.     tnoutreq.io_Length = 0;
  321.     tninreq.io_Command = CMD_READ;
  322.     tnoutreq.io_Command = CMD_WRITE;
  323.     tninreq.io_Message.mn_ReplyPort = tcpinp;
  324.     tnoutreq.io_Message.mn_ReplyPort = tcpoutp;
  325.     /* Create and initialize a Telnet protocol descriptor */
  326.     if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
  327.         myoserr("calloc faiuled\n");
  328.         goto done;
  329.     }
  330.     tn->session = s;    /* Upward pointer */
  331.     tn->state = TS_DATA;
  332.         SendIO(&tninreq);
  333.     onbreak(&brk);
  334.     InputCharacter = ' ';
  335.     while (! done)
  336.       {
  337.         eihalt();
  338.         if ((amount = kbread()) >= 0){
  339.           
  340.           unix_send_tel(recv, amount);}
  341.  
  342.         if (CheckIO(&tninreq))
  343.           {
  344.         chkabort();
  345.         WaitIO(&tninreq);
  346.             rcv_char();
  347.             if (tninreq.io_State != old)
  348.               {
  349.             showstate(old, tninreq.io_State);
  350.             old = tninreq.io_State;
  351.               }
  352.                 SendIO(&tninreq);
  353.           }
  354.       }
  355. done:
  356.     clean("All done");          
  357.  
  358. }
  359.  
  360. /* Process typed characters */
  361. int
  362. unix_send_tel(buf,n)
  363. char *buf;
  364. int16 n;
  365. {
  366.     int i;
  367.     for (i=0; (i<n) && (buf[i] != '\r'); i++)
  368.         ;
  369.     if (buf[i] == '\r') {
  370.         buf[i] = '\n';
  371.         n = i+1;
  372.     }
  373.     send_tel(buf,n);
  374. }
  375. int
  376. send_tel(buf,n)
  377. char *buf;
  378. int16 n;
  379. {
  380.     int i;
  381.  
  382.     tnoutreq.io_Data = buf;
  383.     tnoutreq.io_Length = n;
  384.  
  385.     SendIO(&tnoutreq);
  386.     i = WaitIO(&tnoutreq); 
  387.     if (tnoutreq.io_State != old)
  388.       {
  389.         showstate(old, tnoutreq.io_State);
  390.         old = tnoutreq.io_State;
  391.       }
  392.  
  393. }
  394.  
  395. /* Process incoming TELNET characters */
  396. int
  397. tel_input(tn,bp, len)
  398. register struct telnet *tn;
  399. char *bp;
  400. int len;
  401. {
  402.     char c;
  403.     int ci;
  404.     void doopt(),dontopt(),willopt(),wontopt(),answer();
  405. #ifdef    FAST    /* DON'T USE -- Aztec memchr() routine is broken */
  406.     char *memchr();
  407.  
  408.     /* Optimization for very common special case -- no command chars */
  409.     if(tn->state == TS_DATA){
  410.         while(bp != NULLBUF && memchr(bp->data,IAC,bp->cnt) 
  411.             == NULLCHAR){
  412.             fflush(stdout);
  413.             write(1,bp->data,bp->cnt);
  414.             bp = free_mbuf(bp);
  415.         }
  416.     }
  417. #endif
  418.     while(len--){
  419.         c = *bp++;
  420.         ci = c & 0xff;
  421.         switch(tn->state){
  422.         case TS_DATA:
  423.             if(ci == IAC){
  424.                 tn->state = TS_IAC;
  425.             } else {
  426.                 if(!tn->remote[TN_TRANSMIT_BINARY])
  427.                     c &= 0x7f;
  428.                 putchar(c);
  429.             }
  430.             break;
  431.         case TS_IAC:
  432.             switch(ci){
  433.             case WILL:
  434.                 tn->state = TS_WILL;
  435.                 break;
  436.             case WONT:
  437.                 tn->state = TS_WONT;
  438.                 break;
  439.             case DO:
  440.                 tn->state = TS_DO;
  441.                 break;
  442.             case DONT:
  443.                 tn->state = TS_DONT;
  444.                 break;
  445.             case IAC:
  446.                 putchar(c);
  447.                 tn->state = TS_DATA;
  448.                 break;
  449.             default:
  450.                 tn->state = TS_DATA;
  451.                 break;
  452.             }
  453.             break;
  454.         case TS_WILL:
  455.             willopt(tn,ci);
  456.             tn->state = TS_DATA;
  457.             break;
  458.         case TS_WONT:
  459.             wontopt(tn,ci);
  460.             tn->state = TS_DATA;
  461.             break;
  462.         case TS_DO:
  463.             doopt(tn,ci);
  464.             tn->state = TS_DATA;
  465.             break;
  466.         case TS_DONT:
  467.             dontopt(tn,ci);
  468.             tn->state = TS_DATA;
  469.             break;
  470.         }
  471.     }
  472. }
  473.  
  474. #ifdef NOTDEF
  475. /* State change upcall routine */
  476. void
  477. t_state(tcb,old,new)
  478. register struct tcb *tcb;
  479. char old,new;
  480. {
  481.     struct telnet *tn;
  482.     char notify = 0;
  483.     extern char *tcpstates[];
  484.     extern char *reasons[];
  485.     extern char *unreach[];
  486.     extern char *exceed[];
  487.  
  488.     /* Can't add a check for unknown connection here, it would loop
  489.      * on a close upcall! We're just careful later on.
  490.      */
  491.     tn = (struct telnet *)tcb->user;
  492.  
  493.     if(current != NULLSESSION && current->type == TELNET && current->cb.telnet == tn)
  494.         notify = 1;
  495.  
  496.     switch(new){
  497.     case CLOSE_WAIT:
  498.         if(notify)
  499.             printf("%s\r\n",tcpstates[new]);
  500.         close_tcp(tcb);
  501.         break;
  502.     case CLOSED:    /* court adjourned */
  503.         if(notify){
  504.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  505.             if(tcb->reason == NETWORK){
  506.                 switch(tcb->type){
  507.                 case DEST_UNREACH:
  508.                     printf(": %s unreachable",unreach[tcb->code]);
  509.                     break;
  510.                 case TIME_EXCEED:
  511.                     printf(": %s time exceeded",exceed[tcb->code]);
  512.                     break;
  513.                 }
  514.             }
  515.             printf(")\r\n");
  516.             cmdmode();
  517.         }
  518.         del_tcp(tcb);
  519.         if(tn != NULLTN)
  520.             free_telnet(tn);
  521.         break;
  522.     default:
  523.         if(notify)
  524.             printf("%s\r\n",tcpstates[new]);
  525.         break;
  526.     }
  527.     fflush(stdout);
  528. }
  529. #endif
  530. /* Delete telnet structure */
  531. static
  532. free_telnet(tn)
  533. struct telnet *tn;
  534. {
  535.  
  536.     if(tn != NULLTN)
  537.         free((char *)tn);
  538. }
  539.  
  540. /* The guts of the actual Telnet protocol: negotiating options */
  541. static
  542. void
  543. willopt(tn,opt)
  544. struct telnet *tn;
  545. int opt;
  546. {
  547.     int ack;
  548.     void answer();
  549.  
  550. #ifdef    DEBUG
  551.     printf("recv: will ");
  552.     if(opt <= NOPTIONS)
  553.         printf("%s\r\n",t_options[opt]);
  554.     else
  555.         printf("%u\r\n",opt);
  556. #endif
  557.     
  558.     switch(opt){
  559.     case TN_TRANSMIT_BINARY:
  560.     case TN_ECHO:
  561.     case TN_SUPPRESS_GA:
  562.         if(tn->remote[opt] == 1)
  563.             return;        /* Already set, ignore to prevent loop */
  564.         if(opt == TN_ECHO){
  565.             if(refuse_echo){
  566.                 /* User doesn't want to accept */
  567.                 ack = DONT;
  568.                 break;
  569.             } else
  570.                 raw();        /* Put tty into raw mode */
  571.         }
  572.         tn->remote[opt] = 1;
  573.         ack = DO;            
  574.         break;
  575.     default:
  576.         ack = DONT;    /* We don't know what he's offering; refuse */
  577.     }
  578.     answer(tn,ack,opt);
  579. }
  580. static
  581. void
  582. wontopt(tn,opt)
  583. struct telnet *tn;
  584. int opt;
  585. {
  586.     void answer();
  587.  
  588. #ifdef    DEBUG
  589.     printf("recv: wont ");
  590.     if(opt <= NOPTIONS)
  591.         printf("%s\r\n",t_options[opt]);
  592.     else
  593.         printf("%u\r\n",opt);
  594. #endif
  595.     if(opt <= NOPTIONS){
  596.         if(tn->remote[opt] == 0)
  597.             return;        /* Already clear, ignore to prevent loop */
  598.         tn->remote[opt] = 0;
  599.         if(opt == TN_ECHO)
  600.             cooked();    /* Put tty into cooked mode */
  601.     }
  602.     answer(tn,DONT,opt);    /* Must always accept */
  603. }
  604. static
  605. void
  606. doopt(tn,opt)
  607. struct telnet *tn;
  608. int opt;
  609. {
  610.     void answer();
  611.     int ack;
  612.  
  613. #ifdef    DEBUG
  614.     printf("recv: do ");
  615.     if(opt <= NOPTIONS)
  616.         printf("%s\r\n",t_options[opt]);
  617.     else
  618.         printf("%u\r\n",opt);
  619. #endif
  620.     switch(opt){
  621. #ifdef    FUTURE    /* Use when local options are implemented */
  622.         if(tn->local[opt] == 1)
  623.             return;        /* Already set, ignore to prevent loop */
  624.         tn->local[opt] = 1;
  625.         ack = WILL;
  626.         break;
  627. #endif
  628.     default:
  629.         ack = WONT;    /* Don't know what it is */
  630.     }
  631.     answer(tn,ack,opt);
  632. }
  633. static
  634. void
  635. dontopt(tn,opt)
  636. struct telnet *tn;
  637. int opt;
  638. {
  639.     void answer();
  640.  
  641. #ifdef    DEBUG
  642.     printf("recv: dont ");
  643.     if(opt <= NOPTIONS)
  644.         printf("%s\r\n",t_options[opt]);
  645.     else
  646.         printf("%u\r\n",opt);
  647. #endif
  648.     if(opt <= NOPTIONS){
  649.         if(tn->local[opt] == 0){
  650.             /* Already clear, ignore to prevent loop */
  651.             return;
  652.         }
  653.         tn->local[opt] = 0;
  654.     }
  655.     answer(tn,WONT,opt);
  656. }
  657. static
  658. void
  659. answer(tn,r1,r2)
  660. struct telnet *tn;
  661. int r1,r2;
  662. {
  663.     struct mbuf *bp,*qdata();
  664.     char s[3];
  665.  
  666. #ifdef    DEBUG
  667.     switch(r1){
  668.     case WILL:
  669.         printf("sent: will ");
  670.         break;
  671.     case WONT:
  672.         printf("sent: wont ");
  673.         break;
  674.     case DO:
  675.         printf("sent: do ");
  676.         break;
  677.     case DONT:
  678.         printf("sent: dont ");
  679.         break;
  680.     }
  681.     if(r2 <= 6)
  682.         printf("%s\r\n",t_options[r2]);
  683.     else
  684.         printf("%u\r\n",r2);
  685. #endif
  686.  
  687.     s[0] = IAC;
  688.     s[1] = r1;
  689.     s[2] = r2;
  690.     tnoutreq.io_Data = s;
  691.     tnoutreq.io_Length = 3;
  692.     DoIO(&tnoutreq);
  693. /*
  694.     bp = qdata(s,(int16)3);
  695.     send_tcp(tn->tcb,bp);
  696. */
  697. }
  698.  
  699. #define    BUFMAXCNT    150
  700. static char conbuf[BUFMAXCNT];
  701. static int concnt = 0;
  702.  
  703. int
  704. amigaputchar(c)
  705.     char c;
  706. {
  707.     conbuf[concnt++] = c;
  708.     if ((c == '\n') || (concnt == BUFMAXCNT))
  709.         amigaflush();
  710.     return c;
  711. }
  712.  
  713. amigaflush()
  714. {
  715.     if (concnt == 0)
  716.         return;
  717.     consout.io_Data = (APTR) conbuf;
  718.     consout.io_Length = concnt;
  719.     consout.io_Command = CMD_WRITE;
  720.     DoIO(&consout);
  721.     concnt = 0;
  722. }
  723.     
  724. /*
  725.  *  Begin terrible, horrible hack.  All output should be printed upon (into?)
  726.  *  the window we opened before.  Here goes nothing...
  727.  */
  728.  
  729. printf(a, b, c, d, e, f, g, h, i, j, k)
  730.     char *a;
  731.     int b, c, d, e, f, g, h, i, j, k;
  732. {
  733.     if (concnt)
  734.         amigaflush();
  735.  
  736.     sprintf(conbuf, a, b, c, d, e, f, g, h, i, j, k);
  737.     consout.io_Data = (APTR) conbuf;
  738.     consout.io_Length = strlen(conbuf);
  739.     consout.io_Command = CMD_WRITE;
  740.     DoIO(&consout);        /* no use in doing this async */
  741. }
  742.