[comp.sources.misc] v07i061: gossip - simple message base

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/08/89)

Posting-number: Volume 7, Issue 61
Submitted-by: mauhk@cu.warwick.ac.uk (Andreas Pagel)
Archive-name: gossip/part01

Gossip is a program that allows a user community to interchange lively debate,
friendly banter, helpful insights, or anything else, by means of editing a
globally writable (set of) file(s).  It is much friendlier than posting news
articles to local groups or with local distribution and the resulting
discussion is much more informal.

More details are contained in README and other files.

Andreas Pagel                        Magician, Programmer and Mathematician
UUCP:   ...!mcvax!ukc!warwick!mauhk            	      University of Warwick
JANET:  mauhk@uk.ac.warwick.cu                        Coventry
ARPA:   mauhk%cu.warwick@nss.cs.ucl.ac.uk             Great Britain
--------------------------------cut here--------------------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  History Makefile README allgoss.1 cat.c cat.h control.c
#   control.h dw dw.1 edit.c edit.h global.h gosip.1 history.c
#   history.h lu.1 lu.c lu.h main.c main.h notes setup util.c util.h
# Wrapped by mauhk@orchid on Tue Jun 13 17:16:44 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'History' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'History'\"
else
echo shar: Extracting \"'History'\" \(2950 characters\)
sed "s/^X//" >'History' <<'END_OF_FILE'
XAPS Gosip: History
X==================
X
XThe original idea was conceived by Jonathan Hughes of Warwick University, back
Xin 1987.  He simply created two writable files, gossip and help, in his home
Xdirectory and invited people to edit them: gossip for general chatting, and
Xhelp for queries about the UNIX system or programming assignments.
X
XI soon realised that if two people edited a file simultaneously, only one
Xperson's changes would be saved.  So I wrote my first ever shell program,
Xsomething like:
X
Xif test -f tmp.gossip
Xthen
X  echo "Sorry, gossip is being edited."
X  cat text.gossip
Xelse
X  touch tmp.gossip
X  ded text.gossip     # ded is the local editor
X  rm temp.gossip
Xfi
X
Xand a similar one for the help file.  Took me weeks to get that right!  This
Xevolved for a bit, but didn't really get drastically better.  As more gosip
Xfiles were created, Geoff Rimmer had the idea of keeping just one version of
Xthe source, hardlinked to all the different gosip files, with all occurrences
Xof "gossip" replaced by "$0".  He also thought of some options to give it, eg.
Xto send the file to a printer, or to list it rather than edit it.
X
XMike Taylor deserves a mention as being the one to polish up this basic idea
Xover the next year or so.  He added in bits to keep a record of who edited
Xwhich file, and he wrote a separate shell program (lu) that would list this
Xinformation, and would also print a short summary of the status of each file
X(the basic help and gossip files soon expanded to include events, suggest,
Xjokes, etc.).
X
XSome of these early versions had some pretty bad bugs in.  Of particular note
Xwas the time that the shell script was made setuid, in order to prevent a
Xcertain person from deleting text.gossip.  Of course, one could then spawn a
Xsetuid shell from the editor - I had about a week of access to Mike's code
Xbefore he finally noticed this security hole.
X
XA major problem all the time was that the lock files/directories (both were
Xused at various stages) used to ensure exclusive access would frequently not
Xbe deleted after a given file was no longer being edited.  The final version
Xof the shell script for gosip included code that would delete lock files more
Xthan half an hour old, on the assumption that their continued existence was a
Xmanifestation of this problem.  There were also a few other bugs in it, and it
Xwas quite slow too.
X
XAt this point, I began to re-write the whole thing in C.  I was still fairly
Xnew to C at that stage of my life, and tried simply to convert the shell
Xversion into C, almost on a line for line basis.  The program went through a
Xlot of refinements, most of which are recorded in the file 'notes' - at
Xaround version 31 I had the brilliant idea of keeping a list of my changes in
Xthis file.
X
XFor the user, the C version offered greater speed, and that was about all.
XHowever, the mutual exclusion problem was finally solved in version 73, and
Xthere was much rejoicing.
X
XAndreas Pagel.
END_OF_FILE
if test 2950 -ne `wc -c <'History'`; then
    echo shar: \"'History'\" unpacked with wrong size!
