home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / PC.C < prev    next >
C/C++ Source or Header  |  1994-10-09  |  46KB  |  1,674 lines

  1. /* OS- and machine-dependent stuff for IBM-PC running MS-DOS and Turbo-C
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  *
  4.  * XMS extensions Copyright 1993 Johan. K. Reinalda, WG7J
  5.  */
  6. #include <dir.h>
  7. #include <dos.h>
  8. #include <io.h>
  9. #include <conio.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <process.h>
  13. #include <fcntl.h>
  14. #include <alloc.h>
  15. #include <stdarg.h>
  16. #include <bios.h>
  17. #include <time.h>
  18. #include "global.h"
  19. #include "mbuf.h"
  20. #include "proc.h"
  21. #include "iface.h"
  22. #include "internet.h"
  23. #include "session.h"
  24. #include "tty.h"
  25. #include "usock.h"
  26. #include "socket.h"
  27. #include "smtp.h"
  28. #include "cmdparse.h"
  29. #include "dirutil.h"
  30. #include "files.h"
  31. #include "pc.h"
  32. #include "index.h"
  33. #include "mailbox.h"
  34. #ifdef EMS
  35. #include "memlib.h"
  36. #endif
  37. #ifdef XMS
  38. #include "xms.h"
  39. #endif
  40.   
  41. #define CTLC    0x3
  42. #define DEL     0x7f
  43.   
  44. static int kbchar __ARGS((void));
  45. extern int Curdisp;
  46. extern struct proc *Display;
  47. FILE *Rawterm;
  48. /* Highest i've ever seen in 'ps' is around 400, ie 800 bytes - WG7J */
  49. unsigned _stklen = 2048;
  50. volatile int Tick;
  51. static int32 Starttime;
  52. int32 Clock;
  53.   
  54. #ifdef MULTITASK
  55. int Background;
  56. int Nokeys;
  57. extern unsigned Minheap;
  58. #endif
  59. extern int Tracesession;
  60.   
  61. #ifdef SPLITSCREEN
  62. extern char MainColors;
  63. extern char SplitColors;
  64. #endif
  65. #ifdef STATUSWIN
  66. extern int StatusLines;
  67. char MainStColors = WHITE+(MAGENTA<<4);
  68. char SesStColors = WHITE+(BLUE<<4);
  69. #else
  70. int StatusLines = 0;
  71. #endif
  72.   
  73. char *Screen;
  74. int ScreenSize;
  75. int SwapMode;
  76. #ifdef XMS
  77. unsigned int ScreenSizeK;
  78. #endif
  79.   
  80. int Watchdog;                       /* Watch Dog off by default */
  81. int WDTick = 300*(1000 / MSPTICK);      /* 5 minutes watchdog timer */
  82. int WDCurr = 300*(1000 / MSPTICK);      /* Initial count down timer */
  83.   
  84. /* This flag is set by setirq() if IRQ 8-15 is used, indicating
  85.  * that the machine is a PC/AT with a second 8259 interrupt controller.
  86.  * If this flag is set, the interrupt return code in pcgen.asm will
  87.  * send an End of Interrupt command to the second 8259 as well as the
  88.  * first.
  89.  */
  90. int Isat;
  91.   
  92. static char Ttbuf[BUFSIZ];
  93. static char Tsbuf[BUFSIZ];
  94. static int saved_break;
  95.   
  96. int am_i_an_AT __ARGS((void));
  97.   
  98.   
  99. /* Keyboard input buffer */
  100. #define KBSIZE  256
  101. static struct {
  102.     char buf[KBSIZE];
  103.     char *wp;
  104.     char *rp;
  105.     int cnt;
  106. } Keyboard;
  107.   
  108. #if !defined CPU286 && !defined CPU386 && !defined CPU486 && !defined CPU586
  109. int
  110. am_i_an_AT()
  111. {
  112.     unsigned char *model_code = MK_FP(0xF000,0xFFFE);
  113.   
  114.     if(*model_code == 0xFC)
  115.         return 1;
  116.     else
  117.         return 0;
  118. }
  119. #endif
  120.   
  121. /* Following code from Doug Crompton */
  122. /* define the error messages for trapping disk problems
  123.  */
  124. static char *crit_err_msg[] = {
  125.     "write protect",
  126.     "unknown unit",
  127.     "not ready",
  128.     "unknown command",
  129.     "data error (CRC)",
  130.     "bad request",
  131.     "seek error",
  132.     "unknown media type",
  133.     "sector not found",
  134.     "printer out of paper",
  135.     "write fault",
  136.     "read fault",
  137.     "general failure",
  138.     "reserved",
  139.     "reserved",
  140.     "invalid disk change"
  141. };
  142.   
  143.   
  144. int
  145. errhandler(int errval,int ax,int bp,int si)
  146. {
  147.     char msg[80];
  148.     unsigned di;
  149.     int drive;
  150.     int errorno;
  151.   
  152.     di= _DI;
  153.   
  154.     if (ax < 0)
  155.         hardretn(3);
  156.     drive = ax & 0x00FF;
  157.     errorno = di & 0x00FF;
  158.     sprintf(msg, "\r\nError: %s on drive %c\r\n$",
  159.     crit_err_msg[errorno], 'A' + drive);
  160.     bdosptr(0x09,msg,0);
  161.     hardretn(3);
  162.     return 0;   /* to please the compiler */
  163. }
  164.   
  165. int c_break(void) {     /* ctrl-brk or ctrl-c handler */
  166.     return 1;
  167. }
  168.   
  169. #ifdef __BORLANDC__
  170. #undef fopen
  171. FILE _FAR *_Cdecl fopen(const char _FAR *__path, const char _FAR *__mode);
  172. #endif
  173.   
  174. /* Called at startup time to set up console I/O, memory heap */
  175. void
  176. ioinit()
  177. {
  178.   
  179.     /* Fail all I/O errors */
  180.     harderr(errhandler);
  181.   
  182.     /* Save these two file table entries for something more useful */
  183.     fclose(stdaux);
  184.     fclose(stdprn);
  185.     setbuf(stdout,Tsbuf);
  186.   
  187.     Rawterm = fopen("con","wb");
  188.     setbuf(Rawterm,Ttbuf);
  189.     /* this breaks tab expansion so you must use ANSI or NANSI */
  190.     ioctl(fileno(Rawterm), 1, (ioctl(fileno(Rawterm),0) & 0xff) | 0x20);
  191.     saved_break = getcbrk();
  192.     setcbrk(0);
  193.     ctrlbrk(c_break);
  194.   
  195. #ifdef MSDOS
  196. #if !defined CPU286 && !defined CPU386 && !defined CPU486 && !defined CPU586
  197.     /* test to see if we're running on an AT class machine.
  198.      * Set Isat flag accordingly - N1BEE
  199.      */
  200.     Isat = am_i_an_AT();
  201. #else
  202.     Isat = 1;
  203. #endif
  204. #endif
  205.     Starttime = bioscnt();
  206.     /* Link timer handler into timer interrupt chain */
  207.     chtimer(btick);
  208.   
  209.     /* Find out what multitasker we're running under, if any */
  210.     chktasker();
  211.   
  212.     /* Initialize keyboard queue */
  213.     Keyboard.rp = Keyboard.wp = Keyboard.buf;
  214.   
  215.     /* Check for essential directories. */
  216.     mkdir(LogsDir);
  217.     mkdir(Fdir);
  218.     mkdir(Spoolqdir);
  219.     mkdir(Mailspool);
  220.     mkdir(Mailqdir);
  221.     mkdir(Routeqdir);
  222. #ifdef MAILBOX
  223.     mkdir(Helpdir);
  224.     mkdir(Signature);
  225. #endif
  226. #if defined NNTPS || defined NNTP
  227.     mkdir(Newsdir);
  228. #endif
  229. }
  230.   
  231. /* Called just before exiting to restore console state */
  232. void
  233. iostop()
  234. {
  235.     struct iface *ifp,*iftmp;
  236.     void (**fp)(void);
  237.   
  238.     setbuf(Rawterm,NULLCHAR);
  239.     ioctl(fileno(Rawterm), 1, ioctl(fileno(Rawterm), 0) & 0xff & ~0x20);
  240.     setcbrk(saved_break);
  241.   
  242.     for(ifp = Ifaces;ifp != NULLIF;ifp = iftmp){
  243.         iftmp = ifp->next;
  244.         if_detach(ifp);
  245.     }
  246.     /* Call list of shutdown functions */
  247.     for(fp = Shutdown;*fp != NULLVFP;fp++){
  248.         (**fp)();
  249.     }
  250. }
  251. #ifdef SHELL
  252. /* Spawn subshell */
  253. int
  254. doshell(argc,argv,p)
  255. int argc;
  256. char *argv[];
  257. void *p;
  258. {
  259.     char *command;
  260.     int ret;
  261.     int OldWd;
  262.   
  263. #ifdef MULTITASK
  264.     if(Background) {
  265.         if(!start_back())
  266.             return -1;
  267.         Nokeys++;
  268.         free(mallocw(Minheap)); /* Force heap/core break to reserve a heap */
  269.     } else
  270. #endif
  271.     {
  272.         OldWd = Watchdog;   /* Save old watchdog state, and turn it off */
  273.         Watchdog = 0;       /* while shelled out */
  274.     }
  275.   
  276.     if(argc == 1 || !stricmp(argv[1], "/c")) {
  277.         if((command = getenv("COMSPEC")) == NULLCHAR)
  278.             command = "COMMAND.COM";
  279.         ret = spawnvp(P_WAIT,command,argv);
  280.     } else {
  281.         ret = spawnvp(P_WAIT,argv[1],(argv + 1));
  282.     }
  283.   
  284. #ifdef MULTITASK
  285.     if(Background) {
  286.         Nokeys--;
  287.         stop_back();
  288.     } else
  289. #endif
  290.     {
  291.         Watchdog = OldWd;   /* Restore old watchdog state, */
  292.         WDCurr = WDTick;    /* and start with a fresh count */
  293.     }
  294.   
  295.     /* Update index files - WG7J */
  296.     UpdateIndex(NULL,0);
  297.   
  298.     return ret;
  299. }
  300. #endif
  301.   
  302. #ifdef ALLCMD
  303. /* Spawn mailer as subshell */
  304. int
  305. dobmail(argc,argv,p)
  306. int argc;
  307. char *argv[];
  308. void *p;
  309. {
  310.     char *command;
  311.     int ret;
  312.     int OldWd;
  313.   
  314. #ifdef MULTITASK
  315.     if(Background) {
  316.         if(!start_back())
  317.             return -1;
  318.         Nokeys++;
  319.         free(mallocw(Minheap));
  320.     } else
  321. #endif
  322.     {
  323.         OldWd = Watchdog;   /* Save old watchdog state, and turn it off */
  324.         Watchdog = 0;       /* while shelled out */
  325.     }
  326.   
  327.     if((command = getenv("MAILER")) == NULLCHAR)
  328.         command = "BM.EXE";
  329.     ret = spawnvp(P_WAIT,command,argv);
  330.   
  331. #ifdef MULTITASK
  332.     if(Background) {
  333.         Nokeys--;
  334.         stop_back();
  335.     } else
  336. #endif
  337.     {
  338.         Watchdog = OldWd;   /* Restore old watchdog state, */
  339.         WDCurr = WDTick;    /* and start with a fresh count */
  340.     }
  341.   
  342.     /* Update index files - WG7J */
  343.     UpdateIndex(NULL,0);
  344.   
  345.     smtptick(NULL);         /* tickle smtp to send any mail */
  346.     return ret;
  347. }
  348. #endif /*ALLCMD*/
  349.   
  350. #ifdef MULTITASK
  351. /* if multitask mode is set - allow NOS and shell/mail to share system time */
  352. int dobackg(argc,argv,p)
  353. int argc;
  354. char *argv[];
  355. void *p;
  356. {
  357.     return setbool(&Background,"Multitasking DOS Shell ",argc,argv);
  358. }
  359. #endif
  360.   
  361. /* if watch-dog mode is set - make NOS reboot the system if it stalls */
  362. int dowatchdog(argc,argv,p)
  363. int argc;
  364. char *argv[];
  365. void *p;
  366. {
  367.     return setbool(&Watchdog,"NOS Watch Dog",argc,argv);
  368. }
  369.   
  370. /* Keyboard interrupt handler */
  371. void
  372. kbint()
  373. {
  374.     int sig = 0;
  375.     int c;
  376.   
  377. #ifdef MULTITASK
  378.     if(Background && Nokeys)
  379.         return;
  380. #endif
  381.   
  382.     while((c = kbraw()) != -1 && Keyboard.cnt < KBSIZE){
  383.         sig = 1;
  384.         *Keyboard.wp++ = c;
  385.         if(Keyboard.wp == &Keyboard.buf[KBSIZE])
  386.             Keyboard.wp = Keyboard.buf;
  387.         Keyboard.cnt++;
  388.     }
  389.     if(sig){
  390.         psignal(&Keyboard,0);
  391.     }
  392. }
  393. static int
  394. kbchar()
  395. {
  396.     int i_state;
  397.     char c;
  398.   
  399. #ifdef MULTITASK
  400.     if(Background && Nokeys)
  401.         return -1;
  402. #endif
  403.   
  404.     i_state = dirps();
  405.     while(Keyboard.cnt == 0)
  406.         pwait(&Keyboard);
  407.     Keyboard.cnt--;
  408.     restore(i_state);
  409.     c = *Keyboard.rp++;
  410.     if(Keyboard.rp == &Keyboard.buf[KBSIZE])
  411.         Keyboard.rp = Keyboard.buf;
  412.     return uchar(c);
  413. }
  414. /* Flush the raw terminal output */
  415. void
  416. rflush()
  417. {
  418.     fflush(Rawterm);
  419. }
  420.   
  421. #ifdef MSDOS
  422. #ifdef ALLCMD
  423. struct funcstr {
  424.     int fkey;
  425.     char alloced;
  426.     char *name;
  427.     char *fvalue;
  428. };
  429.   
  430. static struct funcstr DFAR fkeys[] = {
  431.     15,0,"Stab",NULLCHAR, /* tab + shift */
  432.     59,0,"F1",NULLCHAR,   /* F1 */
  433.     60,0,"F2",NULLCHAR,   /* F2 */
  434.     61,0,"F3",NULLCHAR,   /* F3 */
  435.     62,0,"F4",NULLCHAR,   /* F4 */
  436.     63,0,"F5",NULLCHAR,   /* F5 */
  437.     64,0,"F6",NULLCHAR,   /* F6 */
  438.     65,0,"F7",NULLCHAR,   /* F7 */
  439.     66,0,"F8",NULLCHAR,   /* F8 */
  440.     67,0,"F9",NULLCHAR,   /* F9 */
  441.     68,0,"F10",NULLCHAR,  /* F10 */
  442.     71,0,"home",NULLCHAR, /* home*/
  443.     72,0,"up",NULLCHAR,   /* up arrow*/
  444.     73,0,"pgup",NULLCHAR, /* pgup */
  445.     75,0,"left",NULLCHAR, /* left arrow */
  446.     77,0,"right",NULLCHAR,/* right arrow */
  447.     79,0,"end",NULLCHAR,  /* end */
  448.     80,0,"down",NULLCHAR, /* down arrow */
  449.     81,0,"pgdn",NULLCHAR, /* pgdn */
  450.     82,0,"ins",NULLCHAR,  /* ins */
  451.     83,0,"del",NULLCHAR,  /* del */
  452.     84,0,"SF1",NULLCHAR,  /* F1 + shift*/
  453.     85,0,"SF2",NULLCHAR,  /* F2 + shift*/
  454.     86,0,"SF3",NULLCHAR,  /* F3 + shift*/
  455.     87,0,"SF4",NULLCHAR,  /* F4 + shift*/
  456.     88,0,"SF5",NULLCHAR,  /* F5 + shift*/
  457.     89,0,"SF6",NULLCHAR,  /* F6 + shift*/
  458.     90,0,"SF7",NULLCHAR,  /* F7 + shift*/
  459.     91,0,"SF8",NULLCHAR,  /* F8 + shift*/
  460.     92,0,"SF9",NULLCHAR,  /* F9 + shift*/
  461.     93,0,"SF10",NULLCHAR,  /* F10 + shift*/
  462.     94,0,"CF1",NULLCHAR,  /* F1 + control*/
  463.     95,0,"CF2",NULLCHAR,  /* F2 + control*/
  464.     96,0,"CF3",NULLCHAR,  /* F3 + control*/
  465.     97,0,"CF4",NULLCHAR,  /* F4 + control*/
  466.     98,0,"CF5",NULLCHAR,  /* F5 + control*/
  467.     99,0,"CF6",NULLCHAR,  /* F6 + control*/
  468.     100,0,"CF7",NULLCHAR, /* F7 + control*/
  469.     101,0,"CF8",NULLCHAR, /* F8 + control*/
  470.     102,0,"CF9",NULLCHAR, /* F9 + control*/
  471.     103,0,"CF10",NULLCHAR,/* F10 + control*/
  472.     104,0,"AF1",NULLCHAR, /* F1 + alt*/
  473.     105,0,"AF2",NULLCHAR, /* F2 + alt*/
  474.     106,0,"AF3",NULLCHAR, /* F3 + alt*/
  475.     107,0,"AF4",NULLCHAR, /* F4 + alt*/
  476.     108,0,"AF5",NULLCHAR, /* F5 + alt*/
  477.     109,0,"AF6",NULLCHAR, /* F6 + alt*/
  478.     110,0,"AF7",NULLCHAR, /* F7 + alt*/
  479.     111,0,"AF8",NULLCHAR, /* F8 + alt*/
  480.     112,0,"AF9",NULLCHAR, /* F9 + alt*/
  481.     113,0,"AF10",NULLCHAR, /* F10 + alt*/
  482.     114,0,"Cprnt",NULLCHAR, /* PrtSc + ctl*/
  483.     117,0,"Cend",NULLCHAR, /* end  + ctl */
  484.     118,0,"Cpgup",NULLCHAR, /* pgup + ctl */
  485.     119,0,"Chome",NULLCHAR, /* home + ctl */
  486.     132,0,"Cpgdn",NULLCHAR, /* pgdn + ctl */
  487.     133,0,"F11",NULLCHAR, /* F11 */
  488.     134,0,"F12",NULLCHAR, /* F12 */
  489.     135,0,"SF11",NULLCHAR, /* F11 + shift */
  490.     136,0,"SF12",NULLCHAR, /* F12 + shift */
  491.     137,0,"CF11",NULLCHAR, /* F11 + ctrl */
  492.     138,0,"CF12",NULLCHAR, /* F12 + ctrl */
  493.     139,0,"AF11",NULLCHAR, /* F11 + alt */
  494.     140,0,"AF12",NULLCHAR, /* F12 + alt */
  495.     0,0,NULL,NULLCHAR
  496. };
  497.   
  498. char Leftover = 0;
  499. char *Nextkey;
  500. #endif /*ALLCMD*/
  501. #endif /*MSDOS*/
  502.   
  503. /* Read characters from the keyboard, translating them to "real" ASCII.
  504.  * If none are ready, block. The F-10 key is special; translate it to -2.
  505.  * Modified for function key session switching, and command recall - WG7J
  506.  */
  507. #ifdef ALLCMD
  508. int
  509. kbread()
  510. {
  511. #ifndef MSDOS
  512.     int c;
  513. #else
  514.     int c,i,j;
  515.   
  516.     if((c = Leftover) != 0)  {
  517.         Leftover = *Nextkey++;
  518.         return c;
  519.     }
  520. #endif  MSDOS
  521.     if((c = kbchar()) == 0){
  522.         /* Lead-in to a special char */
  523.         c = kbchar();
  524.         if(Current == Command) {    /* Check for command recall */
  525.             if(c == 72)     /* UP arrow */
  526.                 return UPARROW;
  527.             if(c == 80)     /* DOWN arrow */
  528.                 return DNARROW;
  529.         }
  530.         switch(c){
  531.             case 3:         /* NULL (bizzare!) */
  532.                 c = 0;
  533.                 break;
  534.             case 68:    /* F-10 key (used as command-mode escape) */
  535.                 if(fkeys[10].fvalue == NULLCHAR){
  536.                     c = -2;
  537.                     break;
  538.                 }
  539.             default:    /* Dunno what it is */
  540. #ifdef  MSDOS
  541.                 if(c > 58 && c < 68) {  /* F1 to F9 */
  542.                     if(fkeys[c-58].fvalue == NULLCHAR) {
  543.                         c = (c - 56) * -1; /* NO fkey defined - WG7J */
  544.                         break;
  545.                     }
  546.                 }
  547.                 for(i=0;(j = fkeys[i].fkey) != 0;i++)
  548.                     if(j == c) {
  549.                         Nextkey = fkeys[i].fvalue;
  550.                         if(Nextkey == NULLCHAR) {
  551.                             c = -1;
  552.                             return c;
  553.                         }
  554.             /* If first char of fvalue is '~'
  555.              * switch to command session.
  556.              */
  557.                         if((c = *Nextkey++) == '~') {
  558.                             c = -2;
  559.                             Leftover = *Nextkey++;
  560.                         } else {
  561.                             if(c != 0)
  562.                                 Leftover = *Nextkey++;
  563. #ifdef notdef
  564.                             if (c == '~') /* switch to command ses. on ~ char */
  565.                                 c = -2;  /* KN4L Change */
  566. #endif
  567.                             else
  568.                                 c = -1;
  569.                         }
  570.                         return c;
  571.                     }
  572. #endif
  573.                 c = -1;
  574.         }
  575.     }
  576.     return c;
  577. }
  578. #else /*ALLCMD*/
  579.   
  580. int
  581. kbread()
  582. {
  583.     int c;
  584.   
  585.     if((c = kbchar()) == 0){
  586.         /* Lead-in to a special char */
  587.         c = kbchar();
  588.         if(Current == Command) {    /* Check for command recall */
  589.             if(c == 72)     /* UP arrow */
  590.                 return UPARROW;
  591.             if(c == 80)     /* DOWN arrow */
  592.                 return DNARROW;
  593.         }
  594.         switch(c){
  595.             case 3:         /* NULL (bizzare!) */
  596.                 c = 0;
  597.                 break;
  598.             case 68:        /* F-10 key (used as command-mode escape) */
  599.                 c = -2;
  600.                 break;
  601.             case 83:        /* DEL key */
  602.                 c = 0x7f;
  603.                 break;
  604.             default:        /* Dunno what it is */
  605.                 if(c > 58 && c < 68)    /* F1 to F9 */
  606.                     c = (c - 56) * -1;
  607.                 else
  608.                     c = -1;
  609.         }
  610.     }
  611.     return c;
  612. }
  613.   
  614. #endif /*ALLCMD*/
  615.   
  616. #ifdef  MSDOS
  617. #ifdef ALLCMD
  618. int
  619. dofkey(argc,argv,p)
  620. int argc;
  621. char *argv[];
  622. void *p;
  623. {
  624.     int c,i,j;
  625.     char *q, *r;
  626.     char str[100];
  627.   
  628.     if(argc == 1) {
  629.         tputs("\n  key num definition             key num definition\n");
  630.         for(i=0;fkeys[i].fkey != 0;i++) {
  631.             char *s;
  632.             if((s=fkeys[i].fvalue) == NULL)
  633.                 s = "";
  634.             else {
  635.                 q=s;
  636.                 r=str;
  637.                 s=str;
  638.                 while(*q)
  639.                     if(*q < ' ') { /* This is ASCII dependent !! */
  640.                         *r++ = '^';
  641.                         *r++ = *q++ | 0x40;
  642.                     } else if (*q == '^') {
  643.                         *r++ = '^';
  644.                         *r++ = *q++;
  645.                     } else if (*q == '\177') {
  646.                         *r++ = '^';
  647.                         *r++ = '?';
  648.                         q++;
  649.                     } else {
  650.                         *r++ = *q++;
  651.                     }
  652.                 *r = '\0';
  653.             }
  654.             tprintf("%5.5s %3d %-20.20s ",fkeys[i].name,fkeys[i].fkey,s);
  655.             if(i%2)
  656.                 tputc('\n');
  657.         }
  658.         tputs("\nusage: fkey <key number> [<value> | \"string\"]\n");
  659.         return 0;
  660.     }
  661.   
  662.     c = atoi(argv[1]);
  663.     if(c == 0 || c > 255) {
  664.         tputs("fkey number out of range.\n");
  665.         return 1;
  666.     }
  667.   
  668.     for(j = 0;(i = fkeys[j].fkey) != 0; j++)
  669.         if(i == c)
  670.             break;
  671.   
  672.     if(i == 0){
  673.         tputs("fkey number not found\n");
  674.         return 1;
  675.     }
  676.   
  677.     if(argc == 2) {
  678.         q = fkeys[j].fvalue;
  679.         r = str;
  680.         if(q == NULLCHAR)
  681.             tprintf("fkey %d has no assigned value.\n",c);
  682.         else {
  683.             while(*q)
  684.                 if(*q < ' ') { /* This is ASCII dependent !! */
  685.                     *r++ = '^';
  686.                     *r++ = *q++ + 0x40;
  687.                 } else if (*q == '^') {
  688.                     *r++ = '^';
  689.                     *r++ = *q++;
  690.                 } else if (*q == '\177') {
  691.                     *r++ = '^';
  692.                     *r++ = '?';
  693.                     q++;
  694.                 } else
  695.                     *r++ = *q++;
  696.             *r = '\0';
  697.             tprintf("fkey = %s\n",str);
  698.         }
  699.         return 0;
  700.     }
  701.   
  702.     if(argc == 3) {
  703.         if(fkeys[j].alloced)
  704.             fkeys[j].alloced = 0;
  705.         else
  706.             if(fkeys[j].fvalue != NULLCHAR)
  707.                 free(fkeys[j].fvalue);
  708.   
  709.         r = str;
  710.         q = argv[2];
  711.         while(*q){
  712.             if(*q == '^'){  /* ^ gives control char next */
  713.                 q++;
  714.                 if(*q == '^') {
  715.                     *r++ = *q++; /* No, he wants a ^ */
  716.                 } else if (*q == '?') {
  717.                     *r++ = '\177';
  718.                     q++;
  719.                 } else {
  720.                     *r++ = *q++ & 0x1f;
  721.                 }
  722.             } else
  723.                 *r++ = *q++;
  724.         }
  725.         *r = '\0';
  726.         fkeys[j].fvalue = strdup(str);
  727.     }
  728.     return 0;
  729. }
  730. #endif /*ALLCMD*/
  731. #endif /*MSDOS*/
  732.   
  733. /* Install hardware interrupt handler.
  734.  * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
  735.  * Note that bus line IRQ2 maps to IRQ9 on the AT
  736.  */
  737. int
  738. setirq(irq,handler)
  739. unsigned irq;
  740. INTERRUPT (*handler)();
  741. {
  742.     /* Set interrupt vector */
  743.     if(irq < 8){
  744.         setvect(8+irq,handler);
  745.     } else if(irq < 16){
  746.         Isat = 1;
  747.         setvect(0x70 + irq - 8,handler);
  748.     } else {
  749.         return -1;
  750.     }
  751.     return 0;
  752. }
  753.   
  754. /* Return pointer to hardware interrupt handler.
  755.  * Takes IRQ numbers from 0-7 (0-15 on AT) and maps to actual 8086/286 vectors
  756.  */
  757. INTERRUPT
  758. (*getirq(irq))()
  759. unsigned int irq;
  760. {
  761.     /* Set interrupt vector */
  762.     if(irq < 8){
  763.         return getvect(8+irq);
  764.     } else if(irq < 16){
  765.         return getvect(0x70 + irq - 8);
  766.     } else {
  767.         return NULLVIFP;
  768.     }
  769. }
  770. /* Disable hardware interrupt */
  771. int
  772. maskoff(irq)
  773. unsigned irq;
  774. {
  775.     if(irq < 8){
  776.         setbit(0x21,(char)(1<<irq));
  777.     } else if(irq < 16){
  778.         irq -= 8;
  779.         setbit(0xa1,(char)(1<<irq));
  780.     } else {
  781.         return -1;
  782.     }
  783.     return 0;
  784. }
  785. /* Enable hardware interrupt */
  786. int
  787. maskon(irq)
  788. unsigned irq;
  789. {
  790.     if(irq < 8){
  791.         clrbit(0x21,(char)(1<<irq));
  792.     } else if(irq < 16){
  793.         irq -= 8;
  794.         clrbit(0xa1,(char)(1<<irq));
  795.     } else {
  796.         return -1;
  797.     }
  798.     return 0;
  799. }
  800. /* Return 1 if specified interrupt is enabled, 0 if not, -1 if invalid */
  801. int
  802. getmask(irq)
  803. unsigned irq;
  804. {
  805.     if(irq < 8)
  806.         return (inportb(0x21) & (1 << irq)) ? 0 : 1;
  807.     else if(irq < 16){
  808.         irq -= 8;
  809.         return (inportb(0xa1) & (1 << irq)) ? 0 : 1;
  810.     } else
  811.         return -1;
  812. }
  813. /* Called from assembler stub linked to BIOS interrupt 1C, called on each
  814.  * hardware clock tick. Signal a clock tick to the timer process.
  815.  */
  816. void
  817. ctick()
  818. {
  819.     if(Watchdog)
  820.         if(WDCurr-- == 0)
  821.             sysreset();
  822.     Tick++;
  823. #ifdef notdef
  824.     Clock++;        /* Keep system time */
  825. #endif
  826.     psignal(&Tick,1);
  827. }
  828. /* Called from the timer process on every tick. NOTE! This function
  829.  * can NOT be called at interrupt time because it calls the BIOS
  830.  */
  831. void
  832. pctick()
  833. {
  834.     long t;
  835.     static long oldt;       /* Value of bioscnt() on last call */
  836.     static long days;       /* # of times bioscnt() has rolled over */
  837.   
  838.     /* Update the time-since-boot */
  839.     t = bioscnt();
  840.   
  841.     if(t < oldt)
  842.         days++; /* bioscnt has rolled past midnight */
  843.     oldt = t;
  844.     Clock = (int32)((days * 0x1800b0L) + t - Starttime);
  845. }
  846.   
  847. /* Set bit(s) in I/O port */
  848. void
  849. setbit(port,bits)
  850. unsigned port;
  851. char bits;
  852. {
  853.     outportb(port,(char)inportb(port)|bits);
  854. }
  855. /* Clear bit(s) in I/O port */
  856. void
  857. clrbit(port,bits)
  858. unsigned port;
  859. char bits;
  860. {
  861.     outportb(port,(char)(inportb(port) & ~bits));
  862. }
  863. /* Set or clear selected bit(s) in I/O port */
  864. void
  865. writebit(port,mask,val)
  866. unsigned port;
  867. char mask;
  868. int val;
  869. {
  870.     register char x;
  871.   
  872.     x = inportb(port);
  873.     if(val)
  874.         x |= mask;
  875.     else
  876.         x &= ~mask;
  877.     outportb(port,x);
  878. }
  879. /* Convert a pointer to a long integer */
  880. long
  881. ptol(p)
  882. void *p;
  883. {
  884.     long x;
  885.   
  886.     x = FP_OFF(p);
  887. #ifdef  LARGEDATA
  888.     x |= (long)FP_SEG(p) << 16;
  889. #endif
  890.     return x;
  891. }
  892. void *
  893. ltop(l)
  894. long l;
  895. {
  896.     register unsigned int seg,offset;
  897.   
  898.     seg = (unsigned int)(l >> 16);
  899.     offset = (unsigned int)l;
  900.     return MK_FP(seg,offset);
  901. }
  902. #ifdef notdef   /* Assembler versions in pcgen.asm */
  903. /* Multiply a 16-bit multiplier by an arbitrary length multiplicand.
  904.  * Product is left in place of the multiplicand, and the carry is
  905.  * returned
  906.  */
  907. int16
  908. longmul(multiplier,n,multiplicand)
  909. int16 multiplier;
  910. int n;                          /* Number of words in multiplicand[] */
  911. register int16 *multiplicand;   /* High word is in multiplicand[0] */
  912. {
  913.     register int i;
  914.     unsigned long pc;
  915.     int16 carry;
  916.   
  917.     carry = 0;
  918.     multiplicand += n;
  919.     for(i=n;i != 0;i--){
  920.         multiplicand--;
  921.         pc = carry + (unsigned long)multiplier * *multiplicand;
  922.         *multiplicand = pc;
  923.         carry = pc >> 16;
  924.     }
  925. }
  926. return carry;
  927. }
  928. /* Divide a 16-bit divisor into an arbitrary length dividend using
  929.  * long division. The quotient is returned in place of the dividend,
  930.  * and the function returns the remainder.
  931.  */
  932. int16
  933. longdiv(divisor,n,dividend)
  934. int16 divisor;
  935. int n;                          /* Number of words in dividend[] */
  936. register int16 *dividend;       /* High word is in dividend[0] */
  937. {
  938.     /* Before each division, remquot contains the 32-bit dividend for this
  939.      * step, consisting of the 16-bit remainder from the previous division
  940.      * in the high word plus the current 16-bit dividend word in the low
  941.      * word.
  942.      *
  943.      * Immediately after the division, remquot contains the quotient
  944.      * in the low word and the remainder in the high word (which is
  945.      * exactly where we need it for the next division).
  946.      */
  947.     unsigned long remquot;
  948.     register int i;
  949.   
  950.     if(divisor == 0)
  951.         return 0;       /* Avoid divide-by-zero crash */
  952.     remquot = 0;
  953.     for(i=0;i<n;i++,dividend++){
  954.         remquot |= *dividend;
  955.         if(remquot == 0)
  956.             continue;       /* Avoid unnecessary division */
  957. #ifdef  __TURBOC__
  958.         /* Use assembly lang routine that returns both quotient
  959.          * and remainder, avoiding a second costly division
  960.          */
  961.         remquot = divrem(remquot,divisor);
  962.         *dividend = remquot;    /* Extract quotient in low word */
  963.         remquot &= ~0xffffL;    /* ... and mask it off */
  964. #else
  965.         *dividend = remquot / divisor;
  966.         remquot = (remquot % divisor) << 16;
  967. #endif
  968.     }
  969.     return remquot >> 16;
  970. }
  971. #endif
  972.   
  973. void
  974. sysreset()
  975. {
  976.     void (*foo) __ARGS((void));
  977.   
  978.     foo = MK_FP(0xffff,0);  /* FFFF:0000 is hardware reset vector */
  979.     (*foo)();
  980. }
  981.   
  982. void
  983. newscreen(sp)
  984. struct session *sp;
  985. {
  986.     if(sp != NULLSESSION)
  987.         sp->screen = callocw(1,sizeof(struct screen));
  988. }
  989.   
  990. void
  991. freescreen(sp)
  992. struct session *sp;
  993. {
  994.     if(sp == NULLSESSION || sp->screen == NULLSCREEN)
  995.         return;
  996. #ifdef XMS
  997.     if(sp->screen->stype == EMS_SWAP)
  998.         effree(sp->screen->sv.ems.token);
  999.     else
  1000. #endif
  1001. #ifdef XMS
  1002.         if(sp->screen->stype == XMS_SWAP && sp->screen->sv.handle != 0)
  1003.             Free_XMS(sp->screen->sv.handle);
  1004.         else
  1005. #endif
  1006.             if(sp->screen->stype == FILE_SWAP && sp->screen->sv.fp != NULL)
  1007.                 fclose(sp->screen->sv.fp);
  1008.             else if(sp->screen->stype == MEM_SWAP && sp->screen->sv.save != NULLCHAR)
  1009.                 free(sp->screen->sv.save);
  1010.     free((char *)sp->screen);
  1011. }
  1012.   
  1013. #ifdef XMS
  1014. /* Free XMS memory used for screen swapping.
  1015.  * Called right before exit().
  1016.  */
  1017. void Free_Screen_XMS(void) {
  1018.     struct session *sp;
  1019.     int i;
  1020.   
  1021.     for(i=0,sp=Sessions;i < Nsessions;sp++,i++)
  1022.         if(sp->type != FREE && sp->screen->stype == XMS_SWAP && \
  1023.             sp->screen->sv.handle != 0)
  1024.             Free_XMS(sp->screen->sv.handle);
  1025. }
  1026. #endif
  1027.   
  1028. extern int Numrows,Numcols;
  1029.   
  1030. /* Save specified session screen and resume console screen */
  1031. void
  1032. swapscreen(old,new)
  1033. struct session *old,*new;
  1034. {
  1035.     struct text_info tr;
  1036. #ifdef XMS
  1037.     long handle;
  1038.     struct XMS_Move X;
  1039. #endif
  1040.   
  1041.     if(old == new)
  1042.         return; /* Nothing to do */
  1043.   
  1044.     fflush(Rawterm);
  1045.     gettextinfo(&tr);
  1046.   
  1047.     if(old != NULLSESSION){
  1048. #ifdef SPLITSCREEN
  1049.         /* Save old screen */
  1050.         if(old->split){
  1051. //            window(1,1,Numcols,Numrows);
  1052.         }
  1053. #endif
  1054. #ifdef EMS
  1055.         if(old->screen->stype == EMS_SWAP) {
  1056.             set1eptr(old->screen->sv.ems.token,(void far**)&old->screen->sv.ems.save);
  1057.             if(old->screen->sv.ems.save != NULLCHAR)
  1058.                 memcpy(old->screen->sv.ems.save,Screen,ScreenSize);
  1059.         } else
  1060. #endif
  1061. #ifdef XMS
  1062.             if(old->screen->stype == XMS_SWAP) {
  1063.                 if(old->screen->sv.handle == 0)
  1064.                     old->screen->sv.handle = Alloc_XMS(ScreenSizeK);
  1065.                 if(old->screen->sv.handle != 0) {
  1066.                 /* Now transfer to XMS */
  1067.                     X.Length = (long) ScreenSize;
  1068.                     X.SourceHandle = 0; /* Indicate conventional memory */
  1069.                     X.SourceOffset = (long) Screen;
  1070.                     X.DestHandle = old->screen->sv.handle;
  1071.                     X.DestOffset = 0L;
  1072.                     Move_XMS(&X);
  1073.                 }
  1074.             } else
  1075. #endif
  1076.                 if(old->screen->stype == FILE_SWAP) {
  1077.                     if(old->screen->sv.fp == NULL)
  1078.                         old->screen->sv.fp = tmpfile();
  1079.                     else
  1080.                         fseek(old->screen->sv.fp,0L,0);
  1081.                     if(old->screen->sv.fp != NULL){
  1082.                         fwrite(Screen,ScreenSize,1,old->screen->sv.fp);
  1083.                     }
  1084.                 } else {
  1085.                     if(old->screen->sv.save == NULLCHAR)
  1086.                         old->screen->sv.save = malloc(ScreenSize);
  1087.                     if(old->screen->sv.save != NULLCHAR){
  1088.                         memcpy(old->screen->sv.save,Screen,ScreenSize);
  1089.                     }
  1090.                 }
  1091.         old->screen->row = tr.cury;
  1092.         old->screen->col = tr.curx;
  1093.     }
  1094.     if(new != NULLSESSION){
  1095.         /* Load new screen */
  1096.         if(new->screen->stype == UNKNOWN_SWAP) {
  1097.             /* A brand new screen */
  1098. #ifdef EMS
  1099.             if(SwapMode == EMS_SWAP) {
  1100.                 if(efmalloc(ScreenSize,&new->screen->sv.ems.token) != 0)
  1101.                     new->screen->sv.ems.token = -1;
  1102.                 new->screen->stype = EMS_SWAP;
  1103.             } else
  1104. #endif
  1105. #ifdef XMS
  1106.                 if(SwapMode == XMS_SWAP) {
  1107.                     new->screen->sv.handle = Alloc_XMS(ScreenSizeK);
  1108.                     new->screen->stype = XMS_SWAP;
  1109.                 } else
  1110. #endif
  1111.                     if(SwapMode == FILE_SWAP)
  1112.                         new->screen->stype = FILE_SWAP;
  1113.                     else
  1114.                         new->screen->stype = MEM_SWAP;
  1115.   
  1116. #ifdef SPLITSCREEN
  1117.             if(new->split){
  1118.                 new->tsavex = 1;
  1119.                 new->tsavey = 1;
  1120.                 new->bsavex = 1;
  1121.                 new->bsavey = Numrows-1;
  1122.                 /* The output window */
  1123.                 window(1,1+StatusLines,Numcols,Numrows);
  1124.                 textattr(SplitColors);
  1125.                 clrscr();       /* Start with a fresh slate */
  1126.                 cputs("_\b");
  1127.   
  1128.                 /* The input window */
  1129.                 window(1,1+StatusLines,Numcols,Numrows-2);
  1130.                 textattr(MainColors);
  1131.                 clrscr();       /* Start with a fresh slate */
  1132.                 cputs("_\b");
  1133.             } else
  1134. #endif
  1135.             {
  1136.                 /* leave room for the status line */
  1137.                 window(1,1+StatusLines,Numcols,Numrows);
  1138.                 clrscr();       /* Start with a fresh slate */
  1139.             }
  1140.         } else {
  1141. #ifdef SPLITSCREEN
  1142.             if(new->split){
  1143.                 window(1,1+StatusLines,Numcols,Numrows-2);
  1144.                 textattr(MainColors);
  1145.             } else
  1146. #endif
  1147.             {
  1148.                 window(1,1+StatusLines,Numcols,Numrows);
  1149.             }
  1150. #ifdef EMS
  1151.             if(new->screen->stype == EMS_SWAP &&
  1152.             (int)new->screen->sv.ems.token != -1) {
  1153.                 /* Get the text from EMS into screen buffer */
  1154.                 set1eptr(new->screen->sv.ems.token,(void far**)&new->screen->sv.ems.save);
  1155.                 memcpy(Screen,new->screen->sv.ems.save,ScreenSize);
  1156.             } else
  1157. #endif
  1158. #ifdef XMS
  1159.                 if(new->screen->stype == XMS_SWAP &&
  1160.                 new->screen->sv.handle) {
  1161.                 /* Get the text from XMS into screen buffer */
  1162.                     X.Length = (long) ScreenSize;
  1163.                     X.SourceHandle = new->screen->sv.handle;
  1164.                     X.SourceOffset = 0L;
  1165.                     X.DestHandle = 0L;  /* Indicate conventional memory */
  1166.                     X.DestOffset = (long) Screen;
  1167.                     Move_XMS(&X);
  1168.                 } else
  1169. #endif
  1170.                     if(new->screen->stype == FILE_SWAP &&
  1171.                     new->screen->sv.fp) {
  1172.                         fseek(new->screen->sv.fp,0L,0);
  1173.                         fread(Screen,ScreenSize,1,new->screen->sv.fp);
  1174.   
  1175.                 /* Not closing is a little faster,
  1176.                  * but takes more resources
  1177.                  * while the session is active - WG7J
  1178.                  */
  1179.                         fclose(new->screen->sv.fp);
  1180.                         new->screen->sv.fp = NULLFILE;
  1181.   
  1182.                     } else if(new->screen->stype == MEM_SWAP &&
  1183.                     new->screen->sv.save) {
  1184.                         memcpy(Screen,new->screen->sv.save,ScreenSize);
  1185.                     /* Free the memory (saves 4K on a continuous basis) */
  1186.                         free(new->screen->sv.save);
  1187.                         new->screen->sv.save = NULLCHAR;
  1188.                     } else
  1189.                 /* Somehow, we couldn't save the old screen */
  1190.                         clrscr();
  1191.             gotoxy(new->screen->col,new->screen->row);
  1192.         }
  1193.     }
  1194.     alert(Display,1);       /* Wake him up */
  1195. }
  1196.   
  1197. void
  1198. display(i,v1,v2)
  1199. int i;
  1200. void *v1;
  1201. void *v2;
  1202. {
  1203.     int c;
  1204.     struct session *sp;
  1205.   
  1206.     /* This is very tricky code. Because the value of "Current" can
  1207.      * change any time we do a pwait, we have to be careful to detect
  1208.      * any change and go back and start again.
  1209.      */
  1210.     for(;;){
  1211.         sp = Current;
  1212.   
  1213.         if(sp->morewait){
  1214.             pwait(&sp->row);
  1215.             if(sp != Current || sp->row <= 0){
  1216.                 /* Current changed value, or the user
  1217.                  * hasn't really hit a key
  1218.                  */
  1219.                 continue;
  1220.             }
  1221.             /* Erase the prompt */
  1222.             if(StatusLines)
  1223.                 cputs("\r        \r");
  1224.             else
  1225.                 fprintf(Rawterm,"\r        \r");
  1226.         }
  1227.         sp->morewait = 0;
  1228.         if((c = rrecvchar(sp->output)) == -1){
  1229.             /* the alert() in swapscreen will cause this to
  1230.              * return -1 when current changes
  1231.              */
  1232.             pwait(NULL);    /* Prevent a nasty loop */
  1233.             continue;
  1234.         }
  1235.         if(StatusLines || sp->split){
  1236.             if(c == 0x0a){
  1237.                 cputs(Eol);
  1238.                 clreol();
  1239.             } else if(c == 0x09)     /* TAB */
  1240.                 do putch(' '); while(wherex() % 8 != 1);
  1241.             else
  1242.                 putch(c);
  1243.         } else {
  1244.             putc(c,Rawterm);
  1245.         }
  1246.         /* Fix by Ron Murray, vk6zjm */
  1247.         if(sp->record != NULLFILE) {     /* Don't save CR if ascii mode */
  1248.             if(c == '\r' || c == '\n')
  1249.                 fflush(sp->record);
  1250.             if(c != '\r' || sockmode(sp->output, -1) != SOCK_ASCII)
  1251.                 putc(c,sp->record);
  1252.         }
  1253. #ifdef notdef
  1254.         if(sp->record != NULLFILE)
  1255.             putc(c,sp->record);
  1256. #endif
  1257.         if(sp->flowmode && c == '\n' && --sp->row <= 0){
  1258.             if(StatusLines)
  1259.                 cputs("--More--");
  1260.             else
  1261.                 fprintf(Rawterm,"--More--");
  1262.             sp->morewait = 1;
  1263.         }
  1264.     }
  1265. }
  1266.   
  1267. /* Return time since startup in milliseconds. If the system has an
  1268.  * 8254 clock chip (standard on ATs and up) then resolution is improved
  1269.  * below 55 ms (the clock tick interval) by reading back the instantaneous
  1270.  * value of the counter and combining it with the global clock tick counter.
  1271.  * Otherwise 55 ms resolution is provided.
  1272.  *
  1273.  * Reading the 8254 is a bit tricky since a tick could occur asynchronously
  1274.  * between the two reads. The tick counter is examined before and after the
  1275.  * hardware counter is read. If the tick counter changes, try again.
  1276.  * Note: the hardware counter counts down from 65536.
  1277.  */
  1278. int32
  1279. msclock()
  1280. {
  1281.     int32 hi;
  1282.     int16 lo;
  1283.     int16 count[4]; /* extended (48-bit) counter of timer clocks */
  1284.   
  1285.     if(!Isat)
  1286.         return Clock * MSPTICK;
  1287.   
  1288.     do {
  1289.         hi = Clock + Tick;
  1290.         lo = clockbits();
  1291.     } while(hi != Clock + Tick); /* Make sure a tick didn't just occur */
  1292.   
  1293.     count[0] = 0;
  1294.     count[1] = hi >> 16;
  1295.     count[2] = hi;
  1296.     count[3] = -lo;
  1297.     longmul(11,4,count);    /* The ratio 11/13125 is exact */
  1298.     longdiv(13125,4,count);
  1299.     return ((long)count[2] << 16) + count[3];
  1300. }
  1301. /* Return clock in seconds */
  1302. int32
  1303. secclock()
  1304. {
  1305.     int32 hi;
  1306.     int16 lo;
  1307.     int16 count[4]; /* extended (48-bit) counter of timer clocks */
  1308.   
  1309.     if(!Isat)
  1310.         return Clock * MSPTICK / 1000L;
  1311.   
  1312.     do {
  1313.         hi = Clock + Tick;
  1314.         lo = clockbits();
  1315.     } while(hi != Clock + Tick); /* Make sure a tick didn't just occur */
  1316.   
  1317.     count[0] = 0;
  1318.     count[1] = hi >> 16;
  1319.     count[2] = hi;
  1320.     count[3] = -lo;
  1321.     longmul(11,4,count);    /* The ratio 11/13125 is exact */
  1322.     longdiv(13125,4,count);
  1323.     longdiv(1000,4,count);
  1324.     return ((long)count[2] << 16) + count[3];
  1325. }
  1326.   
  1327. int
  1328. doisat(argc,argv,p)
  1329. int argc;
  1330. char *argv[];
  1331. void *p;
  1332. {
  1333.     return setbool(&Isat,"AT/386 mode",argc,argv);
  1334. }
  1335.   
  1336. /* Directly read BIOS count of time ticks. This is used instead of
  1337.  * calling biostime(0,0L). The latter calls BIOS INT 1A, AH=0,
  1338.  * wich resets the midnight overflow flag, losing days on the clock.
  1339.  */
  1340. long
  1341. bioscnt()
  1342. {
  1343.     if(Mtasker < 5) {   /* Read direct except under OS/2 or DPMI */
  1344.         int i_state;
  1345.         long rval;
  1346.   
  1347.         i_state = dirps();
  1348.         rval = * (long far *)MK_FP(0x40,0x6c);
  1349.         restore(i_state);
  1350.         return rval;
  1351.     } else {                /* Use BIOS call under OS/2 or DPMI */
  1352.         return(biostime(0,0L));
  1353.     }
  1354. }
  1355.   
  1356. /* same as getenv(), but return "" instead of NULL when it does not exist */
  1357. char *getnenv (name)
  1358. char *name;
  1359. {
  1360.     char *rv;
  1361.   
  1362.     if ((rv = getenv(name)) == NULL)
  1363.         rv = "";                       /* NULL replaced by "" */
  1364.   
  1365.     return rv;
  1366. }
  1367.   
  1368. #ifdef STATUSWIN
  1369.   
  1370. void StatusLine1(void);
  1371.   
  1372. /* First line. Global information */
  1373. void StatusLine1() {
  1374.   
  1375.     struct session *sp;
  1376.     int len=0,s,r,t;
  1377.     char *cp;
  1378.   
  1379.     extern int ConvUsers,ConvHosts,BbsUsers,FwdUsers,FtpUsers,SmtpUsers;
  1380. #ifdef MAILFOR    
  1381.     extern int Mail_Received;    /* see mailfor.c */
  1382.  
  1383.  
  1384. /* PE1DGZ: Show blinking 'MAIL' if unread mail is present */
  1385.     if(Mail_Received) {
  1386.         textattr(MainStColors | 0x80);
  1387.         len += cputs("MAIL ");
  1388.     }
  1389. #endif    
  1390.   
  1391.     /* Set the colors */
  1392.     textattr(MainStColors);
  1393.   
  1394. #ifdef MSDOS
  1395. #if __BORLANDC__ >= 0x0400
  1396.     {
  1397.         char buf[9];
  1398.   
  1399.         _strtime(buf);  /* This only exists in bc3.0 and up */
  1400.         buf[5] = ' ';
  1401.         buf[6] = '\0';
  1402.         len+=cputs(buf);
  1403.     }
  1404. #endif
  1405. #endif
  1406.   
  1407.     len+=cprintf("%5.5lu/%-6.6lu"
  1408. #ifdef CONVERS
  1409.     " CONV=%d LNKS=%d"
  1410. #endif
  1411. #ifdef MAILBOX
  1412.     " BBS=%d"
  1413. #ifdef MBFWD
  1414.     " FWD=%d"
  1415. #endif
  1416. #endif
  1417. #ifdef FTPSERVER
  1418.     " FTP=%d"
  1419. #endif
  1420. #ifdef SMTPSERVER
  1421.     " SMTP=%d"
  1422. #endif
  1423.     "  Ses:",
  1424.   
  1425.     Localheap(),coreleft()
  1426. #ifdef CONVERS
  1427.     ,ConvUsers, ConvHosts
  1428. #endif
  1429. #ifdef MAILBOX
  1430.     ,BbsUsers
  1431. #ifdef MBFWD
  1432.     ,FwdUsers
  1433. #endif
  1434. #endif
  1435. #ifdef FTPSERVER
  1436.     ,FtpUsers
  1437. #endif
  1438. #ifdef SMTPSERVER
  1439.     ,SmtpUsers
  1440. #endif
  1441.     );
  1442.   
  1443.     /* Print all active sessions . Modified from TNOS
  1444.      * Calculate how much room there is left on the line
  1445.      */
  1446.   
  1447.     for(sp=Sessions; sp < &Sessions[Nsessions];sp++) {
  1448. //        if(Numcols - len < 3)
  1449. //            break;
  1450.         if(sp->type == FREE || sp->type == COMMAND || sp->type == TRACESESSION)
  1451.             continue;
  1452.   
  1453.         /* if there is data waiting, blink the session number */
  1454.         r = socklen(sp->output,1);
  1455.         textattr ( (r) ? MainStColors | 0x80 : MainStColors);
  1456.         len+=cprintf (" %d", sp->num);
  1457.     }
  1458.     textattr(MainStColors); /* In case blinking was on! */
  1459.     clreol();
  1460. }
  1461.   
  1462. #ifdef MAILBOX
  1463. char *StBuf2;    /* allocated in main.c */
  1464. int StLen2;  
  1465.  
  1466. void StatusLine2(void);
  1467.   
  1468. void StatusLine2() {
  1469.     struct mbx *m;
  1470.     char *cp;
  1471.     int len;
  1472.   
  1473.     cp = StBuf2+StLen2;
  1474.     *cp = '\0';
  1475.     for(m=Mbox;m;m=m->next) {
  1476.         if((len = strlen(m->name)) != 0 && (len < Numcols-(cp-StBuf2)-4)) {
  1477.             *cp++ = ' ';
  1478.             if(m->sid & MBX_SID)
  1479.                 *cp++ = '*';    /* Indicate a bbs */
  1480.             else switch(m->state) {
  1481.                 case MBX_GATEWAY:
  1482.                     *cp++ = '!';
  1483.                     break;
  1484.                 case MBX_READ:
  1485.                 case MBX_SUBJ:
  1486.                 case MBX_DATA:
  1487.                     *cp++ = '#';
  1488.                     break;
  1489.                 case MBX_UPLOAD:
  1490.                 case MBX_DOWNLOAD:
  1491.                 case MBX_XMODEM_RX:
  1492.                 case MBX_XMODEM_TX:
  1493.                     *cp++ = '=';
  1494.                     break;
  1495.                 case MBX_SYSOPTRY:
  1496.                 case MBX_SYSOP:
  1497.                     *cp++ = '@';
  1498.                     break;
  1499.                 case MBX_CONVERS:
  1500.                 case MBX_CHAT:
  1501.                     *cp++ = '^';
  1502.                 case MBX_CMD:
  1503.                     *cp++ = ' ';    /* To keep things aligned nicely */
  1504.                     break;
  1505.                 default:
  1506.                     *cp++ = '?';
  1507.                     break;
  1508.             }
  1509.             strcpy(cp,m->name);
  1510.             cp += len;
  1511.         }
  1512.     }
  1513.     cputs(StBuf2);
  1514.     clreol();
  1515. }
  1516. #endif /* MAILBOX */
  1517.   
  1518. char *StBuf3;    /* allocated in main.c */
  1519. int StLen3;
  1520. void StatusLine3(void);
  1521.   
  1522. /* The session dependent data */
  1523. void StatusLine3() {
  1524.     char *cp;
  1525.     int  s,t,SesType;
  1526.     struct usock *up;
  1527.   
  1528.     static struct session *MyCurrent;
  1529.     static int SesNameLen;
  1530.     static int SockStatus,SockName,SesData;
  1531.   
  1532.     /* Set the colors */
  1533.     textattr(SesStColors);
  1534.   
  1535.     /* Next line. Session specific information */
  1536.     if(MyCurrent != Current) {
  1537.         /* Keep track of the current session */
  1538.         MyCurrent = Current;
  1539.         SesType = MyCurrent->type;
  1540.         /* Remember to offset for "\r\n" at start of buffer ! */
  1541.         SesNameLen = 2;
  1542.         SesNameLen+=sprintf(StBuf3+SesNameLen,"%d %s:",
  1543.         MyCurrent->num,Sestypes[SesType]);
  1544.   
  1545.         /* We can't show network socket data until socket is valid ! */
  1546.         SesData = SockName = 0;
  1547.   
  1548.         SockStatus = 1; /* Show socket status by default */
  1549.         if(SesType == COMMAND || SesType == TRACESESSION) {
  1550. //            if(Command->curdirs)
  1551. //                sprintf(StBuf3+SesNameLen, "%-30.30s",Command->curdirs->dir);
  1552.             SockStatus = 0;     /* Don't show socket name and ses data */
  1553.   
  1554.         } else if(SesType == MORE ||
  1555.             SesType == REPEAT ||
  1556.             SesType == LOOK) {
  1557.                 sprintf(StBuf3+SesNameLen," %-20.20s",MyCurrent->name);
  1558.                 SockStatus = 0;     /* Don't show socket name and ses data */
  1559.           }
  1560.     }
  1561.     /* Only if this is a session with a network socket do we show the status */
  1562.     if(SockStatus) {
  1563.         if(!SockName && (s=MyCurrent->s) != -1) {
  1564.             int i;
  1565.             struct sockaddr fsocket;
  1566.   
  1567.             /* The session now has a valid network socket.
  1568.              * Go get the name, and pointer.
  1569.              */
  1570.             if(getpeername(s,(char *)&fsocket,&i) == -1)
  1571.                 cp = "";
  1572.             else cp = psocket(&fsocket);
  1573.             SesNameLen+=sprintf(StBuf3+SesNameLen," %-18.18s TxQ ",cp);
  1574.             SockName = SesData = 1;
  1575.         }
  1576.         /* We have the socket name, now go print the socket session data */
  1577.         if(SockName) {
  1578.             /* Some sessions keep hanging on to their network socket
  1579.              * until the use hits return to close the session !
  1580.              * Others will delete it sooner...
  1581.              */
  1582.             if((s=MyCurrent->s) != -1 && ((cp=sockstate(s))!= NULL) ) {
  1583.                 /* Network socket for session still valid */
  1584.                 t = socklen(s,1);
  1585.                 StLen3 = SesNameLen +
  1586.                 sprintf(StBuf3+SesNameLen,"%4.4d St: %-12.12s",t,cp);
  1587.             } else {
  1588.                 SesData = 0;    /* Don't print rest of line 3 ! */
  1589.                 sprintf(StBuf3+SesNameLen-4,"  LIMBO !");
  1590.             }
  1591.             /* If the socket is still valid, print some data */
  1592.             if(SesData) {
  1593.                 up = itop(s);
  1594.                 switch(up->type) {
  1595.                     case(TYPE_TCP):
  1596.                     {
  1597.                         struct tcb *tcb = up->cb.tcb;
  1598.                         sprintf(StBuf3+StLen3,
  1599.                         " T: %5.5ld/%-5.5ld ms",
  1600.                         (long)read_timer(&tcb->timer),
  1601.                         (long)dur_timer(&tcb->timer));
  1602.                     }
  1603.                         break;
  1604. #ifdef AX25
  1605.                     case(TYPE_AX25I):
  1606.                     {
  1607.                         struct ax25_cb *axp = up->cb.ax25;
  1608.                         sprintf(StBuf3+StLen3,
  1609.                         " T1: %5.5ld/%5.5ld ms",
  1610.                         (long)read_timer(&axp->t1),
  1611.                         (long)dur_timer(&axp->t1));
  1612.                     }
  1613.                         break;
  1614. #endif
  1615. #ifdef NETROM
  1616.                     case(TYPE_NETROML4):
  1617.                     {
  1618.                         struct nr4cb *cb = up->cb.nr4;
  1619.                         sprintf(StBuf3+StLen3,
  1620.                         " T: %5.5ld/%5.5ld ms",
  1621.                         (long)read_timer(&cb->tcd),
  1622.                         (long)dur_timer(&cb->tcd));
  1623.                     }
  1624.                         break;
  1625. #endif
  1626.                 }
  1627.             }
  1628.         }
  1629.     }
  1630.     cputs(StBuf3);
  1631.     clreol();
  1632. }
  1633.   
  1634. /* Build the status window on the screen - WG7J */
  1635. void UpdateStatus() {
  1636.   
  1637.     if(!StatusLines)
  1638.         return;
  1639.   
  1640. #ifdef MULTITASK
  1641.     /* if we are shelled out with multi-task on, do not update the status */
  1642.     if(Background && Nokeys)
  1643.         return;
  1644. #endif
  1645.   
  1646.   
  1647.     {
  1648.         struct text_info ti;
  1649.   
  1650.     /* get the current output context */
  1651.         gettextinfo(&ti);
  1652.   
  1653.     /* create the window */
  1654.         window(1,1,Numcols,StatusLines);
  1655.   
  1656.         StatusLine1();  /* Global system status */
  1657.         if(StatusLines > 1)
  1658. #ifdef MAILBOX
  1659.             StatusLine2();  /* Mailbox user status */
  1660.         if(StatusLines > 2)
  1661. #endif
  1662.             StatusLine3();    /* Session dependent status */
  1663.   
  1664.     /* restore the previous output context */
  1665.         window((int)ti.winleft,(int)ti.wintop,
  1666.         (int)ti.winright,(int)ti.winbottom);
  1667.         textattr(ti.attribute);
  1668.         gotoxy(ti.curx,ti.cury);
  1669.     }
  1670. }
  1671.   
  1672. #endif /* STATUSWIN */
  1673.   
  1674.