[comp.sources.misc] v13i070: Faces v1.4.5: a visual list monitor for mail etc..

rburridge@Sun.COM (Rich Burridge) (07/03/90)

Posting-number: Volume 13, Issue 70
Submitted-by: rburridge@Sun.COM (Rich Burridge)
Archive-name: faces-1.4.5/part01

[** NOTE: faces v1.4 is posted to comp.sources.misc at patchlevel #5. **]

This is the third general release of a "faces" server for monitoring a
list visually. Typically this is a list of incoming mail messages, jobs
in the print queue or users on a system.

Included with this release, is the ability to include a face image with
your mail message using an X-Face header line (plus continuation lines).
Faces expects this line to be in a certain compressed format, and
uncompresses it, and displays that image on-the-fly. There is also an
option to automatically update the faces database with this new image.

See the README for more details.

The TODO file contains many ideas of enhancements to faces, some
relatively simple, others quite a lot of work. Volunteers anyone?

Rich Burridge,          DOMAIN: richb@Aus.Sun.COM
PHONE: +61 2 413 2666   ACSnet: richb@sunaus.sun.oz.au

------CUT HERE------faces.part1------CUT HERE------
#! /bin/sh
# this is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh to create the files:
#	mon.c
#	main.c
#	rec.c
# This archive created: Fri Jun 29 10:45:06 EST 1990
#
#
export PATH; PATH=/bin:$PATH
#
if [ -f mon.c ]
then
echo shar: will not over-write existing file mon.c
else
echo shar: extracting 'mon.c',    20347 characters
cat > mon.c <<'Funky_Stuff'

/*  @(#)mon.c 1.17 90/06/26
 *
 *  Monitoring routines used by the faces program.
 * 
 *  Copyright (c) Rich Burridge - Sun Microsystems Australia.
 *                                All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  copyright messages are not removed, and no monies are exchanged. 
 * 
 *  No responsibility is taken for any errors on inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/file.h>
#include "faces.h"
#include "extern.h"


add_face(dtype, itype, name)
enum disp_type dtype ;
enum icon_type itype ;
char *name ;
{
  unsigned short buf[256] ;  /* Ikon/icon image. */

  if (itype == XFACE)
    {
      itype = ORDINARY ;
      destroy_image(itype) ;
      get_xface(face_buf, buf) ;
      load_icon(itype, buf, 0) ;
    }
  else if (itype == ORDINARY)
    {
      destroy_image(itype) ;
      if (get_icon(name, buf) == -1) itype = NOFACE ;
      else load_icon(itype, buf, 0) ;
    }
 
  switch (dtype)
    {
      case DISP_ALL   : adjust_image(DISP_NAME,  itype, row, column) ;
                        adjust_image(DISP_OTHER, itype, row, column) ;
                        adjust_image(DISP_ICON,  itype, 0, 0) ;
                        break ;
      case DISP_BOTH  : adjust_image(DISP_NAME,  itype, row, column) ;
                        adjust_image(DISP_OTHER, itype, row, column) ;
                        break ;
      case DISP_ICON  : adjust_image(dtype, itype, 0, 0) ;
                        break ;
      case DISP_NAME  :
      case DISP_OTHER : adjust_image(dtype, itype, row, column) ;
    }
}


adjust()          /* Adjust the row and column position. */
{
  struct psinfo *this ;

  if (mtype != MONNEW)
    {
      if (++column == maxcols)
        {
          column = 0 ;
          row++ ;
        }
      return ;
    }
  if (psrecs != NULL)           /* Adjust animation positions for MONNEW */
    {
      this = psrecs ;           /* Point to beginning of chain. */
      while (this != NULL)
        {
          this->column++ ;      /* Adjust column position. */
          if (facetype == NEWSTYPE && this->next == NULL)
            this->column-- ;    /* Reset for brand new animate record. */
          this = this->next ;
        }
    }
}


do_check()        /* Perform another check of the appropriate type. */
{
  switch (mtype)
    {
      case MONALL     : do_mail(MONALL) ;  /* Monitor all of the mail file. */
                        break ;
      case MONNEW     : do_mail(MONNEW) ;  /* Monitor new mail only. */
                        break ;
      case MONPRINTER : do_printer() ;     /* Monitor the print queue. */
                        break ;
      case MONPROG    : do_prog() ;        /* Run user supplied program. */
                        break ;
      case MONUSERS   : do_users() ;       /* Monitor users on a machine. */
    }
  firsttime = 0 ;
}


do_face_update(name, buf)   /* Send mail to update faces database. */
char *name, buf[2048] ;
{
  FILE *fp ;                /* File descriptor for users mail spool file. */
  char command[MAXLINE] ;   /* Command to send mail to special alias. */

  SPRINTF(command, UPDATEDEF, update_alias) ;
  if ((fp = popen(command, "w")) == NULL)     /* Open pipe to mail process. */
    {
      FPRINTF(stderr,"%s: couldn't send mail to update database.\n", progname) ;
      return ;
    }
  FPRINTF(fp, "To: %s\n", update_alias) ;
  FPRINTF(fp, "Subject: %s\n\n", name) ;  /* Send icon name. */
  FPRINTF(fp, "%s\n", buf) ;              /* Send ikon data. */
  PCLOSE(fp) ;
}


do_mail(mtype)              /* Monitor a mail file for new or all mail. */
enum mon_type mtype ;
{        
  FILE *fp ;                /* File descriptor for users mail spool file. */
  time_t ubuf[2] ;          /* For reseting the access time on spoolfile. */

  column = row = 0 ;        /* Start in top left corner of pixrect. */
  newmail = 0 ;             /* Assume no new mail. */
  noicons = 0 ;

  if (mtype == MONNEW) make_pixrect(maxcols) ;
  if (stat(spoolfile, &buf) == -1)
    {
      lastsize = 0 ;
      add_face(DISP_ICON, NOMAIL, "") ;
      if (mtype == MONNEW) show_display() ;   /* Show new mail. */
      else make_display() ;     /* Output icons and tidyup chain of records. */
      return ;
    }
  if (buf.st_size > lastsize) newmail = 1 ;   /* New mail found. */
  if (!buf.st_size) add_face(DISP_ICON, NOMAIL, "") ;

  ubuf[0] = buf.st_atime ;                    /* Save for possible reset. */
  ubuf[1] = buf.st_mtime ;

  if (mtype == MONNEW)
    if (buf.st_size <= lastsize)   /* Is the size of mail folder bigger? */
      if (mhflag && (buf.st_size < lastsize) && (buf.st_size != 0))
        {
          lastsize = 0 ;    /* User uses MH. Any shrinkage means new mail. */
          newmail = 1 ;
        }
      else
        {
          lastsize = buf.st_size ;   /* No: save new size and exit. */
          show_display() ;
          return ;
        }

