home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume7 / gosip2 / edit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-07-08  |  9.9 KB  |  370 lines

  1. #include <signal.h>
  2. #include <sys/wait.h>
  3. #include <sys/time.h>
  4. #include <sys/resource.h>
  5.  
  6. #include "edit.h"
  7. #include "global.h"
  8. #include "cat.h"
  9. #include "control.h"
  10. #include "util.h"
  11. #include "history.h"
  12.  
  13. #include <dirent.h>
  14. #include <ctype.h>
  15.  
  16. #define DEFAULT_EDITOR "ded"
  17. #define MIN_AGE 4*60*60         /* minimum age of drivel files in secs */
  18.  
  19. #define smaller(a,b) a * 10 < b * 7 || b - a > 1700
  20.  
  21. extern char *strcat(), *rindex(), *strncpy();
  22. extern int strncmp();
  23.  
  24. static int editor;              /* pid of editor child */
  25. static byte edit_over = 0;      /* flag set by SIGCHLD */
  26. static char *text_copy;         /* file name of text_file's copy */
  27.  
  28. /*  void tidy_up()
  29.  *
  30.  *  Routine called on receiving a terminating signal, such as TERM, QUIT or
  31.  *  HUP.  It releases locks, and removes backup files before quitting.  Also
  32.  *  kills the editor.
  33.  */
  34. static void tidy_up()
  35. {
  36.   (void) kill ( editor, SIGTERM );
  37.   (void) lock ( REMVE );
  38.   (void) unlink ( text_copy );
  39.   (void) fprintf ( stderr, "\r\n\007** %s: received terminating signal - quitting.\n", file );
  40.   exit ( 3 );
  41. }
  42.  
  43. /*  void force_off()
  44.  *
  45.  *  This routine is called when SIGALRM is received.  It prints a final
  46.  *  warning to the user, and then kills his editor after waiting a further
  47.  *  short time.  This will cause him to stop using gosip.
  48.  */
  49. static void force_off()
  50. {
  51.   puts ( "\r\n\007** Edit session terminating almost immediately. **\r" );
  52.   sleep ( 5 );
  53.   (void) kill ( editor, SIGTERM );
  54. }
  55.  
  56. /*  void child()
  57.  *
  58.  *  Called whenever a SIGCHLD is received.  It checks if the editor has
  59.  *  actually exit()ed - it might have changed state by being suspended - and
  60.  *  sets the global flag edit_over if it has.
  61.  */
  62. static void child()
  63. {
  64.   if ( editor == wait3 ( (union wait *) 0, WNOHANG, (struct rusage *) 0 ) )
  65.     edit_over = 1;
  66. }
  67.  
  68. /*  void do_nothing()
  69.  *
  70.  *  This routine does literally nothing.  It is needed as you have to call
  71.  *  something when you get SIGALRM.
  72.  */
  73. static void do_nothing()
  74. {
  75. }
  76.  
  77. /*  void manifesto()
  78.  *
  79.  *  Checks for the file info.gosip and prints its contents.
  80.  *  Currently unimplemented.
  81.  */
  82. /*
  83. static void manifesto()
  84. {
  85.   FILE *info_file;
  86.   char info_filename[MAX_LENGTH];
  87.   int c;
  88.  
  89.   (void) strcpy ( info_filename, "info." );
  90.   (void) strcat ( info_filename, file );
  91.   if ( info_file = fopen ( info_filename, "r" ) )
  92.   {
  93.     while ( ( c = getc ( info_file ) ) != EOF )
  94.       (void) putchar ( (char) c );
  95.     (void) fclose ( info_file );
  96.   }
  97. }
  98. */
  99.  
  100. /*  char *copy ( source, destination )
  101.  *
  102.  *  Copies the source file into the destination file.  If destination is null,
  103.  *  a filename in /tmp is generated and used as destination.  Returns the
  104.  *  destination file on success, otherwise a null pointer.
  105.  *
  106.  *  source       :  filename from which to copy.
  107.  *  destination  :  filename to which to copy.
  108.  */
  109. static char *copy ( source, destination )
  110. char *source, *destination;
  111. {
  112.   FILE *sp, *dp;
  113.   int c;
  114.  
  115.   if ( ! destination )
  116.   {
  117.     char temp[6];
  118.  
  119.     (void) strncpy ( temp, file, 5 );
  120.     temp[5] = '\0';
  121.     destination = tempnam ( "/tmp", temp );
  122.   }
  123.   if ( ! destination )
  124.   {
  125.     perror ( "Couldn't get a temp file" );
  126.     return (char *) 0;
  127.   }
  128.   if ( ! ( sp = fopen ( source, "r" ) ) )
  129.   {
  130.     perror ( "Couldn't open source file" );
  131.     return (char *) 0;
  132.   }
  133.   if ( ! ( dp = fopen ( destination, "w" ) ) )
  134.   {
  135.     perror ( "Couldn't open destination file" );
  136.     (void) fclose ( sp );
  137.     return (char *) 0;
  138.   }
  139.   while ( ( c = getc ( sp ) ) != EOF )
  140.     if ( putc ( (char) c, dp ) == EOF )
  141.     {
  142.       puts ( "Couldn't copy to destination file." );
  143.       (void) fclose ( dp );
  144.       (void) fclose ( sp );
  145.       return (char *) 0;
  146.     }
  147.   (void) fclose ( sp );
  148.   if ( fclose ( dp ) == EOF )
  149.   {
  150.     perror ( "Error closing destination file" );
  151.     return (char *) 0;
  152.   }
  153.   return destination;
  154. }
  155.  
  156. /*  byte changed ( i_size )
  157.  *
  158.  *  This will compare the text_file against the copy, and returns 0 if they
  159.  *  are identical in content, otherwise 1.  That is, it also returns 1 if
  160.  *  either file couldn't be read or some other error occured.  It also
  161.  *  contains code checking for an excessive reduction in the size of the copy,
  162.  *  which asks the user to confirm that he wants to keep the smaller file.
  163.  *
  164.  *  i_size  :  inital size of text_file.
  165.  */
  166. static byte changed ( i_size )
  167. int i_size;
  168. {
  169.   FILE *tp, *cp;
  170.   int c;
  171.   byte result = 0;
  172.   struct stat final;
  173.   
  174.   if ( stat ( text_copy, &final ) == 0 )
  175.     if ( smaller ( final.st_size, i_size ) )
  176.     {
  177.       char ans[10];
  178.       
  179.       while ( 1 )             /* leave loop by 'break' */
  180.       {
  181.         (void) printf ( "The %s file is now somewhat smaller - from %d down to %d bytes.\nDo you want to keep it like this? ", file, i_size, final.st_size );
  182.         result = 1;
  183.         if ( ! fgets ( ans, 10, stdin ) )
  184.           *ans = '\0';
  185.         if ( ! strncmp ( "no", ans, 2 ) )
  186.         {
  187.           (void) unlink ( text_copy );
  188.           (void) free ( text_copy );
  189.           result = 0;
  190.           break;
  191.         }
  192.         if ( ! strncmp ( "yes", ans, 3 ) )
  193.         {
  194.           result = 2;         /* flag proceed as normal */
  195.           break;
  196.         }
  197.         puts ( "Answer 'yes' or 'no'.\n" );
  198.       }
  199.       if ( result < 2 )
  200.       {
  201.         (void) unlink ( text_copy );
  202.         (void) free ( text_copy );
  203.         return result;
  204.       }
  205.     }
  206.  
  207.   if ( result < 2 )
  208.   {
  209.     if ( ! ( tp = fopen ( text_file, "r" ) ) )
  210.       result = 1;
  211.     else
  212.     {
  213.       if ( ! ( cp = fopen ( text_copy, "r" ) ) )
  214.         result = 1;
  215.       else
  216.       {
  217.         do
  218.         {
  219.           if ( ( c = getc ( tp ) ) != getc ( cp ) )
  220.           {
  221.             result = 1;
  222.             break;
  223.           }
  224.         }
  225.         while ( c != EOF );
  226.         (void) fclose ( cp );
  227.       }
  228.       (void) fclose ( tp );
  229.     }
  230.   }
  231.  
  232.   if ( ! copy ( text_copy, text_file ) )
  233.     result = 0;
  234.   (void) unlink ( text_copy );
  235.   (void) free ( text_copy );
  236.   return result;
  237. }
  238.  
  239. /*  void do_edit ( kind, extra_file )
  240.  *
  241.  *  This is the routine that actually lets you edit the gosip file.  It
  242.  *  fork()s off a process which becomes the editor.  While that is running it
  243.  *  check every ten seconds whether the DOWN_FILE exists, and stops the edit
  244.  *  if it does.
  245.  *
  246.  *  It also updates last_file & history_file, checks for files that shouldn't
  247.  *  be in the directory and removes them, prints info about the file (not yet
  248.  *  implemented), and deals with the file already being edited - normally by
  249.  *  calling catfile(), but see desciption of 'kind' below.
  250.  *
  251.  *  kind        :  specifes the type of edit.  It is an enumerated type
  252.  *                 covering
  253.  *                   normal - straight edit
  254.  *                   abort - do not invoke catfile() if gosip is being edited.
  255.  *                   cflag - use emacs with extra_file as editor.
  256.  *  extra_file  :  this file is passed to emacs as well as text_file if kind
  257.  *                 is cflag.  This is only used by Geoff's program.
  258.  */
  259. void do_edit ( kind, extra_file )
  260. enum edit_type kind;
  261. char *extra_file;
  262. {
  263.   int mask, init_size = -1;
  264.   FILE *fp;
  265.   time_t now;
  266.   struct stat info;
  267.   byte lock_status;
  268.  
  269.   mask = sigblock ( sigmask ( SIGALRM ) | sigmask ( SIGCHLD ) );
  270.   (void) signal ( SIGINT, SIG_IGN );
  271.   (void) signal ( SIGCHLD, child );
  272.   (void) signal ( SIGTERM, tidy_up );
  273.   (void) signal ( SIGHUP, tidy_up );
  274.   (void) signal ( SIGQUIT, tidy_up );
  275.   if ( ( lock_status = lock ( CREAT ) ) == -1 )
  276.   {                             /* error creating lock */
  277.     (void) printf ( "Please refer to your local %s maintainer.\n", file );
  278.     exit ( 2 );
  279.   }
  280.   if ( lock_status )
  281.   {                             /* gosip is already being edited */
  282.     if ( kind == abort || kind == cflag )
  283.     {
  284.       lastedit ( EDIT );
  285.       exit ( 1 );
  286.     }
  287.     else
  288.       catfile ( being_edited );
  289.   }
  290.   lastedit ( INFO );
  291.   (void) putchar ( '\n' );
  292.  
  293.   if ( fp = fopen ( last_file , "w" ) )
  294.   {                             /* put user into last_file */
  295.     (void) fprintf ( fp, "%s (%s)\n", gosname(), usercode() );
  296.     if ( fclose ( fp ) == -1 )
  297.       perror ( "Problem closing last file" );
  298.   }
  299.   else
  300.     perror ( "Couldn't write last usage info" );
  301.  
  302.   (void) time ( &now );
  303. #if 0
  304.   manifesto();                  /* print info about this gosip file */
  305. #endif
  306.   if ( stat ( text_file, &info ) == 0 )
  307.     init_size = info.st_size;   /* store initial size for history info. */
  308.   if ( ! ( text_copy = copy ( text_file, (char *) 0 ) ) )
  309.     exit ( 2 );                 /* make copy which is actually edited */
  310.  
  311.   if ( ( editor = fork() ) == -1 )
  312.   {
  313.     perror ( "Fork failed" );
  314.     (void) lock ( REMVE );
  315.     (void) unlink ( text_copy );
  316.     exit ( 2 );
  317.   }
  318.   if ( editor == 0 )
  319.   {
  320.     char *visual;
  321.  
  322.     if ( ! ( visual = getenv ( "VISUAL" ) ) )
  323.       if ( ! ( visual = getenv ( "EDITOR" ) ) )
  324.         visual = DEFAULT_EDITOR;
  325.     if ( kind == cflag )
  326.       execlp ( visual, visual, extra_file, text_copy, (char *) 0 );
  327.     else
  328.       execlp ( visual, visual, text_copy, (char *) 0 );
  329.     (void) fprintf ( stderr, "Couldn't invoke your editor.\n" );
  330.     perror ( visual );
  331.     exit ( 2 );
  332.   }
  333.  
  334.   (void) signal ( SIGALRM, do_nothing );
  335.   while ( ! edit_over )         /* wait for child to die */
  336.   {
  337.     static byte mes_delivered = 0;
  338.     FILE *fp;
  339.  
  340.     if ( ! mes_delivered )
  341.       (void) alarm ( 10 );
  342.     (void) sigpause ( mask );
  343.     if ( ! ( mes_delivered || edit_over ) )
  344.       if ( fp = fopen ( DOWN_FILE, "r" ) )
  345.       {
  346.         char mes[80], *temp;
  347.  
  348.         (void) fgets ( mes, 79, fp );
  349.         if ( temp = rindex ( mes, '\n' ) )
  350.           *temp = '\0';
  351.         (void) fclose ( fp );
  352.         (void) printf ( "\r\n\007** %s is going down for maintenance. **\r\n", file );
  353.         (void) printf ( "      ... %s.  \r\n", mes );
  354.         puts ( "** Please stop editing now. **\r" );
  355.         (void) printf ( "** You will probably be informed when %s is available again. **\r\n", file );
  356.         mes_delivered = 1;
  357.         (void) add_user();
  358.         (void) signal ( SIGALRM, force_off );
  359.         (void) alarm ( 50 );
  360.       }
  361.   }
  362.  
  363.   (void) alarm ( 0 );
  364.   (void) sigsetmask ( mask );
  365.   update_history ( now, init_size, changed ( init_size ) ? edit : no_change );
  366.   (void) lock ( REMVE );
  367.  
  368.   exit ( 0 );
  369. }
  370.