fi
# end of 'History'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(955 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for gosip
X
XCFLAGS=
Xsources=edit.c main.c util.c lu.c history.c cat.c control.c
Xobjects=edit.o main.o util.o lu.o history.o cat.o control.o
X
Xreal: $(sources)
X	$(CC) -O $(CFLAGS) -o real -DREAL $(sources)
X	strip real
X	chmod 711 real
X	rm *.o
X
Xedit.o: edit.h edit.c cat.h control.h util.h global.h history.h
X
Xcat.o: cat.c cat.h util.h global.h history.h
X
Xmain.o: main.c cat.h edit.h control.h util.h global.h main.h lu.h
X
Xutil.o: util.c util.h global.h
X
Xcontrol.o: control.c control.h util.h global.h
X
Xlu.o: lu.c lu.h global.h cat.h main.h util.h history.h
X
Xhistory.o: history.c global.h history.h util.h
X
X
X#  below here is what I use to compile test versions.
X#  Note that for testing I use a different DATA_FILE_DIRECTORY, and
X#  this is reflected by my not using -DREAL when compiling.
X
Xgosip: $(objects)
X	$(CC) $(CFLAGS) $(objects) -o gosip
X	chmod 711 gosip
X	/usr/bin/rm lu
X	ln gosip lu
X
Xfull:
X	lint -u $(sources)
X	$(MAKE) gosip
X	chmod 600 *.o
END_OF_FILE
if test 955 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(4832 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XAPS Gosip
X=========
X
XGosip is a program that allows a user community to interchange lively debate,
Xfriendly banter, helpful insights, or anything else, by means of editing a
Xglobally writable (set of) file(s).  Gosip itself is also a term used to stand
Xfor any particular file's name, a bit like the shell meta character ``*''. [1]
X
XThe file 'History' gives some details on how the whole thing evolved, but the
Xbasic idea is that the compiled program is (hard-)linked to whatever files you
Xwish to support, eg. help.  Then when anyone runs 'help', the program invokes
Xan editor on the file text.help while ensuring exclusive access to that file.
X
XObviously the binaries need to be kept in a directory which is in every
Xpotential user's path.  Here at Warwick, we have a system called 'Newwords+'
Xwhich provides not only gosip, but quite a few other extra commands written by
Xthe users for the user community.  It basically works by putting a
Xsubdirectory of the newwords+ administrator in every subscriber's path.
X
XContents
X========
X
XIn the shar file you should find the source files edit.c main.c util.c lu.c
Xhistory.c cat.c control.c with corresponding header files plus global.h.  Dw
Xis a shell script that displays changes in gosip files [3], and finally setup
Xis a shell program that installs gosip files (more below).
X
XIn addition, the following manual pages are supplied: allgoss.1, lu.1, dw.1,
Xgosip.1.  Other documentation provided: README you already know about, History
Xas mentioned above and notes, which is mainly a record of changes by version
Xnumber.
X
XCustomisation
X=============
X
XYou will need to alter the definition of DATA_FILE_DIRECTORY in global.h to
Xpoint to a place where you wish to keep the files, and MAX_FILE_LENGTH will
Xneed to be altered to reflect your choice of file names.  You might also want
Xto specify yourself as SUPER_USER in control.h.  SUPER_USER is the only one
Xwho can use the -# and -@ options, which are used to deny or restore access to
Xgosip (normally used when a new version is being installed).
X
XFor quite a while lockf() did not work on our system.  I have left in the
Xprevious locking code, based on lock files and flock(), which you can use if
Xyour lockf() is broken: add 'CFLAGS=-DLOCKF_BROKEN' to the makefile, and alter
X'setup' so that DATA_FILE_DIRECTORY has global write permission.  [2]
X
XIn init_file_names(), you might also like to change the names of the history
Xand last files, and of the directory in which they reside.  The history and
Xlast files should not be edited directly by anyone: giving them unusual names
Xin an unreadable directory makes this less likely to occur.
X
XSetup needs to be edited to reflect your choice of DATA_FILE_DIRECTORY and of
Xnames for history and last files (and lock files if used).
X
XThe manual pages will need a few minor changes: you will need to alter the
Xlist of gosip files to specify those you choose to provide.
X
XOf course, they may be lots of portability problems.  I don't know which
Xsystem calls are specific to this version of UNIX (SunOS 4.0.1).
X
XInstallation
X============
X
XTo install gosip, run "setup gosip", where gosip should be replaced with
Xwhatever files you wish to install.  Setup will create the directory for data
Xfiles if it doesn't already exist - note that it must be owned by you.  The
Xdirectory for data files should not be used to keep anything other than what
Xgosip puts there.
X
XProblems
X========
X
XAs I've already said, I really don't know what system calls will or will not
Xwork on your system.  I will be pleased to receive bug reports or suggestions
Xfor modifications mailed to 'mauhk@uk.ac.warwick'.  However, since term ends
Xon 1 July 1989 and I will go home then, I will not be able to reply to mail
Xreceived after that date.  I do, though, expect to come back for a day at some
Xpoint during the summer so I will still see any mail sent.
X
XAndreas Pagel                        Magician, Programmer and Mathematician
XUUCP:   ...!mcvax!ukc!warwick!mauhk                   University of Warwick
XJANET:  mauhk@uk.ac.warwick                           Coventry
XARPA:   mauhk%cu.warwick@nss.cs.ucl.ac.uk             Great Britain
X
X
XFootnotes
X=========
X
X[1]  Gosip files used to be known as "the gossip files" in the manual pages
X     and colloquially.  When I rewrote gosip in C, I needed to compile test
X     versions.  Obviously I needed a name other than gossip by which to call
X     the binary, so I chose gosip.  From this, the current usage of gosip
X     arose.
X
X[2]  I'm not sure whether this will suffice.  I haven't got time to test that
X     aspect, since I'm running out of time in which to post the program.
X
X[3]  Dw's name comes from 'dw gosip' meaning "deal with gosip".  I understand
X     that Bourne shell functions are not universal, so this program may be
X     difficult to port to some systems.
END_OF_FILE
if test 4832 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'allgoss.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'allgoss.1'\"
else
echo shar: Extracting \"'allgoss.1'\" \(382 characters\)
sed "s/^X//" >'allgoss.1' <<'END_OF_FILE'
X.TH ALLGOSS 1 "25 February 1989"
X.SH NAME
Xallgoss \- prints a list of all gosip files
X.SH SYNOPSIS
Xallgoss
X.SH DESCRIPTION
Xallgoss prints a list of all gosip files.
X.SH SEE ALSO
Xdw(1), lu(1), gossip(1), events(1), etc.
X.SH BUGS
XIf the gosip file 'frog' exists, but the corresponding 'text.frog' has been
Xdeleted by some unfriendly lifeform, frog will not be included in the output.
END_OF_FILE
if test 382 -ne `wc -c <'allgoss.1'`; then
    echo shar: \"'allgoss.1'\" unpacked with wrong size!
fi
# end of 'allgoss.1'
fi
if test -f 'cat.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cat.c'\"
else
echo shar: Extracting \"'cat.c'\" \(2794 characters\)
sed "s/^X//" >'cat.c' <<'END_OF_FILE'
X#include <signal.h>
X#include <sys/wait.h>
X
X#include "global.h"
X#include "cat.h"
X#include "util.h"
X#include "history.h"
X
X/*  void page()
X *
X *  If the stdout is connected to a terminal, page will fork off the pager in
X *  the environment variable PAGER, or more(1) if PAGER is not defined.  It
X *  makes all subsequent output to stdout go through the pager.  Any errors,
X *  eg. from fork() or pipe(), cause an exit.  Note that the parent becomes
X *  the pager, and that the return happens in the child.
X */
Xvoid page()
X{
X  int filedes[2], pid;
X  char *pager = getenv ( "PAGER" ), *tmp;
X
X  if ( ! isatty ( 1 ) )
X    return;
X  error ( pipe ( filedes ), "Couldn't create pipe" );
X  error ( pid = fork (), "Couldn't fork" );
X  if ( pid == 0 )
X  {
X    error ( close ( filedes[0] ), "Close failed" );
X    error ( dup2 ( filedes[1], 1 ), "dup2 failed for output" );
X    error ( close ( filedes[1] ), "Close failed" );
X    return;                     /* error() will cause an exit if anything */
X  }                             /* fails, which closes file descriptors. */
X
X  error ( close ( filedes[1] ), "Close failed" );
X  error ( dup2 ( filedes[0], 0 ), "dup2 failed for input" );
X  error ( close ( filedes[0] ), "Close failed" );
X  if ( ! pager )                /* here though, there should really be a */
X    pager = "more";             /* kill(pid) before an exit, not error() */
X  else                          /* get just the pager, not options */
X    for ( tmp = pager; *tmp != '\0'; tmp++ )
X      if ( *tmp == ' ' )
X        *tmp = '\0';
X  execlp ( pager, pager, (char *) 0 );
X  (void) fprintf ( stderr, "Couldn't invoke your pager.\n" );
X  perror ( pager );
X  (void) kill ( SIGTERM, pid );
X  exit ( 2 );
X}
X
X/*  void catfile ( type )
X *
X *  Catfile will print the gosip file, using the program in the environment
X *  variable PAGER to display it.  It forks off a process to run the pager and
X *  feeds it, via a pipe, a linefeed, the existing gosip file, and another
X *  linefeed.
X *
X *  type  :  controls the type of message passed to lastedit() and history.
X */
Xvoid catfile ( type )
Xenum edit_type type;
X{
X  FILE *fp;
X
X  if ( type != raw )
X  {
X    update_history ( time ( (time_t *) 0 ), 0, type == being_edited ? edit_failed : list );
X    page();
X    if ( type == being_edited ) /* can't just use lastedit ( AUTO ) here, */
X      lastedit ( EDIT );        /* since status might have changed. */
X    else
X      lastedit ( AUTO );
X    (void) putchar ( '\n' );
X  }
X
X  if ( fp = fopen ( text_file, "r" ) )
X  {
X    int c;
X
X    while ( ( c = getc ( fp ) ) != EOF )
X      (void) putchar ( (char) c );
X    (void) fclose ( fp );
X  }
X  else
X  {
X    (void) printf ( "<Couldn't access %s text file.>\n\n", file );
X    exit ( 2 );
X  }
X
X  if ( type != raw )
X    (void) putchar ( '\n' );
X  exit ( 0 );
X}
END_OF_FILE
if test 2794 -ne `wc -c <'cat.c'`; then
    echo shar: \"'cat.c'\" unpacked with wrong size!
fi
# end of 'cat.c'
fi
if test -f 'cat.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cat.h'\"
else
echo shar: Extracting \"'cat.h'\" \(1080 characters\)
sed "s/^X//" >'cat.h' <<'END_OF_FILE'
X/*  The cat module contains the code that will simply print the gosip file.
X */
X
X/*  void page()
X *
X *  Page causes all subsequent output to be piped via a pager.  If the
X *  environment variable PAGER is set, it is used (without options), otherwise
X *  more(1).
X */
Xextern void page();
X
X/*  void catfile ( type )
X *
X *  The function prints lastedit information, and then the gosip file itself.
X *  It never returns, but calls exit().
X *
X *  type  :  controls the last usage message text.
X */
Xextern void catfile();
X
Xenum edit_type      /* used to specify type of edit.  */
X{
X  normal,           /* default edit */
X  being_edited,     /* tells catfile() to send "being edited" string */
X                    /* to lastedit() */
X  abort,            /* tells edit() not to call catfile() if gosip is being
X                       edited. */
X  raw,		    /* tells catfile to print gosip file without any extra
X		       information, and without a pager */
X  cflag             /* means gosip was invoked with the c flag.  Used
X                       exclusively by Geoff's program. */
X};
END_OF_FILE
if test 1080 -ne `wc -c <'cat.h'`; then
    echo shar: \"'cat.h'\" unpacked with wrong size!
fi
# end of 'cat.h'
fi
if test -f 'control.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'control.c'\"
else
echo shar: Extracting \"'control.c'\" \(4456 characters\)
sed "s/^X//" >'control.c' <<'END_OF_FILE'
X#include "global.h"
X#include "control.h"
X#include "util.h"
X
X/*  byte parse ( line, code, term, file )
X *
X *  This routine will parse the line its given, picking out usercode, terminal
X *  and file.  The format of the line should be ":code,term,file!".  If term
X *  is a null pointer, just the code will be returned.
X *
X *  Parse() returns 0 if the line didn't begin with a ':', otherwise it
X *  segmentation faults if the rest of the line doesn't contain the right
X *  number of commas and exclamation marks.  If it does, parse() returns 1.
X *
X *  line  :  points to the line to be parsed.
X *  code  :  the code will be written where this points.
X *  term  :  the terminal will be written where this points.
X *  file  :  the file will be written where this points.
X */
Xstatic byte parse ( line, code, term, file )
Xchar *line, *code, *term, *file;
X{
X  if ( *line++ != ':' )
X    return 0;
X  while ( *line != ',' )
X    *code++ = *line++;
X  *code = '\0';
X  if ( ! term )
X    return 1;
X  while ( *++line != ',' )
X    *term++ = *line;
X  *term = '\0';
X  while ( *++line != '!' )
X    *file++ = *line;
X  *file = '\0';
X  return 1;
X}
X
X/*  byte add_user()
X *
X *  If the gosip files are down, add_user() will check DOWN_FILE for the
X *  user's code.  If he is mentioned, it will return 1.  If he isn't, add_user
X *  checks he's on a terminal, and then adds a line to DOWN_FILE in the format
X *  ":<usercode>,<terminal>,<file>!".
X */
Xbyte add_user()
X{
X  FILE *fp = fopen ( DOWN_FILE, "r+" );
X  char line[30], *term, *ttyname();
X
X  if ( ! fp )
X    return 0;
X  (void) fseek ( fp, 0L, 0 );
X  while ( fgets ( line, 29, fp ) )
X  {
X    char code[10];
X
X    if ( parse ( line, code, (char *) 0, (char *) 0 ) )
X      if ( ! strcmp ( code, usercode() ) )
X      {
X        (void) fclose ( fp );
X        return 1;
X      }
X  }
X  if ( term = ttyname ( 2 ) )   /* get the terminal stderr is attached to */
X  {                             /* stderr is least likely to be redirected */
X    struct stat info;
X
X    (void) fprintf ( fp, ":%s,%s,%s!\n", usercode(), term, file );
X    (void) fclose ( fp );
X    if ( stat ( term, &info ) == 0 ) /* make user's terminal world writeable */
X      (void) chmod ( term, (int) info.st_mode & 07777 | 02 ); /* so he can */
X  }                             /* be told when gosip is available again */
X  return 0;
X}
X
X/*  void inform_users()
X *
X *  This routine will attempt to write a message to each of the users in the
X *  DOWN_FILE, to tell them that gosip is up again.
X */
Xvoid inform_users()
X{
X  FILE *fp = fopen ( DOWN_FILE, "r" );
X  char line[40];
X
X  if ( ! fp )
X    return;
X  while ( fgets ( line, 40, fp ) )
X  {
X    char code[10], term[12], file[15];
X    FILE *wr;
X
X    if ( ! parse ( line, code, term, file ) )
X      continue;
X    (void) printf ( "Informing %s ...", code );
X    (void) fflush ( stdout );
X    if ( wr = fopen ( term, "w" ) )
X    {
X      struct stat info;
X
X      (void) fprintf ( wr, "\007\r\n** The %s file is now in service again. **\r\n", file );
X      (void) fclose ( wr );
X      if ( stat ( term, &info ) == 0 ) /* futile attempt to protect the */
X        (void) chmod ( term, (int) info.st_mode & 07775 ); /* terminal again */
X      puts ( " done." );
X    }
X    else
X      puts ( " couldn't write his terminal." );
X  }
X  (void) fclose ( fp );
X}
X
X/*  void check_if_up()
X *
X *  Checks for the presence of DOWN_FILE.  If it exists, it indicates that
X *  gosip is down.  If the user is SUPER_USER, it just prints a warning
X *  message, otherwise it calls add_user() and prints an appropiate message
X *  depending on the return value.
X */
Xvoid check_if_up()
X{
X  if ( access ( DOWN_FILE, F_OK ) == -1 )
X    return;    /* ought really to check that owner == SUPER_USER */
X  else
X    if ( ! strcmp ( usercode(), SUPER_USER ) )
X    {
X      (void) printf ( "Warning: %s is down.\n\n", file );
X      return;
X    }
X  if ( add_user() )
X  {
X    (void) printf ( "\007I've already told you that the %s files are down.\n", file );
X    puts ( "You'll be told when service is restored, provided your terminal is writeable." );
X  }
X  else
X  {
X    FILE *fp = fopen ( DOWN_FILE, "r" );
X    char mes[80], *temp;
X    extern char *rindex();
X
X    (void) fgets ( mes, 80, fp );
X    if ( temp = rindex ( mes, '\n' ) )
X      *temp = '\0';
X    (void) printf ( "Sorry, the %s file is down for maintenance.\n", file );
X    (void) printf ( "\t... %s.\n", mes );
X    puts ( "You will probably be informed when it returns to service." );
X  }
X  exit ( 1 );
X}
END_OF_FILE
if test 4456 -ne `wc -c <'control.c'`; then
    echo shar: \"'control.c'\" unpacked with wrong size!
fi
# end of 'control.c'
fi
if test -f 'control.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'control.h'\"
else
echo shar: Extracting \"'control.h'\" \(1486 characters\)
sed "s/^X//" >'control.h' <<'END_OF_FILE'
X/*  This module contains routines that can be used to stop people from using
X *  gosip.  This would be needed, for example, if a new version was to be
X *  installed which used a different lock mechanism.
X */
X
X/*  byte add_user()
X *  
X *  If the gosip files are down, add_user() will check DOWN_FILE for the
X *  user's code.  If he is mentioned, it will return 1.  If he isn't,
X *  add_user() checks he's on a terminal, and then adds a line to DOWN_FILE in
X *  the format ":<usercode>,<terminal>,<file>!".
X */
Xextern byte add_user();
X
X/*  void inform_users()
X *
X *  This routine will attempt to write a message to each of the users in the
X *  DOWN_FILE, to tell them that gosip is up again.
X */
Xextern void inform_users();
X
X/*  void check_if_up()
X *
X *  Checks for the presence of DOWN_FILE.  It's existance would indicate that
X *  gosip is down.  If the user is SUPER_USER, it just prints a warning
X *  message, otherwise an entry is added to DOWN_FILE, if necessary, and an
X *  appropiate message printed.
X */
Xextern void check_if_up();
X
X#ifdef REAL
X#define DOWN_FILE "/tmp/sutech" /* DOWN_FILE exists when gosip is down. */
X#else REAL                      /* It is created by the -# option and */
X#define DOWN_FILE "/tmp/frog"   /* removed by the -@ option. */
X#endif REAL
X
X#define SUPER_USER "mauhk"      /* SUPER_USER is immune to gosip being down */
X                                /* and is the only one who can use the -# */
X                                /* and -@ options. */
END_OF_FILE
if test 1486 -ne `wc -c <'control.h'`; then
    echo shar: \"'control.h'\" unpacked with wrong size!
fi
# end of 'control.h'
fi
if test -f 'dw' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dw'\"
else
echo shar: Extracting \"'dw'\" \(2357 characters\)
sed "s/^X//" >'dw' <<'END_OF_FILE'
Xtrap tidy 2
X
Xallfiles=`allgoss`
Xmesg n
X
Xtidy()
X{
X  echo $0: Tidying ...
X  echo 1 > pid.*
X  for pid in pid.*
X  do
X    kill `cat $pid`
X    rm -f $pid
X  done
X  rm -f pid.*
X  for file in $allfiles
X  do
X    rm -f text.$file diff.$file
X  done
X  mesg y
X  exit 1;
X}
X
Xcheck()
X{
X  trap '' 18        #  ignore suspend
X  touch $1
X  diff -b -t -c4 $1 text.$1 > diff.$1 && rm diff.$1
X  rm pid.$1
X}
X
Xobtain()
X{
X  if test -f text.$1
X  then
X    echo Already checking $1.
X  else
X    if $1 -z > text.$1
X    then
X      chmod 666 text.$1
X      check $1 &
X      echo $! > pid.$1
X    else
X      echo no $1
X    fi
X  fi
X}
X
Xshow()
X{
X  if test -f diff.$1
X  then
X    echo "$1 changed:"
X    less diff.$1
X    mv diff.$1 sutech
X  else
X    echo "$1 unchanged."
X  fi
X  echo ''
X  lu -4 $1 | cat      # | cat  means lu does not try to fork less.
X  badans=init
X  while test -n "$badans"
X  do
X    echo -n "Command? [bneEqQcCa] "
X    badans=''
X    read ans
X    case $ans in
X      ''|b) mv text.$1 $1
X            echo Backed up $1.;;
X      n) rm text.$1
X         echo Failed to backup $1;;
X      c) mv text.$1 $1
X         echo "Backed up $1."
X         obtain $1
X         echo "Checking $1 again.";;
X      C) rm text.$1
X         obtain $1
X         echo "Checking $1 again.";;
X      e) mv text.$1 $1
X         echo "Backed up $1 and calling $1."
X         $1 -c/poppy/ma/uhk/.backups/sutech   # gosip invokes emacs with the
X         obtain $1                            # diffs in another buffer.
X         echo "Rechecking $1.";;
X      E) rm text.$1
X         echo "Calling $1."
X         $1 -c/poppy/ma/uhk/.backups/sutech
X         obtain $1
X         echo "Rechecking $1.";;
X      Q) echo Quitting.
X         tidy $0;;
X      q) mv text.$1 $1
X         echo "Backed up $1 and quitting."
X         tidy $0;;
X      a) echo -n 'Add which file? '
X         read file
X         obtain $file
X         badans=again;;
X      *) echo Bad answer.
X         badans=yes;;
X    esac
X  done
X  rm -f sutech
X}
X
Xcd /poppy/ma/uhk/.backups
Xif test $# -eq 0
Xthen
X  set $allfiles
Xfi
Xecho -n "Copying "
Xfor file
Xdo
X  rm -f text.$file
X  echo -n .
X  obtain $file
Xdone
Xecho " done."
Xset $allfiles
Xmore=true
Xwhile $more
Xdo
X  more=false
X  for file
X  do
X    if test -f text.$file
X    then
X      if test -f pid.$file
X      then
X        sleep 2
X      else
X        show $file
X      fi
X      more=true
X    fi
X  done
Xdone
Xecho The End.
Xmesg y
END_OF_FILE
if test 2357 -ne `wc -c <'dw'`; then
    echo shar: \"'dw'\" unpacked with wrong size!