  if ((fp = fopen(spoolfile, "r")) == NULL)   /* Open spoolfile. */
    {
      if (mtype == MONNEW) show_display() ;   /* Show new mail. */
      else make_display() ;     /* Output icons and tidyup chain of records. */
      return ;
    }
  if (mtype == MONNEW) FSEEK(fp, lastsize, 0) ;
  lastsize = buf.st_size ;
  while (fgets(line, MAXLINE, fp) != NULL)
    {
      if (EQUAL(line, "From "))
        {
          if (froms_found) process_info() ;  /* Process previous mail. */
          process_from() ;                   /* Save new from details. */
        }
      else if (EQUAL(line, "From:"))  process_from() ;
      else if (EQUAL(line, "X-Face")) process_face() ;
      else if (doing_xface)           process_face() ;
    }
  FCLOSE(fp) ;

  if (!stat(spoolfile, &buf))    /* Reset access time if no change in size. */
    if (buf.st_size == lastsize)
      UTIME(spoolfile, ubuf) ;

  if (froms_found) process_info() ;       /* Process remaining mail item. */
  if (mtype == MONNEW) show_display() ;   /* Show new mail. */
  else make_display() ;     /* Output icons and tidyup chain of records. */
}


do_printer()                 /* Monitor printer queue. */
{
  struct recinfo *this, *next ;
  FILE *fp ;                 /* File descriptor for users mail spool file. */
  char command[MAXLINE] ;    /* Print status command for this printer. */
  char owner[MAXLINE] ;      /* Owner of job in the print queue. */
  int size ;                 /* Size of this print job in bytes. */

  noicons = 0 ;
  SPRINTF(command, PRINTDEF, printer) ;
  if ((fp = popen(command, "r")) == NULL)     /* Open spoolfile. */
    {
      FPRINTF(stderr,"%s: couldn't get printer stats.\n", progname) ;
      return ;
    }
  column = row = 0 ;          /* Start in top left corner of pixrect. */
  FGETS(nextline, MAXLINE, fp) ;
  if (EQUAL(nextline, "no entries"))
    {
      make_pixrect(1) ;                    /* Just the "no print" icon. */
      add_face(DISP_ALL, NOPRINT, "") ;    /* Set to "no print" icon. */
      text(DISP_ALL, LEFT, printer) ;      /* Output printer name. */
    }
  else if (EQUAL(nextline, "Printer Error: may need attention!"))
    {
      make_pixrect(1) ;                    /* Just the "no paper" icon. */
      add_face(DISP_ALL, NOPAPER, "") ;    /* Set to "no paper" icon. */
      text(DISP_ALL, LEFT, printer) ;      /* Output printer name. */
    }
  else
    {
      FGETS(nextline, MAXLINE, fp) ;    /* Skip the next line. */
      while (fgets(nextline, MAXLINE, fp) != NULL)
        {
          SSCANF(&nextline[7], "%s", owner) ;
          SSCANF(&nextline[60], "%d", &size) ;
          h_to_c("", community) ;
          found = make_iconname(facedir, community, owner) ;
          add_record("", owner, "", size) ;
        }
      make_pixrect(noicons) ;
      this = recs ;
      while (this != NULL)
        {
          next = this->next ;
          add_face(DISP_BOTH, ORDINARY, this->iconname) ;
          SPRINTF(nextline, "%1d", this->size) ;
          if (!dontshowuser)
            text(DISP_NAME, LEFT, this->username) ;  /* Owner. */
          text(DISP_OTHER, LEFT, nextline) ;         /* Size. */
          if (this == recs)
            {
              add_face(DISP_ICON, ORDINARY, this->iconname) ;
              text(DISP_ICON, LEFT, printer) ;
              SPRINTF(nextline, "%1d %s", noicons,
                                         (noicons == 1 ? "job" : "jobs")) ;
              text(DISP_ICON, RIGHT, nextline) ;   /* Number of jobs. */
            }
          adjust() ;    /* Adjust column and row. */
          remove_record(this) ;
          this = next ;
        }
      recs = last = NULL ;
    }
  PCLOSE(fp) ;
  show_display() ;
}


do_prog()                   /* Run user supplied program or script. */
{
  FILE *fp ;                /* File descriptor for users command output. */
  enum icon_type ftype ;    /* Type of the current face. */
  char host[MAXLINE] ;      /* Pointer to host name from the "From" line. */
  char ileft[MAXLINE] ;     /* Text for left side of icon display. */
  char iright[MAXLINE] ;    /* Text for right side of icon display. */
  char user[MAXLINE] ;      /* Pointer to user name from the "From" line. */
  char wleft[MAXLINE] ;     /* Text for left side of window display. */
  char wright[MAXLINE] ;    /* Text for right side of window display. */
  int cols = maxcols ;      /* Maximum number of columns for display. */
  int rows = 1 ;            /* Maximum number of rows for display. */

  if ((fp = popen(userprog, "r")) == NULL)   /* Connect to user program. */
    {
      FPRINTF(stderr,"%s: couldn't get program (%s) information.\n",
              progname, userprog) ;
      return ;
    }
  FGETS(nextline, MAXLINE, fp) ;
  SSCANF(nextline, "Cols=%d Rows=%d", &cols, &rows) ;
  if (cols < 1) cols = maxcols ;
  if (rows < 1) rows = 1 ;

  width = cols * imagewidth ;
  height = rows * imageheight ;
  create_pixrects(width, height) ;

  maxcols = cols ;          /* Maximum width in columns. */
  column = row = 0 ;        /* Start in top left corner of pixrect. */
  while (fgets(nextline, MAXLINE, fp) != NULL)
    {
      nextline[80] = '\0' ;
      if (sscanf(&nextline[70], "%s", iright) == EOF) iright[0] = '\0' ;
      nextline[70] = '\0' ;
      if (sscanf(&nextline[60], "%s", ileft)  == EOF) ileft[0] = '\0' ;
      nextline[60] = '\0' ;
      if (sscanf(&nextline[50], "%s", wright) == EOF) wright[0] = '\0' ;
      nextline[50] = '\0' ;
      if (sscanf(&nextline[40], "%s", wleft)  == EOF) wleft[0] = '\0' ;
      nextline[40] = '\0' ;
      if (sscanf(&nextline[20], "%s", host)   == EOF) host[0] = '\0' ;
      nextline[20] = '\0' ;
      if (sscanf(&nextline[ 0], "%s", user)   == EOF) user[0] = '\0' ;

      STRCPY(community, "") ;
      if (!(EQUAL(user, "NOMAIL")  || EQUAL(user, "NOPAPER") ||
            EQUAL(user, "NOPRINT") || EQUAL(user, "NOUSERS")))
        {
          h_to_c(host, community) ;   /* Turn hostname into community name. */
          a_to_u(community, user, realname) ;
          found = make_iconname(facedir, community, realname) ;
        }
           if (EQUAL(user, "NOMAIL"))  ftype = NOMAIL ;
      else if (EQUAL(user, "NOPAPER")) ftype = NOPAPER ;
      else if (EQUAL(user, "NOPRINT")) ftype = NOPRINT ;
      else if (EQUAL(user, "NOUSERS")) ftype = NOUSERS ;
      else                             ftype = ORDINARY ;
      add_face(DISP_BOTH, ftype, iconname) ;
      if (strlen(ileft))  text(DISP_OTHER, LEFT,  ileft) ;
      if (strlen(iright)) text(DISP_OTHER, RIGHT, iright) ;
      if (strlen(wleft))  text(DISP_NAME,  LEFT,  wleft) ;
      if (strlen(wright)) text(DISP_NAME,  RIGHT, wright) ;
      adjust() ;             /* Adjust column and row. */
    }
  PCLOSE(fp) ;
  show_display() ;
}


