[alt.sources] FIDOGATE Part 4/6

mj@dfv.rwth-aachen.de (Martin Junius) (01/28/91)

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is part 04 of a multipart archive
# ============= funcs.c ==============
if test -f 'funcs.c' -a X"$1" != X"-c"; then
	echo 'x - skipping funcs.c (File already exists)'
else
echo 'x - extracting funcs.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'funcs.c' &&
/*:ts=4*/
/*****************************************************************************
X * FIDOGATE --- Gateway software UNIX <-> FIDO
X *
X * $Id: funcs.c,v 1.7 90/12/02 21:21:57 mj Exp $
X *
X * Miscancelleus functions, logging, sequence numberic etc.
X *
X * $Log:	funcs.c,v $
X * Revision 1.7  90/12/02  21:21:57  mj
X * Changed program header to mention both authors of the original
X * software posted to alt.sources.
X * 
X * Revision 1.6  90/11/05  20:49:53  mj
X * Changed my signature in all program headers.
X * 
X * Revision 1.5  90/11/01  14:33:39  mj
X * Added function xtol()
X * 
X * Revision 1.4  90/10/29  21:19:14  mj
X * Added functions strnicmp() and stricmp().
X * 
X * Revision 1.3  90/09/08  18:45:54  mj
X * Some changes.
X * 
X * Revision 1.2  90/08/12  14:14:09  mj
X * Removed unused code.
X * 
X * Revision 1.1  90/06/28  22:04:15  mj
X * Much rework of the sources, no more hsu.h and other clean up.
X * rmail improved, now handles special XENIX quirks.
X * 
X * Revision 1.0  90/06/21  19:01:04  mj
X * Initial revision
X * 
X *
X *****************************************************************************
X * This version hacked and maintained by:
X *    _____ _____
X *   |     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
X *   | | | |   | |   Republikplatz 3   DOMAIN:  mju@dfv.rwth-aachen.de
X *   |_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931
X *
X * Original version of these programs and files:
X *
X *   Teemu Torma
X *   Heikki Suonsivu   FIDO: 2:504/1   UUCP: ...!mcsun!santra!hsu
X *
X *****************************************************************************/
X
#include "fidogate.h"
X
#include <varargs.h>
#include <unistd.h>
X
#include "shuffle.h"
X
X
/*
X * We supply our own version of the toupper()/tolower()
X * macros, because the exact behaviour of those in
X * <ctype.h> varies among systems.
X */
X
#undef _toupper
#undef _tolower
#undef toupper
#undef tolower
X
#define	_toupper(c)	((c)-'a'+'A')
#define	_tolower(c)	((c)-'A'+'a')
#define	toupper(c)	(islower(c) ? _toupper(c) : (c))
#define	tolower(c)	(isupper(c) ? _tolower(c) : (c))
X
X
X
#define labs(n) (((n) < 0l) ? (-(n)) : (n))
X
extern void exit(), perror();
extern long atol();
extern time_t time();
X
X
/***** strnicmp() --- compare n chars of strings ignoring case ***************/
X
int strnicmp(sa, sb, len)
register char *sa, *sb;
int len;
{
X	while(len--)
X		if(tolower(*sa) == tolower(*sb)) {
X			sa++;
X			sb++;
X		}
X		else if(tolower(*sa) < tolower(*sb))
X			return(-1);
X		else
X			return(1);
X	return(0);
}
X
X
X
/***** stricmp() --- compare strings ignoring case ***************************/
X
int stricmp(sa, sb)
register char *sa, *sb;
{
X	while(tolower(*sa) == tolower(*sb)) {
X		if(!*sa)
X			return(0);
X		sa++;
X		sb++;
X	}
X	return(tolower(*sa) - tolower(*sb));
}
X
X
X
/***** xtol() --- convert hex string to long *********************************/
X
long xtol(s)
char *s;
{
long val = 0;
int n;
X
X	while(*s) {
X		n = toupper(*s) - (isalpha(*s) ? 'A'-10 : '0');
X		val = val*16 + n;
X		s++;
X	}
X	return(val);
}
X
X
X
FILE *logfp = NULL;
X
/* Lock file descriptor up to the end. If yur system doesn't have lockf()
X   (also known as locking()), or other region or file locking function
X   or system call, this should be done with lock-files. */
X
int
lock(fd)
X     int fd;
{
#ifdef LOCK_LOCKF
X  return lockf(fd, F_LOCK, 0l);
#else
X  return locking(fd, F_LOCK, 0l);
#endif
}
X
/* Unlock file descriptor up to the end. Routine which calls this should
X   first seek to original position. */
X
int
unlock(fd)
X     int fd;
{
#ifdef LOCK_LOCKF
X  return lockf(fd, F_ULOCK, 0l);
#else
X  return locking(fd, F_ULOCK, 0l);
#endif
}
X
/* Return ascii-date in specified format. If format string is null, return
X   date as date(1) returns it. Format is same than date(1) has, in addition
X   %z, which means timezone name. Clock is the time to convert, if NULL,
X   we'll use current time. */
X
char *
date(fmt, clock)
X     char *fmt;
X     time_t *clock;
{
X  /* names for weekdays */
X  static char *weekdays[] = {
X    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
X  };
X
X  /* names for months */
X  static char *months[] = {
X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X  };
X
X  static char buffer[80];
X  char *bp = buffer;
X  time_t _clock;
X  struct tm *tm;
X
X  if (!clock)
X    _clock = time((long *) 0);
X  tm = localtime(clock ? clock : &_clock);
X
X  /* if no format string, this is default */
X  if (!fmt)
X    fmt = "%a %h %d %T %z 19%y";
X
X  for (*bp = 0; *fmt; fmt++)
X    switch (*fmt)
X      {
X      case '%':
X        switch (*++fmt)
X          {
X            /* newline */
X          case 'n':
X            *bp++ = '\n';
X            break;
X            /* tabulator */
X          case 't':
X            *bp++ = '\t';
X            break;
X            /* month number 1-12 */
X          case 'm':
X            (void) sprintf(bp, "%02d", tm->tm_mon + 1);
X            while (*bp)
X              bp++;
X            break;
X            /* day of month 1-31 */
X          case 'd':
X            (void) sprintf(bp, "%2d", tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X          case 'q':
X            (void) sprintf(bp, "%02d", tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X            /* year 00-99 */
X          case 'y':
X            (void) sprintf(bp, "%02d", tm->tm_year);
X            while (*bp)
X              bp++;
X            break;
X            /* date in format YY/MM/DD */
X          case 'D':
X            (void) sprintf(bp, "%02d/%02d/%02d", tm->tm_year,
X                           tm->tm_mon + 1, tm->tm_mday);
X            while (*bp)
X              bp++;
X            break;
X            /* hour 0-23 */
X          case 'H':
X            (void) sprintf(bp, "%02d", tm->tm_hour);
X            while (*bp)
X              bp++;
X            break;
X            /* minutes 0-59 */
X          case 'M':
X            (void) sprintf(bp, "%02d", tm->tm_min);
X            while (*bp)
X              bp++;
X            break;
X            /* seconds 0-59 */
X          case 'S':
X            (void) sprintf(bp, "%02d", tm->tm_sec);
X            while (*bp)
X              bp++;
X            break;
X            /* time in format HH:MM:SS */
X          case 'T':
X            (void) sprintf(bp, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
X                           tm->tm_sec);
X            while (*bp)
X              bp++;
X            break;
X            /* day of year 1-356 */
X          case 'j':
X            (void) sprintf(bp, "%03d", tm->tm_yday + 1);
X            while (*bp)
X              bp++;
X            break;
X            /* weekday 0-6 */
X          case 'w':
X            (void) sprintf(bp, "%d", tm->tm_wday);
X            while (*bp)
X              bp++;
X            break;
X            /* name of weekday 'Mon', 'Tue', ... , 'Sun' */
X          case 'a':
X            (void) strcpy(bp, weekdays[tm->tm_wday]);
X            while (*bp)
X              bp++;
X            break;
X            /* name of month 'Jan', 'Feb', ... , 'Dec' */
X          case 'h':
X            (void) strcpy(bp, months[tm->tm_mon]);
X            while (*bp)
X              bp++;
X            break;
X            /* name of timezone, e.g. EST */
X          case 'z':
X            (void) strcpy(bp, *tzname);
X            while (*bp)
X              bp++;
X            break;
X            /* numeric time zone, e.g. +0200 */
X          case 'o':
X            (void) sprintf(bp, "%c%02ld%02ld", (timezone <= 0l) ? '+' : '-',
X                           (labs(timezone) / (60l * 60l)),
X                           (labs(timezone) % (60l * 60l)));
X            while (*bp)
X              bp++;
X            break;
X          case 'l':
X            /* military time zone, Z = UT, A = -1, M = -12, (J not used),
X               N = +1, Y = +12.. */
X            *bp = (timezone == 0l) ? 'Z' : ((int) (labs(timezone) /
X                                                   (60l * 60l)) +
X                                            ((timezone < 0l) ? 'M' : '@'));
X            if (timezone > 0l && *bp >= 'J')
X              (*bp)++;
X            *++bp = 0;
X            break;
X          default:
X            *bp++ = *fmt;
X            break;
X          }
X        break;
X      default:
X        *bp++ = *fmt;
X        break;
X      }
X
X  *bp = 0;
X  return buffer;
}
X
X
X
/*
X * strerror()  ---  get string from sys_errlist[]
X */
X
char *
strerror(errnum)
int errnum;
{
extern int sys_nerr;
extern char *sys_errlist[];
X
X	if (errnum > 0 && errnum < sys_nerr)
X		return sys_errlist[errnum];
X	return "";
}
X
X
X
/*
X * Log to logfile. If logfile is not open, open it.
X *
X * If first character in format string is '$', print also errno. If external
X * variable verbose is set, logging will be done also to stderr.
X */
X
/**VARARGS**/
void
log(va_alist)
va_dcl
{
va_list args;
char *fmt;
X
X	va_start(args);
X
X	fmt = va_arg(args, char *);
X
X	if(!logfp)
X		if ((logfp = fopen(LOGFILE, "a")) == NULL) {
X			perror("Cannot open log file");
X			return;
X		}
X
X	(void) fprintf(logfp, "%s: ", date("%d %h %y %T", (long *) 0));
X	(void) vfprintf(logfp, *fmt == '$' ? fmt + 1 : fmt, args);
X	if (*fmt == '$')
X		(void) fprintf(logfp, "\n\t\terrno = %d (%s)\n", errno, strerror(errno));
X	else
X		(void) fprintf(logfp, "\n");
X	(void) fflush(logfp);
X
X	/*
X	 * if verbose is set, print also to stderr (without date)
X	 */
X	if (verbose) {
X		(void) vfprintf(stderr, *fmt == '$' ? fmt + 1 : fmt, args);
X		if (*fmt == '$')
X			(void) fprintf(stderr, "\n\t\terrno = %d (%s)\n", errno, strerror(errno));
X		else
X			(void) fprintf(stderr, "\n");
X		(void) fflush(stderr);
X	}
X
X	va_end(args);
}
X
X
X
/*
X * Debug output. First argument should be number, rest are used arguments
X * for vfprintf(3S). If external variable verbose has equal or greater
X * value than first number, vfprintf(3S) will be used to print other
X * arguments to stderr.
X */
X
/**VARARGS**/
void
debug(va_alist)
va_dcl
{
va_list args;
char *fmt;
int debug_level;
X
X	va_start(args);
X
X	debug_level = va_arg(args, int);
X	fmt = va_arg(args, char *);
X
X	if (debug_level <= verbose) {
X		if (*fmt != '>' && *fmt != '<')
X		(void) vfprintf(stderr, fmt, args);
X		(void) fprintf(stderr, "\n");
X	}
X
X	va_end(args);
}
X
X
X
/* Get next job number. New sequemnt number will be taken from file
X   LIBDIR/seq, which is in ascii-format and new number will be saved
X   back there. */
X
long
job_number()
{
X	return sequencer(JOBSEQ);
}
X
/* General sequencer */
X
long
sequencer(filename)
X     char *filename;
{
X  char seqfile[128], buffer[14];
X  FILE *fp;
X  long seqn = 0;
X
X  (void) sprintf(seqfile, "%s", filename);
X  if ((fp = fopen(seqfile, "r+")) == NULL)
X    {
X      if (errno == ENOENT)
X    {
X      if ((fp = fopen(seqfile, "w+")) == NULL)
X        {
X          log("$Can not create seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X      fputs("1", fp);
X      fclose(fp);
X      if ((fp = fopen(seqfile, "r+")) == NULL)
X        {
X          log("$Can not open new seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X    }
X      else
X    {
X      log("$Can not open seq-file %s", seqfile);
X      exit(EX_OSFILE);
X    }
X    }
X
X  (void) lock(fileno(fp));
X  if (fgets(buffer, 14, fp))
X    seqn = atol(buffer);
X  else
X    seqn = 0; /* This can theoretically fail */
X
X  seqn++;
X  (void) rewind(fp);
X  (void) fprintf(fp, "%ld\n", seqn);
X  (void) unlock(fileno(fp));
X  (void) fclose(fp);
X  return seqn;
}
X
/* Returns current last sequence number */
long
getsequencer(filename)
X     char *filename;
{
X  char seqfile[128], buffer[14];
X  FILE *fp;
X  long seqn = 0;
X
X  (void) sprintf(seqfile, "%s", filename);
X  if ((fp = fopen(seqfile, "r+")) == NULL)
X    {
X      if (errno == ENOENT)
X    {
X      if ((fp = fopen(seqfile, "w+")) == NULL)
X        {
X          log("$Can not create seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X      fputs("1", fp);
X      fclose(fp);
X      if ((fp = fopen(seqfile, "r+")) == NULL)
X        {
X          log("$Can not open new seq-file %s", seqfile);
X          exit(EX_OSFILE);
X        }
X    }
X      else
X    {
X      log("$Can not open seq-file %s", seqfile);
X      exit(EX_OSFILE);
X    }
X    }
X
X  (void) lock(fileno(fp));
X  if (fgets(buffer, 14, fp))
X    seqn = atol(buffer);
X  else
X    seqn = 0; /* This can theoretically fail */
X
X  (void) unlock(fileno(fp));
X  (void) fclose(fp);
X  return seqn;
}
X
/* Get full pathname for spoolfile with new job number. File is in
X   spool directory and contains prefix followed by four digit
X   job number. */
X
char *
spoolfile(prefix)
X     char *prefix;
{
X  static char file[BUFLEN];
X
X  (void) sprintf(file, "%s/%s%08ld", SPOOL, prefix, job_number());
X  return file;
}
X
/* Return basename of s */
X
char *
basename(s)
X     register char *s;
{
X  register char *p = s;
X
X  while (*s)
X    if (*s++ == '/')
X      p = s;
X  return p;
}
X
/* Open file with directory name and filename. */
X
FILE *
pfopen(dir, name, mode)
X     char *dir, *name, *mode;
{
X  char filename[128];
X
X  (void) strcpy(filename, dir);
X  (void) strcat(filename, "/");
X  (void) strcat(filename, name);
X
X  return fopen(filename, mode);
}
X
char *baseit(n)
X     long n;
{
X  static char tab[] =
X    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
X  int count = 0;
X
X  SHUFFLEBUFFERS;
X
X  while (n)
X    {
X      tcharp[count] = tab[n % strlen(tab)];
X      n = n / strlen(tab);
X      count++;
X    }
X
X  tcharp[count] = 0;
X  return tcharp;
}
X
/* Create packet name for node given */
X
sprintpacketname(s, node)
X     char *s;
X     Node node;
{
X  sprintf(s, "%s%s.%s.%s", node.point ?
X      sprintfs("%s.", baseit( (long) node.point)) : "",
X      baseit( (long) node.node), baseit( (long) node.net),
X      baseit( (long) node.zone));
}
X
/* Create packet name for inbound xx.xx.xx.xx.num. If point
X   number is 0, don't include it. All numbers are in ~63-base to squeeze
X   them to as small space as possible. It could be more sensible solution
X   to make them directory trees but I would need more time for that. This
X   trick makes finding packets difficult.
X   */
X
sprintipacketname(s, node)
X     char *s;
X     Node node;
{
X  sprintf(s, "%s%s.%s.%s.%s", node.point ?
X      sprintfs("%s.", baseit( (long) node.point)) : "",
X      baseit( (long) node.node), baseit( (long) node.net),
X      baseit( (long) node.zone), baseit(sequencer(IPACKETSEQUENCE)));
}
X
X
X
/*
X * Get line from config file. If *-character is in first column, report
X * EOF, and otherwise return line with comments stripped. This causes
X * effect, that each section in configuration file looks like it's own
X * file. Arguments and return value are the same than with fgets(3S).
X */
X
char *
getcl(buffer, len, fp)
char *buffer;
int len;
FILE *fp;
{
char *cp;
X
X	while (fgets(buffer, len, fp)) {
X		buffer[strlen(buffer) - 1] = 0;
X		if (*buffer == '*')
X			return (char *) NULL;
X		/* everything after #-sign is comment */
X		if (cp = strchr(buffer, '#'))
X			*cp = 0;
X		/* if there's something left, return it */
X		if (*buffer)
X			return buffer;
X	}
X	return (char *) NULL;
}
X
X
X
/* Scan config file to specified section. This mechanism is not very
X   effective, but otherwise it would get too complicated. */
X
void
section(number, config)
X     int number;
X     FILE *config;
{
X  char buffer[BUFLEN];
X
X  (void) rewind(config);
X  while (--number)
X    while (getcl(buffer, BUFLEN, config))
X      /* skip section */;
}
X
/* Get header field from file. */
X
#define MAX_HEADER_LEN 256
X
char *mheader(fp, headername)
X     FILE *fp;
X     char *headername;
{
X  static char header[MAX_HEADER_LEN];
X  long position;
X
X  position = ftell(fp);
X
X  rewind(fp);
X
X  /* Blank line terminates also, there shouldn't be any headers
X     after it any more */
X
X  while (fgets(header, MAX_HEADER_LEN, fp) && *header != '\n')
X    if (!strncmp(header, headername, strlen(headername)))
X      {
X    /* Remove \n at end */
X    header[strlen(header) - 1] = 0;
X    fseek(fp, position, 0);
X    return header + strlen(headername);
X      }
X
X  /* Not found, return empty string */
X
X  fseek(fp, position, 0);
X  return "";
}
SHAR_EOF
chmod 0644 funcs.c ||
echo 'restore of funcs.c failed'
Wc_c="`wc -c < 'funcs.c'`"
test 15826 -eq "$Wc_c" ||
	echo 'funcs.c: original size 15826, current size' "$Wc_c"
fi
# ============= rmail.c ==============
if test -f 'rmail.c' -a X"$1" != X"-c"; then
	echo 'x - skipping rmail.c (File already exists)'
else
echo 'x - extracting rmail.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'rmail.c' &&
/*:ts=4*/
/*****************************************************************************
X * FIDOGATE --- Gateway software UNIX <-> FIDO
X *
X * $Id: rmail.c,v 1.9 91/01/05 13:08:58 mj Exp $
X *
X * Replacement for rmail.
X * This program performs two functions:
X * - it checks for mail addressed to the FIDO domain (.fidonet.org)
X *   and passes these messages to rfmail
X * - It does some rudimentary transforming for domain style addresses
X *
X * This is just a small hack. For real mail processing use
X * sendmail or smail, which are much better at those things.
X *
X * $Log:	rmail.c,v $
X * Revision 1.9  91/01/05  13:08:58  mj
X * Recognize MY_HOSTNAME.MY_DOMAIN as local address.
X * 
X * Revision 1.8  90/12/09  17:35:50  mj
X * Readdress mail to UUCPFEED. Removed unnessary `GATEWAY' code.
X * 
X * Revision 1.7  90/12/02  21:22:32  mj
X * Changed program header to mention both authors of the original
X * software posted to alt.sources.
X * 
X * Revision 1.6  90/11/05  20:51:06  mj
X * Changed my signature in all program headers.
X * 
X * Revision 1.5  90/07/11  17:57:50  mj
X * Removed an obscure bug while feeding letter to mail receiving
X * process. Once in a while the result was garbage. The cause was
X * that the temporary file was opened by both the parent and the
X * child process. Even if the child never ever does something to
X * this file, this seems to confuse the operating system.
X * 
X * Revision 1.4  90/07/07  17:54:47  mj
X * Improved version. Now rmail is able to get destination address
X * from message header, `To: ...' line. This has been implemented
X * to make rmail work with nn, 'cause this news reader doesn't pass
X * the address as a command line argument to rmail.
X * 
X * Revision 1.3  90/07/01  15:20:37  mj
X * Fixed some bugs in funpack caused by the removal of alloca().
X * No more core dumps, but heaven knows, why it works now. Strange.
X * 
X * Revision 1.2  90/07/01  13:46:12  mj
X * Removed all calls to alloca(). All unsave malloc()'s without
X * checking the returned pointer are now done via xmalloc().
X * Fixed a malloc() error in rmail.
X * 
X * Revision 1.1  90/06/28  22:04:56  mj
X * Much rework of the sources, no more hsu.h and other clean up.
X * rmail improved, now handles special XENIX quirks.
X * 
X * Revision 1.0  90/06/19  18:34:10  mj
X * Initial revision
X * 
X *
X *****************************************************************************
X * This version hacked and maintained by:
X *    _____ _____
X *   |     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
X *   | | | |   | |   Republikplatz 3   DOMAIN:  mju@dfv.rwth-aachen.de
X *   |_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931
X *
X * Original version of these programs and files:
X *
X *   Teemu Torma
X *   Heikki Suonsivu   FIDO: 2:504/1   UUCP: ...!mcsun!santra!hsu
X *
X *****************************************************************************/
X
#include "fidogate.h"
X
X
X
#define PROGRAMNAME "rmail $Revision: 1.9 $"
X
X
X
/*
X * This is special to XENIX:
X * On XENIX, one can't use rmail for sending mail, 'cause this
X * program doesn't generate a correct `From user ...' line for
X * locally created mail. One has to execute /usr/lib/mail/execmail
X * instead. But execmail *always* generates it's own `From ...'
X * line, even if there is already one in the mail. So for mail
X * of remote origin, e.g. those messages created by funpack, one
X * must instruct execmail to handle this right. This can be done
X * with the `-f' flags of execmail, whose argument replaces user
X * in `From user ...' line.
X */
#ifdef M_XENIX
# define EXECMAIL							/* Use -f from, if neccessary */
# undef  RECVMAIL
# define RECVMAIL "/usr/lib/mail/execmail"	/* Force use of execmail */
#endif
X 
X
X
/* verbosity */
/* int verbose = 3; /**/
int verbose = INIT_VERBOSE;
X
X
X
/*
X * For domain type addresses (name@system):
X *
X * Local adress converting:
X *     user@localhost.localdomain  ->  user
X * .uucp domain to bang converting:
X *     user@system.uucp            ->  system!user
X *     user@system                 ->  system!user
X * Forward other messages to UUCP feed
X *     user@system.domain          ->  uucpfeed!domain!user
X */
X
char *process_address(addr)
char *addr;
{
static char hostname[256];
static char address[256];
static char newaddress[256];
char *p;
int len;
int not_uucp = FALSE;
X
X	gethostname(hostname, 20);
X	strcpy(address, addr);
X	debug(3, "Address to process: %s", addr);
X
X	if(p = strchr(address, '@')) {
X		/*
X		 * Domain address: name@system.domain
X		 *
X		 * Isolate user name, p points to system.domain
X		 */
X		*p++ = 0;
X		/*
X		 * Remove suffix ".uucp"
X		 */
X		len = strlen(p);
X		if(len>5 && (!strcmp(p+len-5, ".uucp") || !strcmp(p+len-5, ".UUCP")))
X			p[len - 5] = 0;
X		else if(strchr(p, '.'))
X			not_uucp = TRUE;
X		/*
X		 * If addressed to our hostname or full domain name,
X		 * just send to user name
X		 */
X		if(!strcmp(p, hostname))
X			*p = 0;
X		else {
X			strcat(hostname, MY_DOMAIN);
X			if(!strcmp(p, hostname))
X				*p = 0;
X		}
X		/*
X		 * Construct converted address
X		 */
X		if(!*p)								/* Local address */
X			strcpy(newaddress, address);
X		else if(not_uucp)					/* Address domain via UUCPFEED */
X			sprintf(newaddress, "%s!%s!%s", UUCPFEED, p, address);
X		else								/* UUCP address */
X			sprintf(newaddress, "%s!%s", p, address);
X		debug(2, "Renamed %s to %s", addr, newaddress);
X		return( strsave(newaddress) );
X	}
X	else
X		return( strsave(address) ); 
}
X
X
X
/*
X * Get name from `From user ...' line.
X * Understood format of From line is:
X *     `From USER day mon dd hh:mm:ss [zone] yyyy [remote from SYSTEM]'
X * Destroys contents of buf!
X */
X
char *get_from_name(buf)
char *buf;
{
char *name, *system;
static char from[64];
X
X	name   = "anonymous";					/* Defaults */
X	system = "";
X
X	buf += 5;								/* Skip `From ' */
X	if(*buf) {
X		name = buf;
X		while(*buf && *buf!=' ' && *buf!='\t')
X			buf++;
X		if(*buf) {
X			*buf++ = 0;
X			/* Scan for `remote from ' */
X			while(strlen(buf) >= 12)		/* 12 = strlen("remote from") */
X				if(!strncmp(buf, "remote from ", 12)) {		/* gefunden! */
X					buf += 12;
X					system = buf;
X					while(*buf && *buf!=' ' && *buf!='\t' && *buf!='\n')
X						buf++;
X					*buf = 0;
X					break;
X				}
X				else
X					buf++;
X		}
X	}
X	
X	if(*system) {
X		strcpy(from, system);
X		strcat(from, "!");
X	}
X	else
X		*from = 0;
X	strcat(from, name);
X		
X	return(from);
}
X
X
X
/*
X * get_to_name() --- Get destination address from `To: ...' line
X */
X
char *get_to_name(buffer)
char *buffer;
{
register char *cp, *np;
register int cnt;
Node dummynode;
static char to[64];
X
X	buffer += strlen("To: ");
X	*to = 0;
X
X	/*
X	 * Parse the name from `To: ...' line. There are basically
X	 * two formats:
X	 *     `User Name <address>'    or
X	 *     `address (User Name)'
X	 * We'll try to figure it out which format sender uses.
X	 */
X	if ((cp = strchr(buffer, '<')) && (np = strchr(cp, '>'))) {
X		/*
X		 * Format is 'From: Name <address>'
X		 */
X		for(np=cp+1, cnt=0; *np && *np!='>'; np++, cnt++)
X			to[cnt] = *np;
X		to[cnt] = 0;
X	}
X	else if ((cp = strchr(buffer, '(')) && (np = strchr(cp, ')'))) {
X		/*
X		 * Format is 'From: address (Name)'
X		 */
X		for(np=buffer, cnt=0; *np && *np!='(' && !isspace(*np); np++, cnt++)
X			to[cnt] = *np;
X		to[cnt] = 0;
X	}
X	else {
X		/*
X		 * Not a recognized format, just copy
X		 */
X		strncpy(to, buffer, 64);
X		to[63] = 0;
X		cnt = strlen(to);
X		if(to[cnt - 1] == '\n')
X			to[cnt - 1] = 0;
X	}
X	
X	return( *to ? to : NULL);
}
X
X	
X
/*
X * Open stream associated with programs standard input. Program is invoked
X * with given argument list. Popen(3S) would invoke mailer thru sh(1),
X * so this uses less memory and is faster.
X */
X
FILE *
open_mailer(program, args, pid)
char *program, **args;
int *pid;
{
FILE *fp;
int fd[2];
X
X	/* create pipe */
X	if (pipe(fd) == -1) {
X		perror("rmail: pipe");
X		exit(EX_OSERR);
X	}
X
X	switch (*pid = fork()) {
X		case -1:								/* Error */
X			perror("rmail: fork failed");
X			exit(EX_OSERR);
X		case 0:									/* Child */
X			(void) close(0);
X			if (dup(fd[0]) == 0) {
X				(void) close(fd[0]);
X				(void) close(fd[1]);
X				(void) execvp(program, args);
X				perror(program);
X			}
X			else
X				perror("rmail: dup");
X			exit(EX_OSERR);
X		default:								/* Parent */
X			(void) close(fd[0]);
X			if ((fp = fdopen(fd[1], "w")) == NULL) {
X				perror("rmail: fdopen");
X				exit(EX_OSERR);
X			}
X	}
X	return fp;
}
X
X
X
int main(argc, argv)
int argc;
char *argv[];
{
int cnt;
char **rargs, **fargs;
int rrec = 0, frec = 0, rargc = 1, fargc = 1;
int status = EX_OK;
char dummyname[100];
Node dummynode;
FILE *mailer;
static char buffer[BUFSIZ];
int stat_loc, pid;
char *from;
char *to;
static char tempname[64];
FILE *temp;
int in_header_flag;
X
X	/*
X	 * Allocate memory for argument lists of RECVMAIL and RFMAIL.
X	 * 2 extra pointers are needed, 'cause we eventually insert
X	 * `-f user' for XENIX execmail. 1 more extra pointer for
X	 * address from `To: ...' line.
X	 */
X	rargs = (char **)xmalloc( (argc + 4) * sizeof(char *) );
X	fargs = (char **)xmalloc( (argc + 4) * sizeof(char *) );
X
X	rargs[0] = RECVMAIL;
X	fargs[0] = RFMAIL;
X
X	/*
X	 * Scan thru receiver list and put all receivers in fidonet in fido-
X	 * mailer's receiver-list and all others in real rmails one. No
X	 * options can be passed to fidomailer thru this, because it would
X	 * be too difficult to determine which one goes to which one and
X	 * there might be same options also. Somehow it's good that fidomailer
X	 * is well hidden under this...
X	 */
X	for (cnt = 1; cnt < argc; cnt++)
X		if (*argv[cnt] == '-')
X			rargs[rargc++] = strsave(argv[cnt]);
X		else {
X			if(parse_address(argv[cnt], dummyname, &dummynode) == NULL) {
X				/*
X				 * No error from parse_address(), so this is
X				 * mail for FIDO.
X				 */
X				debug(2, "Argument %d (receiver %d) in fidomailer: %s",
X								fargc, frec + 1, argv[cnt]);
X				fargs[fargc++] = strsave(argv[cnt]);
X				frec++;
X			}
X			else {
X				/*
X				 * Not a valid FIDO address, so this must be for UUCP.
X				 * In this case process address further to convert
X				 * internet domain address name@system.domain to
X				 * UUCP bang address system!name.
X				 */
X				debug(2, "Argument %d (receiver %d) in rmail: %s",
X								rargc, rrec + 1, argv[cnt]);
X				rargs[rargc++] = process_address(argv[cnt]);
/*				rargs[rargc++] = strsave(argv[cnt]); /**/
X				rrec++;
X			}
X		}
X
X	/*
X	 * Open temporary file and copy mail from stdin to there
X	 */
X	tmpnam(tempname);
X	temp = fopen(tempname, "w");
X	if(!temp) {
X		log("$Can't create temporary file %s", tempname);
X		exit(1);
X	}
X	*buffer = 0;
X	from = to = NULL;
X	in_header_flag = TRUE;
#ifdef EXECMAIL
X	/*
X	 * Look form `From user ...' line
X	 */
X	*buffer = 0;
X	fgets(buffer, BUFSIZ, stdin);
X	if(!strncmp(buffer, "From ", 5)) {
X		from = get_from_name(buffer);
X		debug(3, "from = %s", from);
X		*buffer = 0;
X	}
X	else {
X		from = NULL;
X		goto test_header;
X	}
#endif
X	while(fgets(buffer, BUFSIZ, stdin)) {
test_header:
X		if(in_header_flag) {
X			if(*buffer == '\n')
X				in_header_flag = FALSE;
X			else if(!strncmp(buffer, "To: ", 4)) {
X				to = get_to_name(buffer);
X				debug(3, "to = %s", to);
X			}
X		}
X		fputs(buffer, temp);
X	}
X	fclose(temp);
X
X	/*
X	 * If no address on command line, then use the one from
X	 * get_to_name()
X	 */
X	if(!frec && !rrec && to) {
X		if(parse_address(to, dummyname, &dummynode) == NULL) {
X			/*
X			 * No error from parse_address(), so this is
X			 * mail for FIDO.
X			 */
X			debug(2, "Argument %d (receiver %d) in fidomailer: %s",
X							fargc, frec + 1, to);
X			fargs[fargc++] = strsave(to);
X			frec++;
X		}
X		else {
X			/*
X			 * Not a valid FIDO address, so this must be for UUCP.
X			 * In this case process address further to convert
X			 * internet domain address name@system.domain to
X			 * UUCP bang address system!name.
X			 */
X			debug(2, "Argument %d (receiver %d) in rmail: %s",
X							rargc, rrec + 1, to);
X			rargs[rargc++] = process_address(to);
/*			rargs[rargc++] = strsave(to); /**/
X			rrec++;
X		}
X	}
X
X	/*
X	 * NULL terminate arument lists
X	 */
X	rargs[rargc] = NULL;
X	fargs[fargc] = NULL;
X
X
X	if (rrec) {
X		/*
X		 * Mail to UUCP, use rmail (or XENIX special: execmail)
X		 */
X		debug(1, "Mail to UUCP, executing %s", RECVMAIL);
#ifdef EXECMAIL
X		/*
X		 * Insert `-f FROM' into argument list of execmail (rargs[])
X		 */
X		if(from) {
X			for(cnt=rargc; cnt>=1; cnt--)
X				rargs[cnt + 2] = rargs[cnt];
X			rargs[1] = "-f";
X			rargs[2] = from;
X			rargc += 2;
X		}
#endif /**EXECMAIL**/
X		/*
X		 * Open mailer and feed letter to it
X		 */
X        mailer = open_mailer(RECVMAIL, rargs, &pid);
X        temp = fopen(tempname, "r");
X        if(!temp) {
X        	log("$Can't open %s again", tempname);
X        	unlink(tempname);
X        	exit(1);
X        }
X		while (fgets(buffer, BUFSIZ, temp))
X			fputs(buffer, mailer);
X        fclose(mailer);
X        /*
X         * Wait for rmail to exit
X         */
X		wait(&stat_loc);
X		if(!status)
X			status = (stat_loc & 0xff) == 0 ? (stat_loc >> 8) & 0xff : 1;
X	}
X
X	if (frec) {
X		/*
X		 * Mail to FIDO, use rfmail
X		 */
X		debug(1, "Mail to FIDO, executing %s", RFMAIL);
X		/*
X		 * Open mailer and feed letter to it
X		 */
X        mailer = open_mailer(RFMAIL, fargs, &pid);
X        temp = fopen(tempname, "r");
X        if(!temp) {
X        	log("$Can't open %s again", tempname);
X        	unlink(tempname);
X        	exit(1);
X        }
X		while (fgets(buffer, BUFSIZ, temp))
X			fputs(buffer, mailer);
X        fclose(mailer);
X        /*
X         * Wait for rfmail to exit
X         */
X		wait(&stat_loc);
X		if(!status)
X			status = (stat_loc & 0xff) == 0 ? (stat_loc >> 8) & 0xff : 1;
X	}
X	
X	/*
X	 * Remove temporary file
X	 */
X	unlink(tempname);
X
X	exit(status);
}
SHAR_EOF
chmod 0644 rmail.c ||
echo 'restore of rmail.c failed'
Wc_c="`wc -c < 'rmail.c'`"
test 13476 -eq "$Wc_c" ||
	echo 'rmail.c: original size 13476, current size' "$Wc_c"
fi
# ============= fpack.c ==============
if test -f 'fpack.c' -a X"$1" != X"-c"; then
	echo 'x - skipping fpack.c (File already exists)'
else
echo 'x - extracting fpack.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'fpack.c' &&
/*:ts=4*/
/*****************************************************************************
X * FIDOGATE --- Gateway software UNIX <-> FIDO
X *
X * $Id: fpack.c,v 2.2 90/12/09 18:35:27 mj Exp $
X *
X * Create and update fidomail packets. Read mail messages from
X * spool directory and append them to packet. If packet doesn't
X * exist already, it will be created.
X *
X * $Log:	fpack.c,v $
X * Revision 2.2  90/12/09  18:35:27  mj
X * Rewrote some more code. Now support `X' header line and crash mail.
X * 
X * Revision 2.1  90/12/02  21:21:53  mj
X * Changed program header to mention both authors of the original
X * software posted to alt.sources.
X * 
X * Revision 2.0  90/11/23  21:49:24  mj
X * Major rewrite of fpack started: no more ugly messing around with
X * byte order, use machine independent function write_int() instead.
X * Removed some functions and macros.
X * 
X * Revision 1.3  90/11/05  20:49:38  mj
X * Changed my signature in all program headers.
X * 
X * Revision 1.2  90/07/29  18:10:52  mj
X * Place real net/node in message header for FIDO netmail. Also
X * a `^AFMPT x' kludge is generated in this case. Recipient of
X * our mail now gets real address from message, which should
X * make replying much easier.
X * 
X * Revision 1.1  90/06/28  22:04:07  mj
X * Much rework of the sources, no more hsu.h and other clean up.
X * rmail improved, now handles special XENIX quirks.
X * 
X * Revision 1.0  90/06/19  18:32:01  mj
X * Initial revision
X * 
X *
X *****************************************************************************
X * This version hacked and maintained by:
X *    _____ _____
X *   |     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
X *   | | | |   | |   Republikplatz 3   DOMAIN:  mju@dfv.rwth-aachen.de
X *   |_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931
X *
X * Original version of these programs and files:
X *
X *   Teemu Torma
X *   Heikki Suonsivu   FIDO: 2:504/1   UUCP: ...!mcsun!santra!hsu
X *
X *****************************************************************************/
X
#include "fidogate.h"
/*
#include <fcntl.h>
#include <sys/stat.h>
*/
X
X
#define PROGRAMNAME "fpack $Revision: 2.2 $"
X
X
X
extern time_t time();
extern int getopt();
extern int optind;
extern char *optarg;
extern unsigned sleep();
extern void exit();
extern void swab();
X
Node node;
int verbose = INIT_VERBOSE;
X
X
X
/*
X * Put string to file in null-terminated format.
X */
X
int put_string(fp, s)
FILE *fp;
char *s;
{
X	while (*s) {
X		putc(*s, fp);
X		s++;
X	}
X	putc(0, fp);
}
X
X
X
/*
X * Write 16-bit integer in 80x86 format, i.e. low byte first,
X * then high byte. Machine independent function.
X */
X
int write_int(value, fp)
int value;
FILE *fp;
{
X	putc(value & 0xff, fp);
X	putc((value >> 8) & 0xff, fp);
}
X
X
/*
X * Copy n-char String, force trailing `\0'
X */
X
char *strncpy0(d, s, n)
char *d, *s;
int n;
{
char *p;
X
X	p = strncpy(d, s, n);
X	d[n - 1] = 0;
X	return(p);
}
X	
X
X
/*
X * Write FIDO message header to mail packet. Information is taken
X * from input message header:
X *
X *     N node         Address to send to
X *     T name         Name of recipient
X *     F name         Name of sender
X *     S subject      Subject of message
X *     D date         Date of message (FTS-0001 format)
X *     X flags        Message flags: P=private, C=crash
X */
X
int write_hdr(source, packet)
FILE *source, *packet;
{
int private_flag = FALSE;
char buffer[BUFSIZ];
char from[SIZE_FROM],
X	 to[SIZE_TO],
X	 subject[SIZE_SUBJECT],
X	 date[SIZE_DATE];
Node msg_node;
int attr = 0;
char *p;
X
X	msg_node.zone = msg_node.net = msg_node.node = msg_node.point = -1;
X
X	/* clean up from, to and subject */
X	*from    = 0;
X	*to      = 0;
X	*subject = 0;
X	*date    = 0;
X
X	while (fgets(buffer, BUFSIZ, source) && *buffer != '\n') {
X		buffer[strlen(buffer) - 1] = 0; /* strip newline */
X		switch(*buffer) {
X			case 'N':
X				if (parsefnetaddress(buffer, &msg_node)) {
X					log("Invalid destination: %s", buffer);
X					return FALSE;
X				}
X				break;
X			case 'F':
X				strncpy0(from, buffer + 2, SIZE_FROM);
X				break;
X			case 'T':
X				strncpy0(to,   buffer + 2, SIZE_TO);
X				break;
X			case 'S':
X				strncpy0(subject, buffer + 2, SIZE_SUBJECT);
X				break;
X			case 'D':
X				strncpy0(date, buffer + 2, SIZE_DATE);
X				break;
X			case 'P':						/* Old `private' header */
X				attr |= ATTR_PRIVATE;
X				private_flag = TRUE;
X				break;
X			case 'X':						/* New flags header */
X				for(p=buffer+2; *p; p++)
X					switch(*p) {
X						case 'P':
X							attr |= ATTR_PRIVATE;
X							private_flag = TRUE;
X							break;
X						case 'C':
X							attr |= ATTR_CRASH;
X							break;
X					}
X				break;
X		}
X	}
X
X	/* Save all header values */
X	write_int(MSGTYPE, packet);							/* save msg type */
X	write_int(private_flag ? REAL_NODE : MY_NODE, packet);/* save our node */
X	write_int(msg_node.node, packet);					/* save messages node */
X	write_int(private_flag ? REAL_NET : MY_NET, packet);/* save our net */
X	write_int(msg_node.net, packet);					/* save messages net */
X	write_int(attr, packet);							/* save attributes */
X	write_int(0, packet);								/* cost, not used by us */
X	put_string(packet, date);							/* save time of mail */
X	put_string(packet, to);								/* save receiver */
X	put_string(packet, from);							/* save sender */
X	put_string(packet, subject);						/* save subject */
X
X	log("Msg from %s to %s at %s", from, to, ascnode(msg_node));
X
X	/* done with this header */
X	return TRUE;
}
X
X
X
/*
X *  Write packet header for new packet.
X */
X
int write_pkthdr(packet)
FILE *packet;
{
Packet header;
int count;
struct tm *tm;
time_t clock = time((long *) 0);
X
X	tm = localtime(&clock);
X
X	/* create packet structure */
X	header.orig_node = MY_NODE;
X	header.dest_node = node.node;
X	header.orig_net  = MY_NET;
X	header.dest_net  = node.net;
X
X	/* save time for header (why all these fields?) */
X	header.year      = tm->tm_year + 1900;
X	header.month     = tm->tm_mon;
X	header.day       = tm->tm_mday;
X	header.hour      = tm->tm_hour + 1;
X	header.minute    = tm->tm_min;
X	header.second    = tm->tm_sec;
X
X	header.rate      = MAXBAUD;
X	header.ver       = HDRVER;
X	header.product   = 0;
X	header.x1        = 0;
#ifdef FIDO_V11w
X	for(count = 0; count < 16; count++)
X		header.fill[count] = 0;
#else
X	for(count = 0; count < 8; count++)
X		header.pwd_kludge[count] = 0;
X	header.orig_zone = MY_ZONE;
X	header.dest_zone = node.zone;
X	for (count = 0; count < 16; count++)
X		header.B_fill2[count] = 0;
X	header.B_fill3 = 0;
#endif
X	/* write header to file */
X	write_int(header.orig_node, packet);
X	write_int(header.dest_node, packet);
X	write_int(header.year     , packet);
X	write_int(header.month    , packet);
X	write_int(header.day      , packet);
X	write_int(header.hour     , packet);
X	write_int(header.minute   , packet);
X	write_int(header.second   , packet);
X	write_int(header.rate     , packet);
X	write_int(header.ver      , packet);
X	write_int(header.orig_net , packet);
X	write_int(header.dest_net , packet);
X	putc(     header.product  , packet);
X	putc(     header.x1       , packet);
X	for(count = 0; count < 8; count++)
X		putc(header.pwd_kludge[count], packet);
X	write_int(header.orig_zone, packet);
X	write_int(header.dest_zone, packet);
X	for(count = 0; count < 16; count++)
X	  	putc(header.B_fill2[count], packet);
X	for(count = 0; count < 4; count++)
X		putc(header.B_fill3 << (8 * count), packet); /* pc long = 4 bytes! */
X
X	if(ferror(packet) || feof(packet)) {
X		log("$Write error on packet header");
X		return FALSE;
X	}
X
X	debug(1, "New packet created");
X
X	return TRUE;
}
X
X
X
int main(argc, argv)
int argc;
char *argv[];
{
DIR *dp;
struct dirent *dir;
FILE *msg, *packet;
char packet_name[16];
int c;
Node np;
char *error;
X
X	node.net = node.zone = -1;
X  
X	/* get options */
X	while ((c = getopt(argc, argv, "vf:")) != EOF)
X		switch (c) {
X			case 'f':
X				if (parsefnetaddress(optarg, &np)) exit(1);
X				node = np;
X				break;
X			case 'v':
X				verbose++;
X				break;
X			default:
X				fprintf(stderr, "%s\n\n", PROGRAMNAME);
X				fprintf(stderr, "Usage: fpack [-v] [-f Z:N/N.P]\n\n");
X				exit(EX_USAGE);
X		}
X
X	/* make sure that we got net/node */
X	if (node.net == -1 || node.node == -1) {
X		node.zone  = REM_ZONE;
X		node.net   = REM_NET;
X		node.node  = REM_NODE;
X		node.point = REM_POINT;
X		strcpy(node.name, REM_NAME);
X	}
X
#if 0
X  /* try to update nodelist-index */
X  if (error = update_index())
X    {
X      if (*error == '$')
X        log("$Cannot update nodelist-index: %s", error + 1);
X      else
X        log("Cannot update nodelist-index: %s", error);
X      exit(EX_OSERR);
X    }
#endif
X
X  /* goto spool directory, everything exiting happens there... */
X	if (chdir(SPOOL) == -1) {
X		log("$Can't chdir to %s", SPOOL);
X		exit(1);
X	}
X
#if 0
X	/* create packet name */
X	sprintpacketname(packet_name, node);
#else
X	/* MSDOS compatible packet names */
X	sprintf(packet_name, "%04x%04x.out", node.net, node.node);
#endif
X
X	if (access(sprintfs("out/%s", packet_name), 0) == 0) {
X		debug(1, "Packet out/%s exists, append to it", packet_name);
X		if ((packet = fopen(sprintfs("out/%s", packet_name), "r+")) == NULL) {
X			log("$Can't open out/%s for update", packet_name);
X			exit(1);
X		}
X		fseek(packet, -2L, 2);
X	}
X	else {
X		debug(1, "New packet out/%s", packet_name);
X		if ((packet = fopen(sprintfs("out/%s", packet_name), "w")) == NULL) {
X			log("$Can't open out/%s for writing", packet_name);
X			exit(1);
X		}
X		/* protect packet from users...*/
X		chmod(sprintfs("out/%s", packet_name), 0600);
X		/* write packet-header, if it fails, exit */
X		if (!write_pkthdr(packet)) {
X			unlink(sprintfs("out/%s", packet_name));
X			exit(1);
X		}
X	}
X
X	/* lock packet, wait if it's alredy locked */
X	while (lock(fileno(packet)) == -1 && errno == EAGAIN)
X		sleep(5);
X
X	/* open spool directory */
X	dp = opendir(".");
X	if(!dp) {
X		log("$Can't open spool directory %s", SPOOL);
X		exit(1);
X    }
X	while (dir = readdir(dp)) {
X		/* check that file is for us */
X		if(dir->d_name[0]=='M' && dir->d_name[1]=='.') {
X			msg = fopen(dir->d_name, "r");
X			if(!msg) {
X				log("$Can't open mail %s for reading", dir->d_name);
X				continue;
X			}
X			debug(1, "Adding mailfile %s", dir->d_name);
X
X			/* save header */
X			if(write_hdr(msg, packet)) {
X				/* copy mail text, replace newlines with <cr> <lf> */
X				while ((c = getc(msg)) && c != EOF) {
X					if (c == '\n')
X						putc('\r', packet);
X					putc(c, packet);
X				}
X				/* null-terminate msg text */
X				putc(0, packet);
X			}
X			fclose(msg);
X			if(unlink(dir->d_name) == -1)
X				log("$Unable to unlink %s", dir->d_name);
X		}
X	}
X	closedir(dp);
X
X	/* msg type 0 indicates end of packet */
X	write_int(0, packet);
X
X	fclose(packet);
X
X	exit(0);
}
SHAR_EOF
chmod 0644 fpack.c ||
echo 'restore of fpack.c failed'
Wc_c="`wc -c < 'fpack.c'`"
test 10480 -eq "$Wc_c" ||
	echo 'fpack.c: original size 10480, current size' "$Wc_c"
fi
true || echo 'restore of funpack.c failed'
echo End of part 4, continue with part 5
exit 0

--
 _____ _____
|     |___  |   Martin Junius     FIDO:    2:242/6.1   2:242/6.0
| | | |   | |   Republikplatz 3   DOMAIN:  mju@dfv.rwth-aachen.de
|_|_|_|_____|   D-5100 Aachen     Tel. (Voice) 0241-86931