fi
# end of 'dw'
fi
if test -f 'dw.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dw.1'\"
else
echo shar: Extracting \"'dw.1'\" \(2672 characters\)
sed "s/^X//" >'dw.1' <<'END_OF_FILE'
X.TH DW 1 "10 June 1989"
X.SH NAME
Xdw - show changes in the gosip files.
X.SH SYNOPSIS
X.B dw [<gosip file> [<gosip file> ...]]
X.SH DESCRIPTION
XDw uses diff(1) to show changes in the specified gosip files.  This
Xsaves you having to wade through 40K of file just to find that the
Xonly change is a spelling correction.  On its own, dw will show
Xcontext diffs for all gosip files (obtained from allgoss(1)); with
Xarguments, differences for those files only.  Note that the diffs are
Xrun asynchronously, so doing dw for a lot of files will push the load
Xaverage up quite a bit.
X.sp
XThe copies of the gosip files are kept in $HOME/.backups.  If you do
Xnot have such a directory, dw will not work.
X.SH USAGE
XDw first copies all the specified gosip files into $HOME/.backups, and
Xrun diff(1) in the background for each.  It then loops through all
Xgosip files, until it finds one for which diff has completed.  The
Xchanges, if any, are displayed, followed by the last four lines of
Xhistory information for that file.  You are then prompted for a command.
X.TP
X.B b
XBackup the new version of the current gosip file.  Equivalent to just
Xpressing return.
X.TP
X.B n
XDo not back this file up.
X.TP
X.B c
XBack this file up and check it again.  You might want to use this
Xcommand if the history information shows that the file has been edited
Xin the last few minutes.
X.TP
X.B C
XCheck this file again but do not back it up first.
X.TP
X.B e
XBackup this file, then edit it.  Dw will use gosip -c, with the extra
Xfile being the context diffs.  This flag causes gosip to use emacs to edit
Xthe file, and load the context diffs into a separate buffer.
X.sp
XWhen the edit finishes, dw will automatically recheck that file.  You
Xmight say, why not just back up the new version, since you presumably
Xknow what you changed, but you'd be surprised how many spelling errors
Xyou spot looking over the diffs.
X.TP
X.B E
XEdit the file but do not backup it up first.  Useful if you wish to
Xreplace some text some cheese head has deleted.
X.TP
X.B q
XBackup up the current file and quit dw.  This involves killing all
Xdiffs that may still be running, and removing any temporary files.
X.TP
X.B Q
XQuit without backing up the current file.
X.TP
X.B a
XAdd another gosip file to those being checked.  You are prompted for
Xthe gosip file you wish to add.
X.SH FILES
X.TP 20
X.B $HOME/.backups
XDirectory containing gosip file backups.
X.SH SEE ALSO
Xallgoss(1), lu(1), gossip(1).
X.SH AUTHOR
XAndreas Pagel, at Warwick University, UK.
X.SH BUGS
XThe tidying routine called by the quit option not work.  I have no
Xidea what is wrong, but quitting will entirely fail to delete all the
Xfiles it should, or indeed to do anything useful at all.
END_OF_FILE
if test 2672 -ne `wc -c <'dw.1'`; then
    echo shar: \"'dw.1'\" unpacked with wrong size!