do_users()                   /* Monitor users on a machine. */
{
  struct recinfo *this, *next ;
  FILE *fp ;                 /* File descriptor for users mail spool file. */
  char command[MAXLINE] ;    /* Rusers system call for this machine. */
  char ts[MAXLINE] ;         /* Pointer to login time from rusers line. */
  char username[MAXLINE] ;   /* Name of user logged in. */
 
  noicons = 0 ;
  SPRINTF(command, USERSDEF, hostname) ;
  if ((fp = popen(command, "r")) == NULL)    /* Connect to rusers command. */
    {
      FPRINTF(stderr,"%s: couldn't get user stats.\n", progname) ;
      return ;
    }
  column = row = 0 ;          /* Start in top left corner of pixrect. */
  while (fgets(nextline, MAXLINE, fp) != NULL)
    {
      SSCANF(&nextline[0], "%s", username) ;
      SSCANF(&nextline[38], "%s", ts) ;
      h_to_c("", community) ;
      found = make_iconname(facedir, community, username) ;
      add_record("", username, ts, 0) ;
    }
  if (!noicons)
    {
      make_pixrect(1) ;                    /* Just the "no users" icon. */
      add_face(DISP_ALL, NOUSERS, "") ;    /* Set to "no users" icon. */
    }
  else
    {
      make_pixrect(noicons) ;
      add_face(DISP_ICON, NOFACE, "") ;
      this = recs ;
      while (this != NULL)
        {
          next = this->next ;
          add_face(DISP_BOTH, ORDINARY, this->iconname) ;
          if (!dontshowtime)
            text(DISP_OTHER, LEFT, this->ts) ;         /* Timestamp. */
          if (!dontshowuser)
            text(DISP_NAME, LEFT, this->username) ;    /* Username. */
          adjust() ;    /* Adjust column and row. */
          remove_record(this) ;
          this = next ;
        }
      SPRINTF(nextline, "%1d %s", noicons, (noicons == 1 ? "user" : "users")) ;
      text(DISP_ICON, RIGHT, nextline) ;   /* Number of jobs. */
      recs = last = NULL ;
    }
  PCLOSE(fp) ;
  show_display() ;
}


make_pixrect(count)   /* Make window pixrect the correct size. */
int count ;
{
  int c, r ;          /* Size in columns and rows of window display. */

  r = ((count-1) / maxcols) + 1 ;   /* Number of rows of faces. */
  c = maxcols ;                     /* Full width display. */
  if (count <= 10)
    {
      r = 1 ;         /* One row. */
      c = count ;     /* Of 'count' columns. */
    }

  height = r * imageheight ;    /* Height of the icon display. */
  width = c * imagewidth ;      /* Width of the icon display. */
  create_pixrects(width, height) ;
}


make_display()              /* Output icons and tidyup chain of records. */
{
  int count ;               /* Name of faces in icon display. */
  struct recinfo *this, *next ;

  count = noicons ;         /* Number of faces to display. */
  if (!count) count = 1 ;   /* Always one "no mail" icon. */
  make_pixrect(count) ;

  count = 0 ;
  if (!noicons) add_face(DISP_ALL, NOMAIL, "") ;
  else
    {
      this = recs ;
      while (this != NULL)
        {
          next = this->next ;
          if (!this->total)
            {
              this = next ;
              continue ;
            }
          if (this->faceimage != NULL)
            {
              STRCPY(face_buf, (char *) this->faceimage) ;
              add_face(DISP_ALL, XFACE, this->iconname) ;
              if (!this->update && update)               /* Update database */
                {
                  do_face_update(this->iconname, face_buf) ;
                  this->update = 1 ;
                }
            }
          else add_face(DISP_ALL, ORDINARY, this->iconname) ;
          count += this->total ;
          if (!dontshowno)
            {
              SPRINTF(nextline, "%1d", this->total) ;
              text(DISP_OTHER, RIGHT, nextline) ;
            }
          if (!dontshowtime) text(DISP_OTHER, LEFT, this->ts) ;
          if (!dontshowuser)
            {
              text(DISP_NAME, LEFT, this->username) ;
              text(DISP_ICON, LEFT, this->username) ;
            }
          adjust() ;
          this = next ;
        }
      SPRINTF(nextline, "%1d", count) ;
      text(DISP_ICON, RIGHT, nextline) ;
      garbage_collect() ;                 /* Remove zero length records. */
    }
  show_display() ;         /* Display the latest set of faces. */
}


process_face()        /* Extract next line of face image information. */
{

/*  Read in the X-Face: data. By default, the first line will contain an
 *  initial X-Face: (which is ignored), followed by 72 characters of
 *  compressed data. Second and subsequent lines will contain an initial
 *  space (which is ignored), followed by 79 characters of compressed data.
 *  The last line may contain less characters.
 *
 *  The trouble is that users may try to pretty up this output. In order to
 *  handle that possibility, the following rules will apply:
 *
 *  On the initial line, the "X-Face: " will be removed, any immediate
 *  whitespace (tabs and spaces) will be removed, and the remainder of
 *  the line will be placed in the data buffer (minus the trailing newline).
 *
 *  On subsequent lines, initial whitespace will be removed, and the
 *  remainder of the data appended to the data buffer (minus the trailing
 *  newline).
 *
 *  When a blank line, or a line with the initial non whitespace character,
 *  then this will signify the end of X-Face data, and the buffer will be
 *  passed to the uncompression routine.
 *
 *  Note that this extraction process is not perfect, and it is possible that
 *  more data may be appended to the face buffer then necessary. This should
 *  not be a problem, because the face uncompression routine should ignore
 *  that extra input data.
 */

  char *ptr ;

  if (!doing_xface)
    {
      bzero(face_buf, 2048) ;
      ptr = &line[7] ;
      while (*ptr == ' ' || *ptr == '\t') ptr++ ;
      STRNCPY(face_buf, ptr, strlen(ptr)-1) ;
      face_buf[strlen(ptr)-1] = '\0' ;
      doing_xface = 1 ;
    }
  else if (!strlen(line) || !(line[0] == ' ' || line[0] == '\t'))
    {
      doing_xface = 0 ;
      if (uncompface(face_buf) < 0) return ;
      x_face = 1 ;                       /* We have an "on-the-fly" face. */
    }
  else
    {
      ptr = line ;
      while (*ptr == ' ' || *ptr == '\t') ptr++ ;
      STRNCAT(face_buf, ptr, strlen(ptr)-1) ;
    }
}


