home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume27 / ytalk-3.0 / part01 / fd.c < prev    next >
C/C++ Source or Header  |  1993-08-20  |  6KB  |  262 lines

  1. /* fd.c */
  2.  
  3. /*               NOTICE
  4.  *
  5.  * Copyright (c) 1990,1992,1993 Britt Yenne.  All rights reserved.
  6.  * 
  7.  * This software is provided AS-IS.  The author gives no warranty,
  8.  * real or assumed, and takes no responsibility whatsoever for any 
  9.  * use or misuse of this software, or any damage created by its use
  10.  * or misuse.
  11.  * 
  12.  * This software may be freely copied and distributed provided that
  13.  * no part of this NOTICE is deleted or edited in any manner.
  14.  * 
  15.  */
  16.  
  17. /* Mail comments or questions to ytalk@austin.eds.com */
  18.  
  19. #include "header.h"
  20. #include "menu.h"
  21. #include <sys/time.h>
  22. #include <signal.h>
  23. #ifdef _AIX
  24. # include <sys/select.h>
  25. #endif
  26.  
  27. static fd_set fdset;        /* descriptors to select on */
  28. static fd_set fdtmp;        /* descriptors to select on (input_loop) */
  29. static fd_set sel;        /* currently readable descriptors */
  30. static int high_fd = 0;        /* highest fd so far */
  31. int input_flag = 0;        /* flag: waiting for user input */
  32. int user_winch = 0;        /* flag: user window/status changed */
  33.  
  34. struct fd_func {
  35.     void (*func)();            /* user function */
  36. };
  37. static struct fd_func tag[MAX_FILES];    /* one function per file descriptor */
  38.  
  39. /* Initialize fdset data.
  40.  */
  41. void
  42. init_fd()
  43. {
  44.     FD_ZERO(&fdset);
  45. }
  46.  
  47. /* Add a file descriptor to the current checklist.  The supplied
  48.  * function will be called whenever the file descriptor has input
  49.  * waiting.
  50.  */
  51. void
  52. add_fd(fd, user_func)
  53.   int fd;
  54.   void (*user_func)();
  55. {
  56.     if(fd < 0 || fd >= MAX_FILES)
  57.     {
  58.     show_error("add_fd: descriptor out of range");
  59.     return;
  60.     }
  61.     FD_SET(fd, &fdset);
  62.     tag[fd].func = user_func;
  63.     if(fd >= high_fd)
  64.     high_fd = fd + 1;
  65. }
  66.  
  67. /* Remove a file descriptor from the checklist.
  68.  */
  69. void
  70. remove_fd(fd)
  71.   int fd;
  72. {
  73.     if(fd < 0 || fd >= MAX_FILES)
  74.     {
  75.     show_error("remove_fd: descriptor out of range");
  76.     return;
  77.     }
  78.     FD_CLR(fd, &fdset);
  79.     FD_CLR(fd, &fdtmp);
  80.     FD_CLR(fd, &sel);
  81. }
  82.  
  83. /* Read an entire length of data.
  84.  * Returns 0 on success, -1 on error.
  85.  */
  86. int
  87. full_read(fd, buf, len)
  88.   int fd;
  89.   register char *buf;
  90.   register int len;
  91. {
  92.     register int rc;
  93.  
  94.     while(len > 0)
  95.     {
  96.     if((rc = read(fd, buf, len)) <= 0)
  97.         return -1;
  98.     buf += rc;
  99.     len -= rc;
  100.     }
  101.     return 0;
  102. }
  103.  
  104. /* -- MAIN LOOPS -- */
  105.  
  106. static long lastping, curtime;
  107.  
  108. void
  109. main_loop()
  110. {
  111.     register int fd, rc;
  112.     int mask, old_mask;
  113.     struct timeval tv;
  114.  
  115.     /* Some signals need to be blocked while doing internal
  116.      * processing, else some craziness might occur.
  117.      */
  118.  
  119.     mask = 0;
  120.  
  121. #ifdef SIGWINCH
  122.     mask |= sigmask(SIGWINCH);
  123. #endif
  124.  
  125.     /* For housecleaning to occur every CLEAN_INTERVAL seconds, we make
  126.      * our own little timer system.  SIGALRM is nice; in fact it's so
  127.      * useful that we'll be using it in other parts of YTalk.  Since
  128.      * we therefore can't use it here, we affect the timer manually.
  129.      */
  130.  
  131.     house_clean();
  132.     curtime = lastping = time(NULL);
  133.     for(;;)
  134.     {
  135.     /* check if we're done */
  136.  
  137.     if(connect_list == NULL
  138.     && wait_list == NULL
  139.     && menu_ptr == NULL
  140.     && running_process == 0)
  141.         bail(0);
  142.  
  143.     /* select */
  144.  
  145.     sel = fdset;
  146.     if(curtime > lastping + CLEAN_INTERVAL)
  147.         tv.tv_sec = 0;
  148.     else
  149.         tv.tv_sec = (lastping + CLEAN_INTERVAL) - curtime;
  150.     tv.tv_usec = 0;
  151.     if((rc = select(high_fd, &sel, 0, 0, &tv)) < 0)
  152.         if(errno != EINTR)
  153.         show_error("main_loop: select failed");
  154.  
  155.     /* block signals while doing internal processing */
  156.  
  157.     old_mask = sigblock(mask);
  158.  
  159.     /* process file descriptors with input waiting */
  160.  
  161.     if(rc > 0)
  162.         for(fd = 0; fd < high_fd; fd++)
  163.         if(FD_ISSET(fd, &sel))
  164.         {
  165.             errno = 0;
  166.             tag[fd].func(fd);
  167.             if(--rc <= 0)
  168.             break;
  169.         }
  170.  
  171.     /* check timer */
  172.  
  173.     curtime = time(NULL);
  174.     if(curtime - lastping >= CLEAN_INTERVAL)
  175.     {
  176.         house_clean();
  177.         lastping = time(NULL);
  178.     }
  179.  
  180.     /* re-allow signals */
  181.  
  182.     sigsetmask(old_mask);
  183.     if(user_winch)
  184.     {
  185.         /* This is a cute hack that updates a user menu
  186.          * dynamically as information changes.  So I had
  187.          * some free time.  there.
  188.          */
  189.         user_winch = 0;
  190.         update_user_menu();
  191.     }
  192.     }
  193. }
  194.  
  195. /* Input loop.  This loop keeps everything except user input going until
  196.  * input is received from <me>.  This is necessary for answering pressing
  197.  * questions without needing to add a getch_term() function to the terminal
  198.  * definition library.  Hack?  maybe.  Fun, tho.
  199.  */
  200. void
  201. input_loop()
  202. {
  203.     register int fd, rc;
  204.     struct timeval tv;
  205.     static int left_loop;
  206.  
  207.     left_loop = 0;
  208.     fdtmp = fdset;
  209.     while(io_len <= 0)
  210.     {
  211.     /* select */
  212.  
  213.     sel = fdtmp;
  214.     if(curtime > lastping + CLEAN_INTERVAL)
  215.         tv.tv_sec = 0;
  216.     else
  217.         tv.tv_sec = (lastping + CLEAN_INTERVAL) - curtime;
  218.     tv.tv_usec = 0;
  219.     if((rc = select(high_fd, &sel, 0, 0, &tv)) < 0)
  220.         if(errno != EINTR)
  221.         show_error("input_loop: select failed");
  222.  
  223.     /* process file descriptors with input waiting */
  224.  
  225.     if(rc > 0)
  226.         for(fd = 0; fd < high_fd; fd++)
  227.         if(FD_ISSET(fd, &sel))
  228.         {
  229.             /* Here the hack begins.  Any function that takes user
  230.              * input should clear "input_flag" and return.  This
  231.              * tells us to ignore this function for now.  Any
  232.              * function which receives input from <me> should leave
  233.              * my input in io_ptr/io_len.
  234.              */
  235.             errno = 0;
  236.             input_flag = 1;
  237.             tag[fd].func(fd);
  238.             if(left_loop) /* recursive input_loop()s.  argh! */
  239.             return;   /* let my parent function re-call me */
  240.             if(input_flag == 0)
  241.             {
  242.             /* don't check this descriptor anymore */
  243.             FD_CLR(fd, &fdtmp);
  244.             }
  245.             if(--rc <= 0)
  246.             break;
  247.         }
  248.  
  249.     /* check timer */
  250.  
  251.     curtime = time(NULL);
  252.     if(curtime - lastping >= CLEAN_INTERVAL)
  253.     {
  254.         input_flag = 1;
  255.         house_clean();
  256.         lastping = time(NULL);
  257.     }
  258.     }
  259.     input_flag = 0;
  260.     left_loop = 1;
  261. }
  262.