fi
# end of 'dw.1'
fi
if test -f 'edit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'edit.c'\"
else
echo shar: Extracting \"'edit.c'\" \(13428 characters\)
sed "s/^X//" >'edit.c' <<'END_OF_FILE'
X#include <signal.h>
X#include <sys/wait.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X
X#include "edit.h"
X#include "global.h"
X#include "cat.h"
X#include "control.h"
X#include "util.h"
X#include "history.h"
X
X#include <dirent.h>
X#include <ctype.h>
X
X#define DEFAULT_EDITOR "ded"
X#define MIN_AGE 4*60*60         /* minimum age of drivel files in secs */
X
X#define smaller(a,b) a * 10 < b * 7 || b - a > 1700
X
Xextern char *strcat(), *rindex(), *strncpy();
Xextern int strncmp();
X
Xstatic int editor;              /* pid of editor child */
Xstatic byte edit_over = 0;      /* flag set by SIGCHLD */
Xstatic char *text_copy;         /* file name of text_file's copy */
X
X/*  void tidy_up()
X *
X *  Routine called on receiving a terminating signal, such as TERM, QUIT or
X *  HUP.  It releases locks, and removes backup files before quitting.  Also
X *  kills the editor.
X */
Xstatic void tidy_up()
X{
X  (void) kill ( editor, SIGTERM );
X  (void) lock ( REMVE );
X  (void) unlink ( text_copy );
X  (void) fprintf ( stderr, "\r\n\007** %s: received terminating signal - quitting.\n", file );
X  exit ( 3 );
X}
X
X/*  void force_off()
X *
X *  This routine is called when SIGALRM is received.  It prints a final
X *  warning to the user, and then kills his editor after waiting a further
X *  short time.  This will cause him to stop using gosip.
X */
Xstatic void force_off()
X{
X  puts ( "\r\n\007** Edit session terminating almost immediately. **\r" );
X  sleep ( 5 );
X  (void) kill ( editor, SIGTERM );
X}
X
X/*  void child()
X *
X *  Called whenever a SIGCHLD is received.  It checks if the editor has
X *  actually exit()ed - it might have changed state by being suspended - and
X *  sets the global flag edit_over if it has.
X */
Xstatic void child()
X{
X  if ( editor == wait3 ( (union wait *) 0, WNOHANG, (struct rusage *) 0 ) )
X    edit_over = 1;
X}
X
X/*  void do_nothing()
X *
X *  This routine does literally nothing.  It is needed as you have to call
X *  something when you get SIGALRM.
X */
Xstatic void do_nothing()
X{
X}
X
X#ifdef NOT_NEEDED
X/*  void clean_directory()
X *
X *  This routine will remove extraneous files from DATA_FILE_DIRECTORY.  Only
X *  files older than MIN_AGE are destroyed.  If a file does not begin with
X *  either "text." or "last." and continue with between 1 and MAX_GOSIP_LENGTH
X *  alphabetic characters, it is considered removable.
X */
Xstatic void clean_directory()
X{
X  DIR *dirp;
X  struct dirent *dep;
X  time_t old_date;
X  struct stat info;
X
X  if ( ! ( dirp = opendir ( "." ) ) )
X    return;
X
X  old_date = time ( (time_t *) 0 ) - MIN_AGE;
X  for ( dep = readdir ( dirp ); dep; dep = readdir ( dirp ) )
X  {
X    static char *ok_prefix[] = { "text.", "last.", "Makefil" };
X    char **name = ok_prefix;    /* don't forget to remove Makefil above */
X    byte ok_file = 0;           /* before posting */
X
X    if ( strcmp ( dep->d_name, "." ) == 0 || strcmp ( dep->d_name, ".." ) == 0 )
X      continue;
X    while ( ! ok_file && name - ok_prefix < sizeof ( ok_prefix ) / sizeof ( char * ) )
X    {
X      char *dp, *cp;
X
X      ok_file = 1;              /* check prefix */
X      for ( dp = dep->d_name, cp = *name++; *cp; cp++, dp++ )
X        if ( *cp != *dp )
X        {
X          ok_file = 0;
X          break;
X        }
X      if ( *dp++ == '\0' )      /* prefix on its own is invalid */
X        ok_file = 0;
X      if ( ok_file )            /* if prefix ok, check the rest of */
X      {                         /* the filename */
X        cp = dp;                /* used to check length later */
X        while ( *dp != '\0' ) 
X          if ( ! isalpha ( *dp++ ) )
X          {
X            ok_file = 0;
X            break;
X          }
X        if ( dp - cp >= MAX_GOSIP_LENGTH )
X          ok_file = 0;            /* filename too long */
X      }
X    }
X    if ( ! ok_file )
X      if ( stat ( dep->d_name, &info ) == 0 )
X        if ( info.st_mtime < old_date )
X          (void) unlink ( dep->d_name );
X  }
X}
X#endif
X
X/*  void manifesto()
X *
X *  Checks for the file info.gosip and prints its contents.
X *  Currently unimplemented.
X */
X/*
Xstatic void manifesto()
X{
X  FILE *info_file;
X  char info_filename[MAX_LENGTH];
X  int c;
X
X  (void) strcpy ( info_filename, "info." );
X  (void) strcat ( info_filename, file );
X  if ( info_file = fopen ( info_filename, "r" ) )
X  {
X    while ( ( c = getc ( info_file ) ) != EOF )
X      (void) putchar ( (char) c );
X    (void) fclose ( info_file );
X  }
X}
X*/
X
X/*  char *copy ( source, destination )
X *
X *  Copies the source file into the destination file.  If destination is null,
X *  a filename in /tmp is generated and used as destination.  Returns the
X *  destination file on success, otherwise a null pointer.
X *
X *  source       :  filename from which to copy.
X *  destination  :  filename to which to copy.
X */
Xstatic char *copy ( source, destination )
Xchar *source, *destination;
X{
X  FILE *sp, *dp;
X  int c;
X
X  if ( ! destination )
X  {
X    char temp[6];
X
X    (void) strncpy ( temp, file, 5 );
X    temp[5] = '\0';
X    destination = tempnam ( "/tmp", temp );
X  }
X  if ( ! destination )
X  {
X    perror ( "Couldn't get a temp file" );
X    return (char *) 0;
X  }
X  if ( ! ( sp = fopen ( source, "r" ) ) )
X  {
X    perror ( "Couldn't open source file" );
X    return (char *) 0;
X  }
X  if ( ! ( dp = fopen ( destination, "w" ) ) )
X  {
X    perror ( "Couldn't open destination file" );
X    (void) fclose ( sp );
X    return (char *) 0;
X  }
X  while ( ( c = getc ( sp ) ) != EOF )
X    if ( putc ( (char) c, dp ) == EOF )
X    {
X      puts ( "Couldn't copy to destination file." );
X      (void) fclose ( dp );
X      (void) fclose ( sp );
X      return (char *) 0;
X    }
X  (void) fclose ( sp );
X  if ( fclose ( dp ) == EOF )
X  {
X    perror ( "Error closing destination file" );
X    return (char *) 0;
X  }
X  return destination;
X}
X
X#ifdef NOT_NEEDED
X/*  void make_writable()
X *
X *  If text.gosip does not have write permission, this function will attempt
X *  to make it writable.  It first tries to chmod it, but this will fail if
X *  the user is not the owner.  In this case, it copies text.gosip into
X *  another file, owned by the user, renames that file to text.gosip, and
X *  ensures it has the correct permissions.
X */
Xstatic void make_writeable()
X{
X  (void) printf ( "Sadly %s is unwriteable, trying to fix ...\n", text_file );
X  if ( chmod ( text_file, 0666 ) == -1 )
X  {
X    char *tempfile;
X
X    if ( ! ( tempfile = copy ( text_file ) ) )
X      exit ( 2 );               /* error message provided by copy() */
X    if ( rename ( tempfile, text_file ) )
X    {
X      perror ( "Failed to install" );
X      (void) unlink ( tempfile );
X      exit ( 2 );
X    }
X      
X    error ( chmod ( text_file, 0666 ), "Couldn't alter permissions!" );
X    (void) free ( tempfile );
X  }
X  puts ( "Done." );
X  sleep ( 2 );
X}
X#endif
X
X/*  byte changed ( i_size )
X *
X *  This will compare the text_file against the copy, and returns 0 if they
X *  are identical in content, otherwise 1.  That is, it also returns 1 if
X *  either file couldn't be read or some other error occured.  It also
X *  contains code checking for an excessive reduction in the size of the copy,
X *  which asks the user to confirm that he wants to keep the smaller file.
X *
X *  i_size  :  inital size of text_file.
X */
Xstatic byte changed ( i_size )
Xint i_size;
X{
X  FILE *tp, *cp;
X  int c;
X  byte result = 0;
X  struct stat final;
X  
X  if ( stat ( text_copy, &final ) == 0 )
X    if ( smaller ( final.st_size, i_size ) )
X    {
X      char ans[10];
X      
X      while ( 1 )             /* leave loop by 'break' */
X      {
X        (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 );
X        result = 1;
X        if ( ! fgets ( ans, 10, stdin ) )
X          *ans = '\0';
X        if ( ! strncmp ( "no", ans, 2 ) )
X        {
X          (void) unlink ( text_copy );
X          (void) free ( text_copy );
X          result = 0;
X          break;
X        }
X        if ( ! strncmp ( "yes", ans, 3 ) )
X        {
X          result = 2;         /* flag proceed as normal */
X          break;
X        }
X        puts ( "Answer 'yes' or 'no'.\n" );
X      }
X      if ( result < 2 )
X      {
X        (void) unlink ( text_copy );
X        (void) free ( text_copy );
X        return result;
X      }
X    }
X
X  if ( result < 2 )
X  {
X    if ( ! ( tp = fopen ( text_file, "r" ) ) )
X      result = 1;
X    else
X    {
X      if ( ! ( cp = fopen ( text_copy, "r" ) ) )
X        result = 1;
X      else
X      {
X        do
X        {
X          if ( ( c = getc ( tp ) ) != getc ( cp ) )
X          {
X            result = 1;
X            break;
X          }
X        }
X        while ( c != EOF );
X        (void) fclose ( cp );
X      }
X      (void) fclose ( tp );
X    }
X  }
X
X  if ( ! copy ( text_copy, text_file ) )
X    result = 0;
X  (void) unlink ( text_copy );
X  (void) free ( text_copy );
X  return result;
X}
X
X/*  void do_edit ( kind, extra_file )
X *
X *  This is the routine that actually lets you edit the gosip file.  It
X *  fork()s off a process which becomes the editor.  While that is running it
X *  check every ten seconds whether the DOWN_FILE exists, and stops the edit
X *  if it does.
X *
X *  It also updates last_file & history_file, checks for files that shouldn't
X *  be in the directory and removes them, prints info about the file (not yet
X *  implemented), and deals with the file already being edited - normally by
X *  calling catfile(), but see desciption of 'kind' below.
X *
X *  kind        :  specifes the type of edit.  It is an enumerated type
X *                 covering
X *                   normal - straight edit
X *                   abort - do not invoke catfile() if gosip is being edited.
X *                   cflag - use emacs with extra_file as editor.
X *  extra_file  :  this file is passed to emacs as well as text_file if kind
X *                 is cflag.  This is only used by Geoff's program.
X */
Xvoid do_edit ( kind, extra_file )
Xenum edit_type kind;
Xchar *extra_file;
X{
X  int mask, init_size = -1;
X  FILE *fp;
X  time_t now;
X  struct stat info;
X  byte lock_status;
X
X  mask = sigblock ( sigmask ( SIGALRM ) | sigmask ( SIGCHLD ) );
X  (void) signal ( SIGINT, SIG_IGN );
X  (void) signal ( SIGCHLD, child );
X  (void) signal ( SIGTERM, tidy_up );
X  (void) signal ( SIGHUP, tidy_up );
X  (void) signal ( SIGQUIT, tidy_up );
X  if ( ( lock_status = lock ( CREAT ) ) == -1 )
X  {                             /* error creating lock */
X    (void) printf ( "Please refer to your local %s maintainer.\n", file );
X    exit ( 2 );
X  }
X  if ( lock_status )
X  {                             /* gosip is already being edited */
X    if ( kind == abort || kind == cflag )
X    {
X      lastedit ( EDIT );
X      exit ( 1 );
X    }
X    else
X      catfile ( being_edited );
X  }
X  lastedit ( INFO );
X  (void) putchar ( '\n' );
X
X  if ( fp = fopen ( last_file , "w" ) )
X  {                             /* put user into last_file */
X    (void) fprintf ( fp, "%s (%s)\n", gosname(), usercode() );
X    if ( fclose ( fp ) == -1 )
X      perror ( "Problem closing last file" );
X  }
X  else
X    perror ( "Couldn't write last usage info" );
X
X  (void) time ( &now );
X#ifdef NOT_NEEDED
X  manifesto();                  /* print info about this gosip file */
X  clean_directory();            /* remove old irrelevent files */
X  if ( access ( text_file, F_OK | R_OK ) )  
X    (void) unlink ( text_file ); /* if text.gosip is unreadable, remove it */
X  if ( ! access ( text_file, F_OK ) ) 
X    if ( access ( text_file, W_OK ) == -1 )
X      make_writeable();         /* text.gosip unwriteable is curable */
X#endif
X  if ( stat ( text_file, &info ) == 0 )
X    init_size = info.st_size;   /* store initial size for history info. */
X  if ( ! ( text_copy = copy ( text_file, (char *) 0 ) ) )
X    exit ( 2 );                 /* make copy which is actually edited */
X
X  if ( ( editor = fork() ) == -1 )
X  {
X    perror ( "Fork failed" );
X    (void) lock ( REMVE );
X    (void) unlink ( text_copy );
X    exit ( 2 );
X  }
X  if ( editor == 0 )
X  {
X    char *visual;
X
X    if ( ! ( visual = getenv ( "VISUAL" ) ) )
X      if ( ! ( visual = getenv ( "EDITOR" ) ) )
X        visual = DEFAULT_EDITOR;
X    if ( kind == cflag )
X      execlp ( visual, visual, extra_file, text_copy, (char *) 0 );
X    else
X      execlp ( visual, visual, text_copy, (char *) 0 );
X    (void) fprintf ( stderr, "Couldn't invoke your editor.\n" );
X    perror ( visual );
X    exit ( 2 );
X  }
X
X  (void) signal ( SIGALRM, do_nothing );
X  while ( ! edit_over )         /* wait for child to die */
X  {
X    static byte mes_delivered = 0;
X    FILE *fp;
X
X    if ( ! mes_delivered )
X      (void) alarm ( 10 );
X    (void) sigpause ( mask );
X    if ( ! ( mes_delivered || edit_over ) )
X      if ( fp = fopen ( DOWN_FILE, "r" ) )
X      {
X        char mes[80], *temp;
X
X        (void) fgets ( mes, 79, fp );
X        if ( temp = rindex ( mes, '\n' ) )
X          *temp = '\0';
X        (void) fclose ( fp );
X        (void) printf ( "\r\n\007** %s is going down for maintenance. **\r\n", file );
X        (void) printf ( "      ... %s.  \r\n", mes );
X        puts ( "** Please stop editing now. **\r" );
X        (void) printf ( "** You will probably be informed when %s is available again. **\r\n", file );
X        mes_delivered = 1;
X        (void) add_user();
X        (void) signal ( SIGALRM, force_off );
X        (void) alarm ( 50 );
X      }
X  }
X
X  (void) alarm ( 0 );
X  (void) sigsetmask ( mask );
X  update_history ( now, init_size, changed ( init_size ) ? edit : no_change );
X  (void) lock ( REMVE );
X
X  exit ( 0 );
X}
END_OF_FILE
if test 13428 -ne `wc -c <'edit.c'`; then
    echo shar: \"'edit.c'\" unpacked with wrong size!