process_from()             /* Extract information from the From line. */
{
  char *host, *user ;
  char temp[MAXLINE], ts[9] ;

  if (EQUAL(line, "From "))
    {
      SSCANF(line, "%s %s %s %s %s %s", temp, temp, temp, temp, temp, ts) ;
      ts[5] = '\0' ;
      STRCPY(face_ts, ts) ;
      froms_found = 1 ;
    }
  else
    {
      if (fromc_found) return ;    /* Only process first From: line. */
      fromc_found = 1 ;
    }

  if (parsefrom(line, &user, &host))
    {
      STRCPY(face_user, strlower(user)) ;
      STRCPY(face_host, host) ;
    }
  else FPRINTF(stderr, "%s: couldn't parse '%s'\n", progname, line) ;
}


process_info()          /* Process From line and face information. */
{
  struct recinfo *crec ;    /* Pointer to current mail record for updating. */

  h_to_c(face_host, community) ;    /* Turn hostname into community name. */
  a_to_u(community, face_user, realname) ;
  if (mtype == MONNEW)
    {
      if (x_face)
        {
          SPRINTF(iconname, "%s/%s/%s/48x48x1",
                  strlen(facedir) ? facedir : defdir, community, realname) ;
          add_face(DISP_ALL, XFACE, iconname) ;
          if (update) do_face_update(iconname, face_buf) ;
        }
      else
        {
          if (!(found = make_iconname(facedir, community, realname)))
            add_face(DISP_ALL, NOFACE, (char *) NULL) ;
          else add_face(DISP_ALL, ORDINARY, iconname) ;
        }

      if (!dontshowtime) text(DISP_OTHER, LEFT, face_ts) ;
      if (!dontshowuser)
        {
          text(DISP_NAME, LEFT, realname) ;
          text(DISP_ICON, LEFT, realname) ;
        }
      adjust() ;
    }
  else
    {
      found = make_iconname(facedir, community, realname) ;
      if (x_face)
        SPRINTF(iconname, "%s/%s/%s/48x48x1",
                strlen(facedir) ? facedir : defdir, community, realname) ;
      if ((crec = rec_exists(community, realname)) != NULL)
        {
          STRCPY(crec->ts, face_ts) ;
          if (!crec->total) noicons++ ;
          crec->total++ ;
        }
      else add_record(community, realname, face_ts, 0) ;
    }
  fromc_found = froms_found = x_face = 0 ;  /* Reset for the next message. */
}
Funky_Stuff
len=`wc -c < mon.c`
if [ $len !=    20347 ] ; then
echo error: mon.c was $len bytes long, should have been    20347
fi
fi # end of overwriting check
if [ -f main.c ]
then
echo shar: will not over-write existing file main.c
else
echo shar: extracting 'main.c',    20038 characters
cat > main.c <<'Funky_Stuff'

/*  @(#)main.c 1.16 90/06/29
 *
 *  Icon face server for monitoring mail and print jobs.
 *  This program is based on the AT&T v8 mail face server,
 *  vismon, but is not derived from vismon source.
 * 
 *  Copyright (c) Rich Burridge - Sun Microsystems Australia.
 *                                All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  copyright messages are not removed, and no monies are exchanged. 
 * 
 *  No responsibility is taken for any errors on inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netdb.h>
#include "faces.h"

unsigned short noface_image[] = {
#include "noface.icon"
} ;

unsigned short nomail_image[] = {
#include "nomail.icon"
} ;
 
unsigned short nopaper_image[] = {
#include "nopaper.icon"
} ;
 
unsigned short noprint_image[] = {
#include "noprint.icon"
} ;
 
unsigned short nousers_image[] = {
#include "nousers.icon"
} ;

char *getname() ;
int do_check() ;
struct recinfo *rec_exists() ;

struct comminfo *communities = NULL ;  /* Community alias/username chain. */
struct comminfo *clast = NULL ;     /* End of chain of community records. */
struct machinfo *machines = NULL ;  /* Known machine/communities. */
struct machinfo *mlast = NULL ;     /* End of chain of machine records. */
struct psinfo *psrecs = NULL ;      /* List of news.ps animation files. */
struct psinfo *plast = NULL ;       /* End of chain of NeWS animation files. */
struct recinfo *recs = NULL ;       /* Mail messages being monitored. */
struct recinfo *last = NULL ;       /* End of the chain of mail records. */
struct stat buf ;                   /* Buffer for file existence stat call. */
long lastsize = 0L ;                /* Last known size of the mail folder. */

enum disp_type wdtype = DISP_NAME ; /* Current window display option. */
enum gr_type gtype ;                /* Which graphics system is being used. */
enum mon_type mtype = MONNEW ;      /* Type of monitoring operation to do. */

char bgicon[MAXLINE] ;           /* Alternate background pattern. */
char community[MAXLINE] ;        /* Community name ("real" host name). */
char defdir[MAXLINE] ;           /* The default face directory. */
char display[MAXLINE] ;          /* X11 display information. */
char *envmail ;             /* Pointer to MAIL environment variable value */
char face_buf[2048] ;       /* Buffer for "X-Face" face images. */
char face_host[MAXLINE] ;   /* Pointer to host name from the "From" line. */
char face_ts[MAXLINE] ;     /* Pointer to time stamp from the "From" line. */
char face_user[MAXLINE] ;   /* Pointer to user name from the "From" line. */
char facedir[MAXLINE] ;          /* Alternate face image directory. */
char fname[MAXTYPES][MAXLINE] ;  /* Array of various face name types. */
char geometry[MAXLINE] ;    /* X11 geometry information. */
char hostname[MAXLINE] ;    /* Machine name to monitor. */
char iconname[MAXLINE] ;    /* Name of the icon file for this person. */
char line[MAXLINE] ;        /* Next line from users mail spool file. */
char machfile[MAXLINE] ;    /* Name of the machine/community file. */
char nextline[MAXLINE] ;    /* Input/conversion buffer for various routines. */
char peopfile[MAXLINE] ;    /* Name of the people/username file. */
char printer[MAXLINE] ;     /* Printer name to monitor. */
char progname[MAXLINE] ;    /* Name of this program. */
char realname[MAXLINE] ;    /* Real username for this user. */
char spoolfile[MAXLINE] ;   /* Full pathname of users current mail. */
char update_alias[MAXLINE] ;   /* Name of mail alias for database updates. */
char *username ;            /* This users name. */
char userprog[MAXLINE] ;    /* User supplied program to run. */

int beeps ;             /* Number of beeps for arrival of new mail. */
int column ;            /* Column number for next icon. */
int doing_xface = 0 ;   /* Set if we've started to process an X-Face: */
int dontshowno = 0 ;    /* Set if number of messages shouldn't be displayed. */
int dontshowtime = 0 ;  /* Set if timestamp for new mail shouldn't be shown. */
int dontshowuser = 0 ;  /* Set if username for new mail shouldn't be shown. */

int facetype ;          /* Type of face file found. */
int firsttime = 1 ;     /* Zeroised after first mail/printer check. */
int flashes ;           /* Number of flashes for arrival of new mail. */
int found ;             /* Set if iconname found under username directory. */
int fromc_found = 0 ;   /* Set if "From:" line found during processing. */
int froms_found = 0 ;   /* Set if "From " line found during processing. */
int height ;            /* Height in pixels of faces display. */

int iconheight = ICONHEIGHT ;  /* Height of an individual icon. */
int iconwidth = ICONWIDTH ;    /* Width of an individual icon. */
int iconic = 0 ;               /* Faces window is closed if set. */
int imageheight = ICONHEIGHT ;  /* Height of an individual face image. */
int imagewidth = ICONWIDTH ;    /* Width of an individual face image. */
int invert = 0 ;               /* Set if to use reverse video. */
int ix = 0 ;                   /* Initial X position of the icon. */
int iy = 0 ;                   /* Initial Y position of the icon. */
int maxcols = NO_PER_ROW ;     /* Maximum number of columns to display. */

int maxtypes ;      /* Maximum number of icon types for graphics target. */
int mhflag = 0 ;    /* Set if this user uses MH to read mail. */
int newmail ;       /* Set if there is new mail this time around. */
int noicons ;       /* Number of faces this time around. */
int old_style = 0 ; /* Set when "old" style face ikon is found. */
int period = 60 ;   /* Period in seconds for new mail check. */
int posspec = 0 ;   /* Set if -Wp or -g option is present (for X11) */
int row ;           /* Row number for next icon. */
int update = 0 ;    /* If set, send mail to update faces database. */
int width ;         /* Width in pixels of faces display. */
int wx = 0 ;        /* Initial X position of the window. */
int wy = 0 ;        /* Initial Y position of the window. */
int x_face = 0 ;    /* Set if we've extracted an "on-the-fly" face. */

/*  256-byte table for quickly reversing the bits in an unsigned 8-bit char,
 *  used to convert between MSBFirst and LSBFirst image formats.
 */

char revtable[256] = {
        0, -128,   64,  -64,   32,  -96,   96,  -32,
       16, -112,   80,  -48,   48,  -80,  112,  -16,
        8, -120,   72,  -56,   40,  -88,  104,  -24,
       24, -104,   88,  -40,   56,  -72,  120,   -8,
        4, -124,   68,  -60,   36,  -92,  100,  -28,
       20, -108,   84,  -44,   52,  -76,  116,  -12,
       12, -116,   76,  -52,   44,  -84,  108,  -20,
       28, -100,   92,  -36,   60,  -68,  124,   -4,
        2, -126,   66,  -62,   34,  -94,   98,  -30,
       18, -110,   82,  -46,   50,  -78,  114,  -14,
       10, -118,   74,  -54,   42,  -86,  106,  -22,
       26, -102,   90,  -38,   58,  -70,  122,   -6,
        6, -122,   70,  -58,   38,  -90,  102,  -26,
       22, -106,   86,  -42,   54,  -74,  118,  -10,
       14, -114,   78,  -50,   46,  -82,  110,  -18,
       30,  -98,   94,  -34,   62,  -66,  126,   -2,
        1, -127,   65,  -63,   33,  -95,   97,  -31,
       17, -111,   81,  -47,   49,  -79,  113,  -15,
        9, -119,   73,  -55,   41,  -87,  105,  -23,
       25, -103,   89,  -39,   57,  -71,  121,   -7,
        5, -123,   69,  -59,   37,  -91,  101,  -27,
       21, -107,   85,  -43,   53,  -75,  117,  -11,
       13, -115,   77,  -51,   45,  -83,  109,  -19,
       29,  -99,   93,  -35,   61,  -67,  125,   -3,
        3, -125,   67,  -61,   35,  -93,   99,  -29,
       19, -109,   83,  -45,   51,  -77,  115,  -13,
       11, -117,   75,  -53,   43,  -85,  107,  -21,
       27, -101,   91,  -37,   59,  -69,  123,   -5,
        7, -121,   71,  -57,   39,  -89,  103,  -25,
       23, -105,   87,  -41,   55,  -73,  119,   -9,
       15, -113,   79,  -49,   47,  -81,  111,  -17,
       31,  -97,   95,  -33,   63,  -65,  127,   -1,
} ;


main(argc, argv)
int argc ;
char *argv[] ;
{
  STRCPY(progname, argv[0]) ;   /* Save this programs name. */
  initialise() ;                /* Set default values for various options. */
  get_options(argc, argv) ;     /* Read and process command line options. */
  read_mailrc() ;               /* Get number of flashes and beeps. */
  read_machines() ;             /* Setup up the machine/community chain. */
  read_aliases() ;              /* Setup the hostname alias subchains. */

  if (init_ws_type(argc, argv)) /* Determine window system type. */
    {
      FPRINTF(stderr,"Error initialising window system.\n") ;
      exit(1) ;
    }

#ifdef REVORDER
  load_icon(NOFACE,  noface_image,  1) ;
  load_icon(NOMAIL,  nomail_image,  1) ;
  load_icon(NOPAPER, nopaper_image, 1) ;
  load_icon(NOPRINT, noprint_image, 1) ;
  load_icon(NOUSERS, nousers_image, 1) ;
#else
  load_icon(NOFACE,  noface_image,  0) ;
  load_icon(NOMAIL,  nomail_image,  0) ;
  load_icon(NOPAPER, nopaper_image, 0) ;
  load_icon(NOPRINT, noprint_image, 0) ;
  load_icon(NOUSERS, nousers_image, 0) ;
#endif /*REVORDER*/
  make_area(DISP_ICON, imagewidth, imageheight) ;

  make_icon() ;                 /* Create default faces icon. */
  make_frame(argc, argv) ;      /* Create faces window/icon. */
  do_check() ;                  /* Generate initial faces display. */
  start_tool() ;                /* Start up and display the faces icon. */
}


a_to_u(community, user, realname)    /* Turn possible alias into username. */
char *community, *user, *realname ;
{
  struct comminfo *ctemp ;
  struct peopinfo *ptemp ;

  STRCPY(realname, user) ;      /* In case alias not found. */
  ctemp = communities ;         /* Point to chain of communities. */
  while (ctemp != NULL)
    if (!strcmp(ctemp->community, community))
      {
        ptemp = ctemp->people ;
        while (ptemp != NULL)
          if (!strcmp(ptemp->alias, user))
            {
              STRCPY(realname, ptemp->username) ;
              return ;
            }
          else ptemp = ptemp->next ;
        return ;
      }
    else ctemp = ctemp->next ;
}