fi
# end of 'edit.c'
fi
if test -f 'edit.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'edit.h'\"
else
echo shar: Extracting \"'edit.h'\" \(673 characters\)
sed "s/^X//" >'edit.h' <<'END_OF_FILE'
X/*  void doedit ( kind, extra_file )
X *
X *  This routine handles editing of the gosip file.  It takes care of all
X *  bookkeeping, and does not return - it calls exit().
X *
X *  kind        :  specifes the type of edit.  It is an enumerated type
X *                 covering
X *                   normal - straight edit
X *                   abort - do not invoke catfile() if gosip is being edited.
X *                   cflag - use emacs and extra_file as editor.
X *                 see also "cat.h"
X *  extra_file  :  this file is passed to emacs as well as the text_file if
X *                 kind is cflag.  This is only used by Geoff's program.
X */
Xextern void do_edit();
END_OF_FILE
if test 673 -ne `wc -c <'edit.h'`; then
    echo shar: \"'edit.h'\" unpacked with wrong size!
fi
# end of 'edit.h'
fi
if test -f 'global.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'global.h'\"
else
echo shar: Extracting \"'global.h'\" \(979 characters\)
sed "s/^X//" >'global.h' <<'END_OF_FILE'
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X
X#define error(result,errmes) if ( (result) == -1 ) {perror(errmes); exit(2);}
X#define MAX_GOSIP_LENGTH 8              /* max length of a gosip fileanme */
X#define MAX_FILE_LENGTH (sizeof(DATA_FILE_DIRECTORY)+MAX_GOSIP_LENGTH+13)
X            /* max. length of global file names - the additional 13 allows for
X               the longest prefix, "/Rush/text.", and terminating '\0' */
X
X#ifdef REAL
X#define DATA_FILE_DIRECTORY "/poppy/cs/upt/open/dubbin"
X#else REAL
X#define DATA_FILE_DIRECTORY "/poppy/ma/uhk/gtfd/.files"
X#endif REAL
X
Xextern char *getenv(), *rindex(), *strcpy();
Xextern time_t time();
X
Xextern char text_file[MAX_FILE_LENGTH], file[MAX_FILE_LENGTH];
Xextern char history_file[MAX_FILE_LENGTH], last_file[MAX_FILE_LENGTH];
X#ifdef LOCKF_BROKEN
Xextern char lock_file[MAX_FILE_LENGTH];
X#endif LOCKF_BROKEN
X
Xtypedef char byte;              /* use byte for small ints (-1 up to c.25) */
END_OF_FILE
if test 979 -ne `wc -c <'global.h'`; then
    echo shar: \"'global.h'\" unpacked with wrong size!