initialise()
{
  STRCPY(facedir, "") ;       /* No alternate face directory by default. */
  STRCPY(display, "") ;       /* X11 display type. */
  STRCPY(geometry, "") ;      /* X11 geometry information. */

#ifdef FBMONTYPE
  mtype = FBMONTYPE ;         /* Type of monitoring to do. */
#endif /*FBMONTYPE*/

#ifdef BACKGROUND
  STRCPY(bgicon, BACKGROUND) ;          /* Alternate background pattern. */
#else
  STRCPY(bgicon, "") ;                  /* Default is to use root gray. */
#endif /*BACKGROUND*/

#ifdef FACEDIR
  STRCPY(defdir, FACEDIR) ;            /* Different directory for face icons. */
#else
  STRCPY(defdir,"/usr/local/faces") ;  /* Directory for face icons. */
#endif /*FACEDIR*/

  SPRINTF(machfile, "%s/%s", defdir, MACHINETAB) ;
  SPRINTF(peopfile, "%s/%s", defdir, PEOPLETAB) ;

#ifdef INVERT
  invert = 1 ;               /* Display in reverse video. */
#endif /*INVERT*/

#ifdef PERIOD
  period = PERIOD ;          /* Period for new mail/print check. */
#endif /*PERIOD*/

#ifdef DONTSHOWNO
  dontshowno = 1 ;           /* Don't show number of messages on images. */
#endif /*DONTSHOWNO*/

#ifdef SPOOLFILE
  STRCPY(spoolfile, SPOOLFILE) ;   /* Alternative spoolfile to monitor. */
#else
  if (envmail = getenv("MAIL")) STRCPY(spoolfile, envmail) ;
  else
    {
      username = getname() ;     /* Get users name from passwd entry. */
      SPRINTF(spoolfile, "/usr/spool/mail/%s", username) ;
    }
#endif /*SPOOLFILE*/

#ifdef UPDATE
  STRCPY(update_alias, UPDATE) ;   /* Alternate faces database update alias. */
#else
  STRCPY(update_alias, UPDATE_ALIAS) ;
#endif /*UPDATE*/

#ifdef DONTSHOWTIME
  dontshowtime = 1 ;           /* Don't show timestamp on images. */
#endif /*DONTSHOWTIME*/

#ifdef DONTSHOWUSER
  dontshowuser = 1 ;           /* Don't show username on images. */
#endif /*DONTSHOWUSER*/
}


h_to_c(host, community)        /* Turn hostname into community name. */
char *host, *community ;
{
  struct hostent *hp ;
  struct machinfo *temp ;      /* Pointer to next machine record. */
 
  temp = machines ;            /* Point to list of machine/communitys. */
  if (host[0] == '\0')
    {
      GETHOSTNAME(community, MAXLINE) ;
      hp = gethostbyname(community) ;
      if (hp != NULL) STRCPY(community, hp->h_name) ;
    }
  else STRCPY(community, host) ;   /* Copied in case machine name not found. */
  while (temp != NULL)
    {
      if (!strcmp(temp->machine, community))
        {
          STRCPY(community, temp->community) ;
          return ;
        }
      temp = temp->next ;      /* Point to next record. */
    }
}


make_iconname(facedir, community, user)    /* Construct the icon name. */
char *facedir, *community, *user ;
{

/*  Sets up community and user based on the first successful
 *  open from the following list of files:
 *
 *  $(FACEDIR)/community/user/[face.ps, sun.icon, 48x48x1, face.xbm]
 *  $(FACEDIR)/misc./user/[face.ps, sun.icon, 48x48x1, face.xbm]
 *  $(FACEDIR)/community/unknown/[face.ps, sun.icon, 48x48x1, face.xbm]
 *  $(FACEDIR)/misc./unknown/[face.ps, sun.icon, 48x48x1, face.xbm]
 *
 *  Firstly a check is made for the "old" style; first in a possible
 *  alternate face directory, then in the default directory. This is
 *  where the username is not a directory but the ikon in 48x48x1
 *  format.
 *
 *  If none of these are found, the "blank face" is returned.
 */

  char *cptr ;
  int i ;

  old_style = 0 ;     /* Reset before checking this face. */

  if (strlen(facedir))
    for (i = -1; i < maxtypes; i++)
      for (cptr = community; cptr != NULL; cptr = index(cptr, '.'))
        {
          if (*cptr == '.') cptr++ ;
          if (i < 0)
            {
              SPRINTF(iconname, "%s/%s/%s", facedir, cptr, user) ;
              if (stat(iconname, &buf) == -1) goto mi1 ;
              if ((buf.st_mode & S_IFMT) == S_IFREG)
                {
                  old_style = 1 ;
                  return 1 ;
                }
            }
          else
            {
              SPRINTF(iconname, "%s/%s/%s/%s", facedir, cptr, user, fname[i]) ;
              if (stat(iconname, &buf) != -1) return 1 ;
            }
        }

mi1:
  for (i = -1; i < maxtypes; i++)
    for (cptr = community; cptr != NULL; cptr = index(cptr, '.'))
      {
        if (*cptr == '.') cptr++ ;
        if (i < 0)
          {
            SPRINTF(iconname, "%s/%s/%s", defdir, cptr, user) ;
            if (stat(iconname, &buf) == -1) goto mi2 ;
            if ((buf.st_mode & S_IFMT) == S_IFREG)
              {    
                old_style = 1 ;
                return 1 ;
              }
          } 
        else
          {
            SPRINTF(iconname, "%s/%s/%s/%s", defdir, cptr, user, fname[i]) ;
            if (stat(iconname, &buf) != -1) return 1 ;
          }
      }
 
mi2:
  if (strlen(facedir))
    for (i = -1; i < maxtypes; i++)
      if (i < 0)
        {
          SPRINTF(iconname, "%s/misc./%s", facedir, user) ;
          if (stat(iconname, &buf) == -1) goto mi3 ;
          if ((buf.st_mode & S_IFMT) == S_IFREG)
            {
              old_style = 1 ;
              return 1 ;
            }
        }    
      else
        { 
          SPRINTF(iconname, "%s/misc./%s/%s", facedir, user, fname[i]) ;
          if (stat(iconname, &buf) != -1)
            {
              if (!x_face) STRCPY(community, "misc.") ;
              return 1 ;
            }
        }

mi3:
  for (i = -1; i < maxtypes; i++)
    if (i < 0)
      {
        SPRINTF(iconname, "%s/misc./%s", defdir, user) ;
        if (stat(iconname, &buf) == -1) goto mi4 ;
        if ((buf.st_mode & S_IFMT) == S_IFREG)
          {
            old_style = 1 ;
            return 1 ;
          }
      }    
    else
      {
        SPRINTF(iconname, "%s/misc./%s/%s", defdir, user, fname[i]) ;
        if (stat(iconname, &buf) != -1)
          {
            if (!x_face) STRCPY(community, "misc.") ;
            return 1 ;
          }
      }
 
mi4:
  if (strlen(facedir))
    for (i = -1; i < maxtypes; i++)
      for (cptr = community; cptr != NULL; cptr = index(cptr, '.'))
        {
          if (*cptr == '.') cptr++ ;
          if (i < 0)
            {
              SPRINTF(iconname, "%s/%s/unknown", facedir, cptr) ;
              if (stat(iconname, &buf) == -1) goto mi5 ;
              if ((buf.st_mode & S_IFMT) == S_IFREG)
                {    
                  old_style = 1 ;
                  return 1 ;
                }
            } 
          else
            {
              SPRINTF(iconname, "%s/%s/unknown/%s", facedir, cptr, fname[i]) ;
              if (stat(iconname, &buf) != -1)
                {
                  if (mtype == MONALL && !x_face) STRCPY(user, "unknown") ;
                  return 1 ;
                }
            }
        }

mi5:
  for (i = -1; i < maxtypes; i++)
    for (cptr = community; cptr != NULL; cptr = index(cptr, '.'))
      {
        if (*cptr == '.') cptr++ ;
        if (i < 0)
          {
            SPRINTF(iconname, "%s/%s/unknown", defdir, cptr) ;
            if (stat(iconname, &buf) == -1) goto mi6 ;
            if ((buf.st_mode & S_IFMT) == S_IFREG)
              {    
                old_style = 1 ;
                return 1 ;
              }
          } 
        else
          {
            SPRINTF(iconname, "%s/%s/unknown/%s", defdir, cptr, fname[i]) ;
            if (stat(iconname, &buf) != -1)
              {
                if (mtype == MONALL && !x_face) STRCPY(user, "unknown") ;
                return 1 ;
              }
          }
      }

mi6:
  if (strlen(facedir))
    for (i = -1; i < maxtypes; i++)
      if (i < 0)
        {
          SPRINTF(iconname, "%s/misc./unknown", facedir) ;
          if (stat(iconname, &buf) == -1) goto mi7 ;
          if ((buf.st_mode & S_IFMT) == S_IFREG)
            {
              old_style = 1 ;
              return 1 ;
            }
        }    
      else
        {
          SPRINTF(iconname, "%s/misc./unknown/%s", facedir, fname[i]) ;
          if (stat(iconname, &buf) != -1)
            {
              if (!x_face) STRCPY(community, "misc.") ;
              if (mtype == MONALL && !x_face) STRCPY(user, "unknown") ;
              return 1 ;
            }
        }

mi7:
  for (i = -1; i < maxtypes; i++)
    if (i < 0)
      {
        SPRINTF(iconname, "%s/misc./unknown", defdir) ;
        if (stat(iconname, &buf) == -1) return 0 ;
        if ((buf.st_mode & S_IFMT) == S_IFREG)
          {
            old_style = 1 ;
            return 1 ;
          }  
      }    
    else
      {
        SPRINTF(iconname, "%s/misc./unknown/%s", defdir, fname[i]) ;
        if (stat(iconname, &buf) != -1)
          {
            if (!x_face) STRCPY(community, "misc.") ;
            if (mtype == MONALL && !x_face) STRCPY(user, "unknown") ;
            return 1 ;
          }
      }

/* Face icon not found, so the "blank face" should be used. */

  return 0 ;
}