fi
# end of 'global.h'
fi
if test -f 'gosip.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'gosip.1'\"
else
echo shar: Extracting \"'gosip.1'\" \(3737 characters\)
sed "s/^X//" >'gosip.1' <<'END_OF_FILE'
X.TH GOSSIP 1 "25 February 1989"
X.SH NAME
Xelection, events, faith, flames, games, gossip, help, jokes, musik, politics,
Xsmut, suggest, xian - Print or edit the relevant file.
X.SH SYNOPSIS
X.B gosip [-e|-l|-p[<printer>]|-r|-s|-t|-v|-x]
X.SH DESCRIPTION
XGosip (general term meaning any one of events, flames, etc.) allows you to
Xedit a text file containing material contributed by other gosip users.  When
Xinvoked without flags, it tells you who last edited the file, then calls up
Xthe editor in the environment variable VISUAL, failing that EDITOR, or vi if
Xthat doesn't exist either.  At this point you edit the file, enjoying other
Xpeople's contributions, and adding your own as you wish.  When you quit the
Xeditor, gosip will store details of your edit, which you can examine using lu,
Xbefore ending.
X.sp
XYour name is taken from the environment variable GOSNAME, if defined,
Xotherwise from the variable NAME.
X.sp
XCertain general unwritten rules of etiquette regarding the editing of gosip
Xfiles have evolved over time.
X.IP +
XLeave your name or alias after your contribution.  Many people will
Xwrite '*name*' or something similar after unattributed contributions, or even
Xdelete them.  There is a list of names and aliases near the beginning of the
Xgossip file; feel free to add yours there.
X.IP +
XDoing global replaces, eg. turning all instance of 'and' to '&', is considered
Xpretty tedious.
X.IP +
XDon't delete other people's contributions, just because you don't like them.
XTo keep the size of the files reasonable, removing older material is OK, but
Xtry to leave some context for any comments following those you delete.
X.IP +
XAltering what other people have written to make them look silly isn't
Xconsidered clever or witty, but anti-social.
X.IP +
XMost people find it rather boring to read the same thing several times, so
Xdon't put an indentical piece of text in more than one gosip files.
X.PP
XNote that the symbol :-) (called a smiley face) or some variation is often
Xplaced beside text that is intended to be of a non-serious nature.
X.SH OPTIONS
X.TP
X.B \-e
XIf the gosip file is being edited, this is reported without listing it.
X.TP
X.B \-l
XList the gosip file instead of editing it.  The program in the environment
Xvariable PAGER is used to display it, or more(1) if PAGER is not defined.
X.TP
X.B \-p[<printer>]
XSend the gosip file to the (specified) printer.
X.TP
X.B \-r
XInform the user as soon as gosip becomes free for editing.
X.TP
X.B \-s
XReturn exit status 1 if the file is being edited, or zero if it isn't, without
Xproducing any output.
X.TP
X.B \-t
XReport whether gosip is being edited or not.
X.TP
X.B \-v
XPrint the version number.  Details of changes corresponding to each version
Xnumber are currently available in ~mauhk/gtfd/doc/notes - at least at
XWarwick.
X.TP
X.B \-x
XAct as though called without arguments.  It's use is that it overrides any
Xdefault argument in GOSIP.
X.SH DEFAULT OPTIONS
XIf no flag (or -x) is present, the program will attempt to edit the file.  If,
Xhowever, gosip is being edited by someone else, it will be printed instead.
X.sp
XFor historical reasons, any single letter is equivalent to the '-l'
Xoption, eg. 'gosip s' == 'gosip -l'.
X.sp
XYou can use the environment variable GOSIP to store a default option for
Xgosip.  That is, if gosip is invoked without flags, the flag stored in GOSIP
Xwill be used.
X.SH SEE ALSO
Xallgoss(1), lu(1), dw(1).
X.SH AUTHOR
XOriginal concept by Jonathan Hughes.
X.br
XBasically written by Andreas Pagel.
X.br
XSubstantially modified by Geoff Rimmer.
X.br
XCompletely rewritten by Mike Taylor.
X.br
XCompletely re-rewritten by Andreas Pagel.
X.SH BUGS
XIf you are near your hard quota when trying to edit gosip, you might
Xend up not being able to save it or even deleting it, so be careful.
END_OF_FILE
if test 3737 -ne `wc -c <'gosip.1'`; then
    echo shar: \"'gosip.1'\" unpacked with wrong size!
fi
# end of 'gosip.1'
fi
if test -f 'history.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'history.c'\"
else
echo shar: Extracting \"'history.c'\" \(5270 characters\)
sed "s/^X//" >'history.c' <<'END_OF_FILE'
X#include "global.h"
X#include "util.h"
X#include "history.h"
X
X#define RECENT 395724           /* entries older than this are pruned */
X#define MIN_LINES 40            /* history.gosip is not pruned to less than */
X                                /* this number of lines.  */
X/*  static FILE *his_prune()
X *
X *  This routine will prune history.gosip.  It first works out the number of
X *  entries it currently contains, and subtracts MIN_LINES to get the maximum
X *  number of entries to remove.  It then reads in each entry, and copies it
X *  back into the file only if it is less than RECENT seconds old.
X *
X *  If history.gosip fails to exist, his_prune() will try to create it.
X *
X *  A pointer to history.gosip is returned, or a null pointer in case of
X *  errors.
X */
Xstatic FILE *his_prune()
X{
X  FILE *his_file;
X  long start, end;
X  HIS_ENTRY entry;
X  time_t now;
X  int removable = 1;          /* number of removable lines in history.gosip */
X  struct stat info;
X
X  (void) time ( &now );
X  if ( stat ( history_file, &info ) == 0 )
X    removable = 1 + info.st_size / sizeof ( HIS_ENTRY ) - MIN_LINES;
X  if ( ! ( his_file = fopen ( history_file, "a+" ) ) )
X  {
X    perror ( "Couldn't open history file" );
X    return (FILE *) 0;
X  }
X  start = end = 0L;
X  if ( fseek ( his_file, end, 0 ) == -1 )
X  {
X    perror ( "Error seeking in history file" );
X    (void) fclose ( his_file );
X    return (FILE *) 0;
X  }
X  while ( fread ( (char *) &entry, sizeof ( entry ), 1, his_file ) )
X  {
X    end = ftell ( his_file );
X    if ( now - entry.date < RECENT || removable-- < 1 )
X    {
X      if ( fseek ( his_file, start, 0 ) == -1 )
X      {
X        perror ( "Error seeking in history file" );
X        (void) fclose ( his_file );
X        return (FILE *) 0;
X      }
X      if ( fwrite ( (char *) &entry, sizeof ( entry ), 1, his_file ) < 1 )
X      {
X        perror ( "Error writing to history file" );
X        (void) fclose ( his_file );
X        return (FILE *) 0;
X      }
X      start = ftell ( his_file );
X    }
X    if ( fseek ( his_file, end, 0 ) == -1 )
X    {
X      perror ( "Error seeking in history file" );
X      (void) fclose ( his_file );
X      return (FILE *) 0;
X    }
X  }
X  if ( fseek ( his_file, start, 0 ) == -1 )
X  {
X    perror ( "Error seeking in history file" );
X    (void) fclose ( his_file );
X    return (FILE *) 0;
X  }
X  return his_file;
X}
X
X/*  void update_history ( date, size1, how )
X *
X *  This routine is used to add an entry to history.gosip.  It also
X *  automatically creates the file if it doesn't exist, and keeps it down to a
X *  reasonable size if it does.  It is called by do_edit() and catfile() to
X *  record whenever a user read or edited the gosip file.
X *
X *  Data stored consists of the time the edit commenced, the (gosip) name and
X *  usercode of the person invoking gosip and the initial and final size of
X *  the gosip file.  The final size can be replaced by one of the following:
X *          listing - the gosip file was listed, not edited
X *      edit failed - the user tried to edit the gosip file, but it was
X *                    already being edited.  This entry is only stored if
X *                    catfile() was invoked, not if gosip was called with the
X *                    '-e' option.  The idea behind this is that an entry is
X *                    only made if a user actually got to see the text of the
X *                    gosip file.
X *        unchanged - the user did not alter the gosip file during his edit.
X *                    If the backup of the gosip is somehow deleted during the
X *                    user's edit, gosip will obviously not be able to
X *                    determine whether the file has changed, and will provide
X *                    a final size.
X *
X *  First his_prune() is called to remove older entries.  It will return a
X *  file pointer to history.gosip, leaving it pointing to the correct place in
X *  the file to add the new entry.  The new entry is then contructed - some
X *  information is passed in parameters as detailed below, the rest can be
X *  worked out.
X *
X *  Finally the new entry is written and then history.gosip is truncated to
X *  reflect its new size, and chmod() is called to give it appropiate
X *  permissions.  This should only be necessary if the file was created by
X *  update_history().
X *
X *  date   :  time of access to gosip file.
X *  size1  :  initial size of file - only relevent if how == edit.
X *  how    :  type of access.
X */
Xvoid update_history ( date, size1, how )
Xtime_t date;
Xint size1;
XMODE how;
X{
X  FILE *his_file;
X  
X  if ( his_file = his_prune() )
X  {
X    HIS_ENTRY entry;
X    struct stat info;
X    long new_size;
X
X    (void) strcpy ( entry.name, gosname() );
X    (void) strcpy ( entry.user, usercode() );
X    entry.date = date;
X    entry.how = how;
X    entry.size1 = size1;
X    entry.size2 = -1;
X    if ( stat ( text_file, &info ) == 0 )
X      entry.size2 = info.st_size;
X    if ( fwrite ( (char *) &entry, sizeof ( entry ), 1, his_file ) < 1 )
X      perror ( "Write of history information failed" );
X    new_size = ftell ( his_file );
X    if ( fclose ( his_file ) )
X      perror ( "Error closing history file" );
X    if ( truncate ( history_file, new_size ) )
X      perror ( "Error adjusting size" );
X  }
X  (void) chmod ( history_file, 0666 );
X}
END_OF_FILE
if test 5270 -ne `wc -c <'history.c'`; then
    echo shar: \"'history.c'\" unpacked with wrong size!
fi
# end of 'history.c'
fi
if test -f 'history.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'history.h'\"
else
echo shar: Extracting \"'history.h'\" \(1327 characters\)
sed "s/^X//" >'history.h' <<'END_OF_FILE'
X/*  void update_history ( date, size1, how )
X *
X *  This will add an entry to history.gosip if it can.  It will aso try to
X *  ensure that the file has the correct permissions, and will create it if it
X *  doesn't exist.  Some of the information it works out itself, the rest is
X *  passed as arguments.
X *
X *  It will also remove entries older than RECENT seconds from the file,
X *  provided at least MIN_LINES remain.
X *
X *  date   :  time of access to gosip file.
X *  size1  :  initial size of file - only relevent if how == edit.
X *  how    :  type of access.
X */
Xextern void update_history();
X
Xtypedef enum { edit, list, edit_failed, no_change } MODE;
X
Xtypedef struct                  /* entry in history.gosip */
X{
X  char name[MAX_NAME_LEN];      /* name of user */
X  char user[MAX_CODE_LEN];      /* code of user */
X  time_t date;                  /* time at which they used gosip */
X  int size1, size2;             /* sizes, before & after, of text.gosip */
X  MODE how;                     /* whether they edited or listed, etc. */
X} HIS_ENTRY;  
X
X/* Portability warning: note that the above struct may not be stored the way
X *  on different machines.  Here at Warwick, MAX_NAME_LEN had to carefully
X *  chosen to make the history entries compatible between the two computers
X *  from which gosip could be invoked.
X */
END_OF_FILE
if test 1327 -ne `wc -c <'history.h'`; then
    echo shar: \"'history.h'\" unpacked with wrong size!
fi
# end of 'history.h'
fi
if test -f 'lu.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lu.1'\"
else
echo shar: Extracting \"'lu.1'\" \(2055 characters\)
sed "s/^X//" >'lu.1' <<'END_OF_FILE'
X.TH LU 1 "25 February 1989"
X.SH NAME
Xlu \- Print history information for gosip files.
X.SH SYNOPSIS
X.B lu [-<int>[:<int>]] [gosip-file [gosip-file ...]]
X.SH DESCRIPTION
XWithout arguments, lu prints a summary of information about all gosip files.
XFor each gosip file, it gives the time when the last edit was commenced, its
Xcurrent size and the name and the usercode of the last person to edit it.  A
Xtrailing '*' indicates the file is currently being edited.
X.sp
XWith arguments, some of the recent editing history for each gosip file is
Xgiven.  Each line corresponds to a person looking at the text of the gosip
Xfile, and gives the time of access, the name and usercode of the person
Xinvolved, the initial size, and information on the type of access.  If two
Xnumbers are given, this implies an edit of the gosip file, where the two
Xnumbers are the initial and final sizes.
X.sp
X\'Listing' implies use of the '-l' option.  'Edit failed' means the user
Xattempted to edit the gosip file, only to find it was already being edited; if
Xthe '-e' option was used, an edit failed line is not recorded.  'Unchanged'
Ximplies that the user edited gosip without making any changes.
X.SH OPTIONS
XThe number of lines to print from the history file can be specified
Xby '-<number of lines>' or '-<hour>:<min>'.  The later form will list all
Xentries dated within the last <hour> hours and <min> minutes.  Either option
Xcan be given more than once, eg.
X.sp
X.RS
Xlu events -10 gossip -15 flames help
X.RE
X.sp
Xwhich will list 20 lines (the default) from events, 10 from gossip and 15 from
Xflames and help.
X.sp
XAlternatively, the usage exemplified by
X.sp
X.RS
Xlu -1:
X.RE
X.sp
Xwill print changes for the past hour for all gosip files.
X.sp
XHistory information is currently kept for about 3 days.
X.SH AUTHOR
XOriginally Mike Taylor.
X.br
XModified by Andreas Pagel.
X.br
XRewritten by Andreas Pagel and incoporated into gosip.
X.SH SEE ALSO
Xallgoss(1), gossip(1), dw(1).
X.SH BUGS
XThe usage 'lu -0 gossip' is accepted and treated as 'lu -1 gossip'.  Similar
Xproblems with options '-:' and '-'.
END_OF_FILE
if test 2055 -ne `wc -c <'lu.1'`; then
    echo shar: \"'lu.1'\" unpacked with wrong size!
fi
# end of 'lu.1'
fi
if test -f 'lu.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lu.c'\"
else
echo shar: Extracting \"'lu.c'\" \(9187 characters\)
sed "s/^X//" >'lu.c' <<'END_OF_FILE'
X#include "global.h"
X#include "lu.h"
X#include "cat.h"
X#include "main.h"
X#include "util.h"
X#include "history.h"
X  
X#include <dirent.h>
X#include <ctype.h>
X
Xextern char *ctime(), *malloc(), *strcat();
X
Xtypedef struct fmt
X{
X  union
X  {
X    time_t date;                /* parameter - secs */
X    int max_lines;              /* maximum number of lines */
X  } param;
X  byte time;                    /* whether time or length param */
X  struct fmt *next;
X} FORMAT;
X
Xstatic FORMAT *lines = NULL;
X
X/*  byte gosip_file ( file_name )
X *
X *  This routine examines file_name and returns 1 if it matches text.[a-z]+,
X *  which is what valid gosip files would match, and provided that [a-z]+ bit
X *  is not longer than MAX_GOSIP_LENGTH.  Zero otherwise.
X *
X *  file_name  :  points to filename to check.
X */
Xstatic byte gosip_file ( file_name )
Xchar *file_name;
X{
X  byte index;
X  char *temp;
X
X  for ( index = 0; index < 5; index++ )
X    if ( *file_name++ != "text."[index] )
X      return 0;
X  if ( ! *file_name )           /* prefix on its own is illegal */
X    return 0;
X  temp = file_name;             /* to work out length */
X  while ( *file_name != '\0' )
X    if ( ! isalpha ( *file_name++ ) )
X      return 0;
X  return file_name - temp <= MAX_GOSIP_LENGTH;
X}
X
X/*  byte allgoss ( files )
X *
X *  Returns an alphabetical list of gosip files in the array files.  It
X *  obtains this information by searching DATA_FILE_DIRECTORY for files
X *  matched by gosip_file().  It returns 0 if no files could be found, either
X *  because of an error or because there weren't any; otherwise it returns the
X *  number of files found.
X *
X *  files  :  the array in which the result is stored.
X */
Xbyte allgoss ( files )
Xchar *files[FILE_NUM];
X{
X  DIR *dirp;
X  struct dirent *dep;
X  byte num = 0, sorted = 0, index;
X  char text_dir[sizeof(DATA_FILE_DIRECTORY)+6];
X
X  (void) strcpy ( text_dir, DATA_FILE_DIRECTORY );
X  (void) strcat ( text_dir, "/Rush" );
X  if ( ! ( dirp = opendir ( text_dir ) ) )
X  {
X    perror ( "Couldn't read directory" );
X    return 0;
X  }
X                                /* obtain list of gosip files */
X  for ( dep = readdir ( dirp ); dep; dep = readdir ( dirp ) )
X    if ( gosip_file ( dep->d_name ) )
X    {
X      if ( num >= FILE_NUM )
X      {
X        (void) fprintf ( stderr, "Too many gosip files! (Max %d).\n", FILE_NUM );
X        return 0;
X      }
X      if ( ! ( files[num] = malloc ( (unsigned) strlen ( dep->d_name ) - 4 ) ) )
X      {
X        (void) fprintf ( stderr, "No memory.\n" );
X        return 0;
X      }
X      (void) strcpy ( files[num++], dep->d_name + 5 );
X    }
X  (void) closedir ( dirp );
X  
X  while ( ! sorted )            /* sort gosip files alphabetically */
X  {
X    char *temp;
X    
X    sorted = 1; 
X    for ( index = 1; index < num; index++ )
X      if ( strcmp ( files[index-1], files[index] ) > 0 )
X      {
X        temp = files[index-1];
X        files[index-1] = files[index];
X        files[index] = temp;
X        sorted = 0;
X      }
X  }
X  if ( ! num )
X    (void) fprintf ( stderr, "No files!\n" );
X  return num;
X}
X
X/*  void history ( gosip )
X *
X *  History will list the last n lines from the file history.gosip, where n is
X *  determined by the contents of lines.
X *
X *  This is done by building up a linked list of lines, and throwing away
X *  those that don't meet the requirements.  (**write this out properly**)
X *
X *  gosip  :  the file for which history is required.
X */
X static void history ( gosip )
Xchar *gosip;
X{
X  extern char *sprintf();
X  time_t now;
X  FILE *his_file;
X  HIS_ENTRY entry;
X  char line[120];
X  int ent_num = 0;
X  struct one_entry
X  {
X    int number;                 /* used to check line number limit */
X    char text[120];             /* the line that will be printed */
X    struct one_entry *next;
X  } *first, *last, *temp;
X  
X  first = last = 0;
X  (void) time ( &now );
X  init_file_names ( gosip );
X  if ( ! ( his_file = fopen ( history_file, "r" ) ) )
X  {
X    (void) printf ( "Couldn't open history file for %s.\n", file );
X    return;
X  }
X  (void) printf ( "Editing history of %s:\n", file );
X  while ( fread ( (char *) &entry, sizeof ( entry ), 1, his_file ) )
X  {
X    ent_num++;
X    if ( lines->time == 0 || now - entry.date < lines->param.date )
X    {
X      temp = last;
X      last = (struct one_entry *) malloc ( sizeof ( struct one_entry ) );
X      if ( ! last )
X      {
X        perror ( "No memory" );
X        return;
X      }
X      if ( temp != 0  )
X        temp->next = last;
X      last->number = ent_num;
X      last->next = 0;
X
X      (void) sprintf ( line, "%3d %.16s %8s: %-27s ", ent_num, ctime ( &entry.date ), entry.user, entry.name );
X      switch ( entry.how )
X      {
X      default:
X        (void) sprintf ( last->text, "%s    ***data error***\n", line );
X        break;
X      case list:
X        (void) sprintf ( last->text, "%s%7d      listing\n", line, entry.size2 );
X        break;
X      case edit_failed:
X        (void) sprintf ( last->text, "%s%7d  edit failed\n", line, entry.size2 );
X        break;
X      case edit:
X        (void) sprintf ( last->text, "%s%7d%13d\n", line, entry.size1, entry.size2 );
X        break;
X      case no_change:
X        (void) sprintf ( last->text, "%s%7d    unchanged\n", line, entry.size2 );
X      }
X      
X      if ( ! first )
X        first = last;
X      else
X        if ( lines->time == 0 && last->number - first->number >= lines->param.max_lines )
X        {
X          temp = first->next;
X          (void) free ( (char *) first );
X          first = temp;
X        }
X    }
X  }
X  (void) fclose ( his_file );
X  
X  while ( first )
X  {
X    (void) printf ( "%s", first->text );
X    temp = first->next;
X    (void) free ( (char *) first );
X    first = temp;
X  }
X  
X  lastedit ( AUTO );
X}
X
X/*  void summary()
X *
X *  Prints up a summary of gosip file status.  It gets a list of files from
X *  allgoss(), and prints a one line summary of their current state, taking
X *  information from history.gosip and last.gosip, as well at stat()ing
X *  text.gosip.
X */
Xstatic void summary()
X{
X  char *files[FILE_NUM];
X  byte index, num;
X  
X  if ( ! ( num = allgoss ( files ) ) )
X    exit ( 2 );
X  for ( index = 0; index < num; index++ )
X  {
X    struct stat info;
X    FILE *lfp;
X    
X    init_file_names ( files[index] );
X    (void) free ( files[index] );
X    (void) printf ( "%-*s: ", MAX_GOSIP_LENGTH + 1, file );
X    if ( stat ( last_file, &info ) == 0 )
X      (void) printf ( "%.12s ", ctime ( &info.st_mtime ) + 4);
X    else
X      (void) printf ( "%-13s", "<no date>" );
X    if ( stat ( text_file, &info ) == 0 )
X      (void) printf ( "(%6d bytes ) ", info.st_size );
X    if ( lfp = fopen ( last_file, "r" ) )
X    {
X      int c;
X      
X      while ( ( c = getc ( lfp ) ) != '\n' && c != EOF )
X        (void) putchar ( c );
X      (void) fclose ( lfp );
X    }
X    else
X      (void) printf ( "<no info>" );
X    if ( lock ( CHECK ) )
X      puts ( " *" );
X    else
X      (void) putchar ( '\n' );
X  }
X  exit ( 0 );
X}
X
X/*  byte parse ( str )
X *
X *  Works out whether str is for the form '<int>' or '<int>:<int>' and updates
X *  lines accordingly.  Returns 0 on correct format, 1 otherwise.
X *
X *  str  :  points to string to be parsed.
X */
Xstatic byte parse ( str )
Xchar *str;
X{
X  int number = 0;
X  FORMAT *temp, *end = lines;
X
X  if ( ! ( temp = (FORMAT *) malloc ( sizeof ( FORMAT ) ) ) )
X  {
X    perror ( "No memory" );
X    exit ( 2 );
X  }
X  if ( lines )
X  {
X    while ( end->next )
X      end = end->next;
X    end->next = temp;
X  }
X  else
X    lines = temp;
X  temp->next = NULL;
X  temp->time = 0;
X  temp->param.max_lines = 0;
X  while ( isdigit ( *str ) )
X    number = number * 10 + *str++ - '0';
X  if ( *str == '\0' )
X  {
X    temp->param.max_lines = number;
X    return 0;
X  }
X  if ( *str++ != ':' )
X    return 1;
X  temp->time = 1;
X  temp->param.date = number * 60;
X  number = 0;
X  while ( isdigit ( *str ) )
X    number = number * 10 + *str++ - '0';
X  if ( *str != '\0' )
X    return 1;
X  temp->param.date += number;
X  temp->param.date *= 60;
X  return 0;
X}
X
X/*  void lu ( argc, argv )
X *
X *  Lu prints last usage information.
X *
X *  argc  :  argc with which gosip was invoked
X *  argv  :  argv with which gosip was invoked
X */
Xvoid lu ( argc, argv )
Xint argc;
Xchar *argv[];
X{
X  char **tempv = argv;
X  int tempc = argc;
X  byte all = 1;                 /* dual purpose flag */
X  
X  if ( argc == 1 )
X    summary();
X  
X  (void) parse ( "20" );
X  while ( ++tempv, --tempc )
X    if ( (*tempv)[0] == '-' )
X      if ( parse ( *tempv + 1 ) )
X      {
X        (void) fprintf ( stderr, "Usage: %s [-<number>[:<number>]] [<gossip file>] ...\n", file );
X        exit ( 2 );
X      }
X  
X  page();
X  while ( ++argv, --argc )
X  {
X    if ( (*argv)[0] == '-' )
X    {
X      FORMAT *temp = lines->next;
X
X      (void) free ( (char *) lines );
X      lines = temp;
X    }
X    else
X    {
X      if ( ! all )
X        (void) putchar ( '\n' );
X      history ( *argv );
X      all = 0;
X    }
X  }
X
X  if ( all )                    /* do all gossip files */
X  {
X    char *files[FILE_NUM], **fptr = files;
X    byte num;
X    
X    if ( ! ( num = allgoss ( files ) ) )
X      exit ( 2 );
X    while ( num-- )
X    {
X      if ( ! all )
X        (void) putchar ( '\n' );
X      else
X        all = 0;
X      history ( *fptr++ );
X    }
X  }
X  
X  exit ( 0 );
X}
END_OF_FILE
if test 9187 -ne `wc -c <'lu.c'`; then
    echo shar: \"'lu.c'\" unpacked with wrong size!