read_mailrc()                /* Get number of flashes and beeps. */
{
  char mrcname[MAXLINE] ;    /* Full pathname of the .mailrc file. */
  char *ptr ;
  FILE *fd ;

  beeps = 0 ;     /* Defaults if .mailrc file not found. */
  flashes = 0 ;
  if (getenv("HOME"))
    {
      SPRINTF(mrcname, "%s/.mailrc", getenv("HOME")) ;
      if ((fd = fopen(mrcname, "r")) == NULL) return ;
      while (fgets(nextline, MAXLINE, fd) != NULL)
        if (EQUAL(nextline, "set"))
          {
            ptr = index(nextline, ' ') ;
            if (EQUAL(ptr+1, "flash"))
              {
                ptr = index(nextline, '=') ;
                SSCANF(ptr+1, "%d", &flashes) ;
              }
            else if (EQUAL(ptr+1, "bell"))
              {
                ptr = index(nextline, '=') ;
                SSCANF(ptr+1, "%d", &beeps) ;
              }
          }
    }
  FCLOSE(fd) ;
}
Funky_Stuff
len=`wc -c < main.c`
if [ $len !=    20038 ] ; then
echo error: main.c was $len bytes long, should have been    20038
fi
fi # end of overwriting check
if [ -f rec.c ]
then
echo shar: will not over-write existing file rec.c
else
echo shar: extracting 'rec.c',     8673 characters
cat > rec.c <<'Funky_Stuff'

/*  @(#)rec.c 1.10 90/06/26
 *
 *  Record handling routines used by the faces program.
 * 
 *  Copyright (c) Rich Burridge - Sun Microsystems Australia.
 *                                All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  copyright messages are not removed, and no monies are exchanged. 
 * 
 *  No responsibility is taken for any errors on inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include <stdio.h>
#include <strings.h>
#include "faces.h"
#include "extern.h"


char *
Malloc(n)
int n ;
{
  char *val ;

  if ((val = malloc((unsigned) n)) == NULL)
    FPRINTF(stderr, "%s: Out of memory.\n", progname) ;
  return val ;
}


add_alias(crec, username, alias)  /* Add new alias to hostnames' list. */
struct comminfo *crec ;
char *username, *alias ;
{
  struct peopinfo *cptemp, *ptemp ;

  ptemp = (struct peopinfo *) LINT_CAST(Malloc(sizeof(struct peopinfo))) ;
  ptemp->alias = (char *) Malloc(strlen(alias)+1) ;
  STRCPY(ptemp->alias, alias) ;
  ptemp->username = (char *) Malloc(strlen(username)+1) ;
  STRCPY(ptemp->username, username) ;
  ptemp->next = NULL ;

  if (crec->people == NULL) crec->people = ptemp ;
  else
    {
      cptemp = crec->people ;
      while (cptemp != NULL)
        if (cptemp->next == NULL)
          {
            cptemp->next = ptemp ;
            return ;
          }
        else cptemp = cptemp->next ;
    }
}


add_machine(machine, community)    /* Add new machine to list. */
char *machine, *community ;
{
  struct machinfo *temp ;

  temp = (struct machinfo *) LINT_CAST(Malloc(sizeof(struct machinfo))) ;
  temp->machine = (char *) Malloc(strlen(machine)+1) ;
  STRCPY(temp->machine, machine) ;
  temp->community = (char *) Malloc(strlen(community)+1) ;
  STRCPY(temp->community, community) ;
  temp->next = NULL ;

  if (machines == NULL) machines = mlast = temp ;   /* Start chain. */
  else if (mlast != NULL)
    {
      mlast->next = temp ;     /* Add record to existing chain. */
      mlast = temp ;           /* Point to end of chain. */
    }
}


add_ps_rec(row, column, name)  /* Add record for later animation. */
int row, column ;
char *name ;
{
  struct psinfo *temp ;

  temp = (struct psinfo *) LINT_CAST(Malloc(sizeof(struct psinfo))) ;
  temp->name = (char *) Malloc(strlen(name)+1) ;
  STRCPY(temp->name, name) ;
  temp->row = row ;
  temp->column = column ;
  temp->next = NULL ;

  if (psrecs == NULL) psrecs = plast = temp ;    /* Start chain. */
  else if (plast != NULL)
    {
      plast->next = temp ;     /* Add record to existing chain. */
      plast = temp ;
    }
}