fi
# end of 'lu.c'
fi
if test -f 'lu.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lu.h'\"
else
echo shar: Extracting \"'lu.h'\" \(629 characters\)
sed "s/^X//" >'lu.h' <<'END_OF_FILE'
X/*  void lu ( argc, argv )
X *
X *  Lu prints last usage information, obtained from last and history files.
X *
X *  argc  :  argc with which gosip was invoked
X *  argv  :  argv with which gosip was invoked
X */
Xextern void lu();
X
X/*  byte allgoss ( files )
X *
X *  Returns an alphabetical list of gosip files in the array files.  It
X *  returns 0 if no files could be found, either because of an error or
X *  because there weren't any; otherwise it returns the number of files found.
X *
X *  files  :  the array in which the result is stored.
X */
Xextern byte allgoss();
X
X#define FILE_NUM 14             /* max number of gosip files */
END_OF_FILE
if test 629 -ne `wc -c <'lu.h'`; then
    echo shar: \"'lu.h'\" unpacked with wrong size!
fi
# end of 'lu.h'
fi
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(5541 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X#include "global.h"
X#include "cat.h"
X#include "edit.h"
X#include "control.h"
X#include "util.h"
X#include "lu.h"
X
X#define VERSION 103
X#define usage() (void) fprintf ( stderr, "Usage: %s [-p|-l|-e|-t|-s|-r|-v|-x|-z]\n", file ); exit ( 4 );
X#define check_flag() if ( argv[1][2] ) { usage() }
X
Xextern char *sprintf();
Xchar text_file[MAX_FILE_LENGTH], file[MAX_FILE_LENGTH];
Xchar history_file[MAX_FILE_LENGTH], last_file[MAX_FILE_LENGTH];
X#ifdef LOCKF_BROKEN
Xchar lock_file[MAX_FILE_LENGTH];
X#endif LOCKF_BROKEN
X
X/*  void init_file_names ( invocation )
X *
X *  This function works out the names of the files used by gosip, from the
X *  name by which it is invoked, and places them in various global variables.
X *
X *  invocation  :  points to the name by which gosip was invoked (argv[0]).
X */
Xvoid init_file_names ( invocation )
Xchar *invocation;
X{
X  char *ptr = invocation, *temp = invocation;
X
X  while ( *ptr != '\0' )
X    if (  *ptr++ == '/' )
X      temp = ptr;
X  (void) strcpy ( file, temp);
X  (void) sprintf ( history_file, "%s/history.%s", DATA_FILE_DIRECTORY, file );
X  (void) sprintf ( text_file, "%s/Rush/text.%s", DATA_FILE_DIRECTORY, file );
X  (void) sprintf ( last_file, "%s/last.%s", DATA_FILE_DIRECTORY, file );
X#ifdef LOCKF_BROKEN
X  (void) sprintf ( lock_file, "%s/lLo$cK.%s", DATA_FILE_DIRECTORY, file );
X#endif LOCKF_BROKEN
X}
X
X/*  void reserve()
X *
X *  This function prints a message as soon as the gosip file is no longer
X *  being edited.  It firsts checks if gosip is being edit.  If so, it
X *  fork()s, and the child checks for the lock every 5 seconds.
X */
Xstatic void reserve()
X{
X  int pid;
X
X  if ( ! lock ( CHECK ) )
X  {
X    (void) printf ( "The %s file is not being edited.\n", file );
X    exit ( 1 );
X  }
X  error ( pid = fork(), "fork failed" );
X  if ( pid == 0 )
X  {
X    while ( lock ( CHECK ) )
X      sleep ( 5 );
X    (void) printf ( "\007\r\nThe %s file is no longer being edited.\r\n", file );
X  }
X  else
X    (void) printf ( "You'll be informed when %s becomes availalock ( CHECK ) ? EDIT : INFO;
X  if ( last = fopen ( last_file , "r" ) )
X  {
X    if ( ( c = getc ( last ) ) != EOF )
X    {
X      if ( format == EDIT )
X        (void) printf ( "The %s file is being edited by ", file );
X      else
X        (void) printf ( "Last %s edit by ", file );
X      do
X        (void) putchar ( (char) c );
X      while ( ( c = getc ( last ) ) != '\n' && c != EOF );
X    }
X    (void) fclose ( last );
X  }
X  if ( format == EDIT)
X    if ( stat ( last_file, &info ) == 0 )
X    {
X      edit_time = time ( (time_t *) 0 ) - info.st_mtime;
X      (void) printf ( " [%d:%.2d]", edit_time / 60, edit_time % 60 );
X    }
X  puts ( "." );
X}
END_OF_FILE
if test 9807 -ne `wc -c <'util.c'`; then
    echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
if test -f 'util.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'util.h'\"
else
echo shar: Extracting \"'util.h'\" \(1589 characters\)
sed "s/^X//" >'util.h' <<'END_OF_FILE'
X/*  The utilities module contains functions of general use, that are called by
X *  many of the other modules, yet don't really fit into any of them.
X */
X
X#define MAX_NAME_LEN 28	      /* longest gosip name [returned by gosname()] */
X#define MAX_CODE_LEN 12       /* max length of usercode [from usercode()] */
X
X/*  char *usercode()
X *
X *  Returns the user's usercode if possible, otherwise '??'.
X */
Xextern char *usercode();
X
X/*  char *gosname()
X *
X *  This will return the user's chosen gosip name (GOSNAME), if defined.
X *  Otherwise NAME, or failing that, the user is prompted to enter a name.
X */
Xextern char *gosname();
X
X/*  void lastedit ( format )
X *
X *  Lastedit prints out the name and usercode of the last person to edit the
X *  gosip file.  The exact form of the message depends on type, which also
X *  controls whether elapsed edit time is displayed.
X *
X *  format  :  defines type of message, as specified below.
X */
Xextern void lastedit();
X
X#define AUTO 0                  /* use lock ( CHECK ) to decide itself */
X#define EDIT 1                  /* print 'being edited' message */
X#define INFO 2                  /* just print info */
X
X/*  byte lock ( type )
X *
X *  Returns 0 if there is no lock, 1 if there is, -1 for errors (CREAT and
X *  REMVE only).  It will also create or remove a lock as specified by type.
X *
X *  type  :  specifies whether to create a lock or not (see below).
X */
Xextern byte lock();
X
X#define CHECK 0                 /* do not alter the lock */
X#define CREAT 1                 /* create a lock */
X#define REMVE 2                 /* remove a lock */
END_OF_FILE
if test 1589 -ne `wc -c <'util.h'`; then
    echo shar: \"'util.h'\" unpacked with wrong size!
fi
# end of 'util.h'
fi
echo shar: End of shell archive.
exit 0