add_record(community, username, timestamp, size)
char *community, *username, *timestamp ;
int size ;
{
  struct recinfo *temp ;

  temp = (struct recinfo *) LINT_CAST(Malloc(sizeof(struct recinfo))) ;
  temp->community = (char *) Malloc(strlen(community)+1) ;
  STRCPY(temp->community, community) ;
  temp->username = (char *) Malloc(strlen(username)+1) ;
  STRCPY(temp->username, username) ;
  temp->iconname = (char *) Malloc(strlen(iconname)+1) ;
  STRCPY(temp->iconname, iconname) ;
  if (x_face)
    {
      temp->faceimage = (unsigned char *) Malloc(strlen(face_buf)+1) ;
      STRCPY(temp->faceimage, face_buf) ;
    }
  else temp->faceimage = NULL ;
  STRCPY(temp->ts, timestamp) ;
  temp->size = size ;
  temp->total = 1 ;
  temp->update = 0 ;
  temp->next = NULL ;
  noicons++ ;
  if (recs == NULL) recs = last = temp ;        /* Start chain. */
  else
    {
      last->next = temp ;  /* Add record to existing chain. */
      last = temp ;        /* Point to the end of the chain. */
    }
}


check_comm(hostname, username, alias)  /* Check community list. */
char *hostname, *username, *alias ;
{
  struct comminfo *ctemp ;
 
  ctemp = communities ;      /* Try and find machine record for hostname. */
  while (ctemp != NULL)
    if (!strcmp(ctemp->community, hostname))
      {  
        add_alias(ctemp, username, alias) ;
        return ;
      }  
    else ctemp = ctemp->next ;
 
  ctemp = (struct comminfo *) LINT_CAST(Malloc(sizeof(struct comminfo))) ;
  ctemp->community = (char *) Malloc(strlen(hostname)+1) ;
  STRCPY(ctemp->community, hostname) ; 
  ctemp->people = NULL ; 
  ctemp->next = NULL ; 
 
  if (communities == NULL) communities = clast = ctemp ;  /* Start chain. */ 
  else 
    { 
      clast->next = ctemp ;   /* Add record to existing chain. */
      clast = ctemp ;         /* Point to end of chain. */
    }
  add_alias(ctemp, username, alias) ;
}


/* Remove records with zero count; zeroise count for others. */

garbage_collect()
{
  struct recinfo *last, *this, *next ;

  last = NULL ;
  this = recs ;
  while (this != NULL)
    {
      next = this->next ;
      if (!this->total)
        {
          if (last != NULL) last->next = this->next ;
          if (this == recs) recs = this->next ;
          remove_record(this) ;
        }
      else
        {
          this->total = 0 ;
          last = this ;
        }
      this = next ;
    }
}


read_aliases()     /* Setup the hostname aliases subchains. */
{
  char alias[MAXLINE] ;      /* Alias for this community/username. */
  char hostname[MAXLINE] ;   /* This records' hostname. */
  char username[MAXLINE] ;   /* This records real username. */
  char *ptr1, *ptr2 ;
  FILE *fd ;

  if (strlen(facedir)) SPRINTF(peopfile, "%s/PEOPLETAB", facedir) ;
  if ((fd = fopen(peopfile, "r")) == NULL)   /* Open people file. */
    {
      if (strlen(facedir))
        {
          SPRINTF(peopfile, "%s/machine.tab", defdir) ;
          fd = fopen(peopfile, "r") ;
        }
      if (fd == NULL)
        {
          FPRINTF(stderr,"%s: cannot open %s\n", progname, peopfile) ;
          return ;
        }
    }
  while (fgets(nextline, MAXLINE, fd) != NULL)
    {
      if (nextline[0] == '\n' || nextline[0] == '#') continue ;
      ptr1 = index(nextline, '/') ;
      if (((int) (ptr1-nextline)) <= 0) continue ;  /* Ignore if no length. */
      STRNCPY(hostname, nextline, (int) (ptr1-nextline)) ;
      hostname[(int) (ptr1-nextline)] = '\0' ;
      ptr2 = index(nextline, '=') ;
      if (((int) (ptr2-ptr1-1)) <= 0) continue ;    /* Ignore if no length. */
      STRNCPY(alias, ptr1+1, (int) (ptr2-ptr1-1)) ;
      alias[(int) (ptr2-ptr1-1)] = '\0' ;
      if ((strlen(ptr2)-2) <= 0) continue ;         /* Ignore if no length. */
      STRNCPY(username, ptr2+1, strlen(ptr2)-2) ;
      username[strlen(ptr2)-2] = '\0' ;
      check_comm(hostname, username, alias) ;
    }
  FCLOSE(fd) ;
}


read_machines()       /* Setup the chain of machine/community records. */
{
  char community[MAXLINE] ;   /* This records' community. */
  char machine[MAXLINE] ;     /* This records' machine name. */
  char *ptr ;
  FILE *fd ;

  if (strlen(facedir)) SPRINTF(machfile, "%s/MACHINETAB", facedir) ;
  if ((fd = fopen(machfile, "r")) == NULL)   /* Open machine file. */
    {
      if (strlen(facedir))
        {
          SPRINTF(machfile, "%s/machine.tab", defdir) ;
          fd = fopen(machfile, "r") ;
        }
      if (fd == NULL)
        {
          FPRINTF(stderr,"%s: cannot open %s\n", progname, machfile) ;
          return ;
        }
    }
  while (fgets(nextline, MAXLINE, fd) != NULL)
    {
      if (nextline[0] == '\n' || nextline[0] == '#') continue ;
      ptr = index(nextline, '=') ;
      if (((int) (ptr-nextline)) <= 0) continue ;  /* Ignore if no length. */
      STRNCPY(machine, nextline, (int) (ptr-nextline)) ;
      machine[(int) (ptr-nextline)] = '\0' ;
      if (strlen(ptr)-2 <= 0) continue ;           /* Ignore if no length. */
      STRNCPY(community, ptr+1, strlen(ptr)-2) ;
      community[strlen(ptr)-2] = '\0' ;
      add_machine(machine, community) ;
    }
  FCLOSE(fd) ;
}


struct recinfo *
rec_exists(community,username)    /* Check if record exists for mail item. */
char *community, *username ;
{
  struct recinfo *temp ;     /* Pointer to mail records used for chaining. */

  temp = recs ;
  while (temp != NULL)
    {
      if (!strcmp(temp->username, username) &&
          !strcmp(temp->community, community))
        return(temp) ;       /* Record found. */
      temp = temp->next ;    /* Point to next record. */
    }
  return(NULL) ;
}


remove_record(thisrec)        /* Remove this record from the chain. */
struct recinfo *thisrec ;
{
  if (thisrec->community != NULL) free(thisrec->community) ;
  if (thisrec->faceimage != NULL) free(thisrec->faceimage) ;
  if (thisrec->username  != NULL) free(thisrec->username) ;
  if (thisrec->iconname  != NULL) free(thisrec->iconname) ;
  free((char *) thisrec) ;
}
Funky_Stuff
len=`wc -c < rec.c`
if [ $len !=     8673 ] ; then
echo error: rec.c was $len bytes long, should have been     8673
fi
fi # end of overwriting check