/*
**  Subsystem:   USENET Sources Archiver             
**  File Name:   rkive.c               
**                                                        
**  usage: rkive [-?dgstuvV] [-f config_file] [-n newsgroup] 
**                           [-B batchfile] [-S newsgroup]
**
**
** This software is Copyright (c) 1989, 1990, 1991 by Kent Landfield.
**
** Permission is hereby granted to copy, distribute or otherwise 
** use any part of this package as long as you do not try to make 
** money from it or pretend that you wrote it.  This copyright 
** notice must be maintained in any copy made.
**
** Use of this software constitutes acceptance for use in an AS IS 
** condition. There are NO warranties with regard to this software.  
** In no event shall the author be liable for any damages whatsoever 
** arising out of or in connection with the use or performance of this 
** software.  Any use of this software is at the user's own risk.
**
**  If you make modifications to this software that you feel 
**  increases it usefulness for the rest of the community, please 
**  email the changes, enhancements, bug fixes as well as any and 
**  all ideas to me. This software is going to be maintained and 
**  enhanced as deemed necessary by the community.
**
**              Kent Landfield
**              uunet!sparky!kent
**		kent@sparky.imd.sterling.com
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include "article.h"
#include "cfg.h"

#ifdef NNTP
char sccsid[] = "@(#)rkive.c	2.3 5/9/91 - NNTP Version";
#else
char sccsid[] = "@(#)rkive.c	2.3 5/9/91";
#endif /*!NNTP*/

/* 
** This is necessary since the builtin makedir call uses
** mknod which is a superuser only call for directories.
*/
#ifndef MKDIR 
# ifndef USE_SYSMKDIR
#   define ROOT_ONLY
# endif
#endif

char tmp_mailfile[] = "/tmp/rkive.mail";
char global_mailfile[] = "/tmp/gbl.mail";

char *batch_file = NULL;
char article_name[MAXNAMLEN];

int retrieve = FROM_DISK;      /* default archiving is done from local disk */
int overwrite;
int status_only;

struct stat sbuf;

FILE *inputfp;

int needs_to_be_archived();
char *save_article();
char *compress_file();
char *do_compress();
char *basename();
char *suffix();
char *format_output();
void archive();
void build_index();
void display_group_info();
void logit();
void log_activities();
void mail_file();
void notify_users();
void record_problem();
void set_ownership();

int fclose();
int stat();
int strcmp();
int strlen();
int system();
int unlink();
char *strcpy();
char *strcat();
char *strchr();
FILE *efopen();
void exit();

extern int yydebug;
extern char inputstring[];
extern parser_return_value;

extern int debug;
extern int verbose;
extern int test;
extern int inum;
extern int problem_article;
extern char *nntp_server;

void usage()
{
    (void)fprintf(stderr,"usage: %s [-?dgstuvV] [-f config_file]\n", progname);
    (void)fprintf(stderr,"          [-n newsgroup] [-B batchfile] [-S newsgroup]\n");
    (void)fprintf(stderr,"options:\n");
    (void)fprintf(stderr,"  -?   Display this message.\n");
    (void)fprintf(stderr,"  -d   Debug, assumes verbose flag as well.\n");
    (void)fprintf(stderr,"  -g   Fill in default values with group display.\n"); 
    (void)fprintf(stderr,"       Only effective with verbose mode flag.\n");
    (void)fprintf(stderr,"  -s   Display status of article archiving.\n");
    (void)fprintf(stderr,"  -t   Test mode, display what would be done but do no archiving.\n");
    (void)fprintf(stderr,"  -u   Unconditionally overwrite dupliclate articles.\n");
    (void)fprintf(stderr,"  -v   Verbose archiving information printed.\n");
    (void)fprintf(stderr,"  -V   Display %s software version information.\n",
                  progname);
    (void)fprintf(stderr,"  -f config_file\n");
    (void)fprintf(stderr,"       Specify alternate configuration file to be used.\n");
    (void)fprintf(stderr,"  -n newsgroup\n");
    (void)fprintf(stderr,"       Specify newsgroup to archive or display status for.\n");
    (void)fprintf(stderr,"  -A newsgroup\n");
    (void)fprintf(stderr,"       Take an article to archive from stdin.\n");
    (void)fprintf(stderr,"  -B batchfile\n");
    (void)fprintf(stderr,"       Read names of articles to archive from batchfile.\n");
    (void)fprintf(stderr,"       Note: Use of the -B option requires a newsgroup\n");
    (void)fprintf(stderr,"             be specified using the -n option.\n");
    (void)fprintf(stderr,"  -S newsgroup\n");
    (void)fprintf(stderr,"       Take the name of articles to archive from stdin.\n");
}

int main(argc, argv)
int argc;
char **argv;
{
   int getopt();
   void version();
   void setup_defaults();
   void init_article();

   int c;
   extern char *optarg;
   extern int opterr;
   char *nwsg = NULL;

   yydebug = FALSE;

   opterr = 0;
   progname = argv[0];
   inputfp = stdin;
   logfp = stdout;
   errfp = stderr;

   status_only = debug = verbose = 0;
   test = overwrite = fill_in_defaults = 0;

   /*
   ** Setup the default config file to be used
   ** unless the user specifies otherwise.
   */
   config_file = LOCATION;

   if (argc > 1) {
      while ((c = getopt(argc, argv, "?dgstuvVn:f:A:B:S:y")) != EOF) {
         switch (c) {
             case 'A':   /* stdin article archiving         */
                 retrieve = FROM_STDIN;
                 nwsg = optarg;
                 break;
             case 'B':   /* take filenames from batch file  */
                 retrieve = FROM_BATCHFILE;
                 batch_file = optarg;
                 break;
             case 'd':   /* for extremely verbose output    */
                 debug++;
                 verbose++;
                 break;
             case 'f':   /* alternate configuration file    */
                 config_file = optarg;  
                 break;  
             case 'g':   /* display defaults filled in      */
                 fill_in_defaults++;
                 break;
             case 'n':   /* specify an individual newsgroup */
                 nwsg = optarg;
                 break;
             case 's':   /* display article archive status  */
                 status_only++;
                 break;
             case 'S':   /* single article archiving        */
                 retrieve = FROM_NAME;
                 nwsg = optarg;
                 break;
             case 't':   /* test mode, show but do nothing  */
                 test++;
                 verbose++;
                 break;
             case 'u':   /* overwrite existing duplicates   */
                 overwrite++;
                 break;
             case 'v':   /* display expanded output         */
                 verbose++;
                 break;
             case 'V':   /* display software version only   */
                 version();
                 break;
             case 'y':   /* turn on yydebug facilities      */
                 yydebug++;
                 break;
             case '?':   /* display usage information       */
             default:    /* display usage, invalid option   */
                 usage();
                 return(1);
         }
      }
   }

   /*
   ** If the user has specified that input is be filenames
   ** retrieved from a batch file, the user must specify a
   ** a newsgroup in addition to the batch filename.
   */
   if (retrieve == FROM_BATCHFILE) {
       if (nwsg == NULL) {
           (void) fprintf(errfp,
                  "%s: Must specify a newsgroup when using batchfile mode\n",
                  progname);
           (void) fprintf(errfp,"Sample command line...\n");
           (void) fprintf(errfp,"\t%s -B %s -n newsgroup-here\n",
                  progname, batch_file);
           return(1);
       }
   }
   /*
   ** If the user has specified that the article is to be read from stdin,
   ** the user must specify a a newsgroup as well.  A request for status
   ** on an article from stdin makes little sense to me... If I am wrong
   ** let me know...
   */
   else if (retrieve == FROM_STDIN) {
       if (nwsg == NULL) {
           (void) fprintf(errfp, "%s: Must specify a newsgroup\n",progname);
           (void) fprintf(errfp,"Sample command line...\n");
           (void) fprintf(errfp,"\t%s -A newsgroup-here\n",progname);
           return(1);
       }
       if (status_only != 0) {
           (void) fprintf(errfp,"%s: can't get status for an article on stdin\n", progname);
           (void) fprintf(errfp,"Sample command line...\n");
           (void) fprintf(errfp,"\t%s -A newsgroup-here\n",progname);
           return(1);
       }
   }

   setup_defaults();

   init_article();

   for (c = 0; c <= num; c++)  {
       newsgrp = &group[c];
       /*
       ** Was a newsgroup specified on the command line ?
       */
       if (nwsg != NULL) {
          if (strcmp(nwsg, newsgrp->ng_name) != 0)
              continue;
       }
       archive();
   }

   if (!status_only) {
       /*
       ** Mail notification of the archived members to the 
       ** list of users specified in the configuration file
       ** and remove the file containing the archived info.
       */
       mail_file(mail, global_mailfile, "Rkive results");
       (void) unlink(global_mailfile);
   }
   return(0);
}

void archive()
{
    char *get_archived_rec();
    int retrieve_article();
    void get_header();

    int cct;
    int first;
    int val;
    char *rp, *rec;
    char *new_member;
    char *archived_file;
    
#ifdef ROOT_ONLY
    /*
    ** check to assure that the user is root if 
    ** actual archiving is to take place. This is necessary
    ** if there is no mkdir system call.
    */

    if (!status_only && (getuid() != 0)) {
        (void) fprintf(errfp, "%s: Sorry, Must be root to rkive.\n",
                        progname);
        exit(1);
    }
#endif /* ROOT_ONLY */

    inum = 0;  /* initialize chronological issue counter */

    /* Remove any existing temporary mail file */

    (void) unlink(tmp_mailfile);
    cct = 0;  /* counter for newsgroup message in global mail */

    /*
    ** Assure that there something specified in the 
    ** archive location variable...
    */
    if (!*newsgrp->location) {
        (void) fprintf(errfp, "SKIPPING %s: No archive location specified..\n",
                        newsgrp->ng_name);
        return;
    }

    /*
    ** print out the appropriate 
    ** header for the newsgroup.
    ** If the user wishes to see the newsgroup info as well as all the
    ** information about a file archived, the user must specify 
    ** rkive -svv to see it all... 
    */

    if (debug || (verbose == 2 && status_only)) {
        (void) fprintf(logfp,"\n\n");
        display_group_info(newsgrp);
        (void) fprintf(logfp,"\n");
    }
    else if (status_only || verbose == 2)
        (void) fprintf(logfp, "%s\n",newsgrp->ng_name);

    /*
    ** Create a path to the .archived file for the newsgroup's archive.
    ** This file is used to determine if an article has already been
    ** archived.  If the user did not specify one ...
    */
    if (!*newsgrp->arc_done)
       (void) sprintf(newsgrp->arc_done,"%s/.archived",newsgrp->location);

    /*
    ** Create a path to the .patchlog file for the newsgroup's archive.
    ** This file is used to record patches to posted software so that
    ** it can easily be determined what the full set of software is.
    */
    if (!*newsgrp->patchlog)
       (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location);

    /*
    ** first indicates that archiving has just begun for this group
    ** so that any necessary setup can be done in retrieve_article().
    */

    first = 1;

#ifdef NNTP
    /* 
    ** Determine if the newsgroup being archived is to be accessed via
    ** nntp or via local disk access.
    */
    if (*newsgrp->nntp) {
        nntp_server = newsgrp->nntp;
        retrieve = FROM_NNTP;
    }
    else if (*nntp) {
        nntp_server = nntp;
        retrieve = FROM_NNTP;
    }
#endif /*NNTP*/

    while ((val = retrieve_article(article_name,first)) != DONE) {
        first = 0;
        if (val == ERROR_ENCOUNTERED)
            return;
        else if (val != RETRIEVED) {
           /* 
           ** Invalid return value encountered, we're in truble now..
           */
           (void) fprintf(errfp,"Invalid return from retrieve_article..?? Skipping %s\n",
                          newsgrp->ng_path);
           return;
       }

       /*
       ** Read the news article file to extract the
       ** header information and fill appropriate
       ** data structures.
       */
       get_header(article_name);

       /* 
       ** If the user has specified that a quick status 
       ** listing should be produced then hop to it....
       ** FROM_STDIN is not allowed to have status requests.
       */

       if (status_only) {
            if ((rec = get_archived_rec(header.ident)) == NULL) {
                /*
                ** Assure that the article that we are going
                ** to give status on is one that would be archived
                ** if archiving was occuring. (e.g. does it match 
                ** the user supplied criteria for archiving...)
                */
                if (*newsgrp->match) {
                     parser_return_value = 0;
                     (void) strcpy(inputstring, newsgrp->match);
                     if (yyparse()) 
                         (void) fprintf(logfp, "Parse failed and returned %d\n",
                                                parser_return_value);
                     else {
                         /* Did match string produce true as result? */

                        if (debug)
                            (void) fprintf(logfp, "Parser returned %d\n",
                                                   parser_return_value);
                    }
                    if (!parser_return_value) {
#ifdef NNTP
                        /*
                        ** Since they only want status, remove the 
                        ** nntp transfer tmpfile if newsgroup is
                        ** being archived via nntp.
                        */
                        if (retrieve == FROM_NNTP)
                            (void) unlink(article_name);
#endif /*NNTP*/
                        continue;
                    }
               }
#ifdef NNTP
               if (retrieve == FROM_NNTP)
                  (void) fprintf(logfp,
                                 "\t[%s] Awaiting Archiving\n",header.ident);
               else
                  (void) fprintf(logfp,
                                 "\t[%s] Awaiting Archiving\n",article_name);
#else /*!NNTP*/
               (void) fprintf(logfp,"\t[%s] Awaiting Archiving\n",article_name);
#endif /*NNTP*/
            }
            else if (verbose) {
                if ((rp = strchr(rec,' ')) == NULL) {
#ifdef NNTP
                   if (retrieve == FROM_NNTP)
                      (void) fprintf(logfp,"\t[%s] Archived\n",header.ident);
                   else
                      (void) fprintf(logfp,"\t[%s] Archived\n",article_name);
#else /*!NNTP*/
                   (void) fprintf(logfp,"\t[%s] Archived\n",article_name);
#endif /*NNTP*/
                }
                else {
                    rp++;
                    *(rp-1) = '\0';
                    (void) fprintf(logfp,"\t%s Archived as %s\n",rec,rp);
                }
            }
#ifdef NNTP
            /*
            ** Since they only want status, remove the 
            ** nntp transfer tmpfile if newsgroup is
            ** being archived via nntp.
            */
            if (retrieve == FROM_NNTP)
                (void) unlink(article_name);
#endif /*NNTP*/
            continue;
       }  /* End of status only... */

       /* 
       **
       ** We have located a file that may need to be archived. 
       ** First read the header information from the found file.
       ** Next, determine if the file needs to be archived by
       ** doing a linear search of of the contents of the .archived file
       ** comparing the message-id of the new article with the message-ids
       ** recorded in the .archived file. If the new message-id is not
       ** specified in the .archived file, it has not been archived
       ** before and we can proceed with the archiving.
       ** Can we say slow... maybe dbm in the future ???
       */

       if (!needs_to_be_archived(header.ident)) {
#ifdef NNTP
           /*
           ** Remove the nntp transfer tmpfile if the
           ** newsgroup is being archived via nntp.
           */
           if (retrieve == FROM_NNTP)
               (void) unlink(article_name);
#endif /*NNTP*/
           /*
           ** Remove the tmpfile if the article came from stdin.
           */
           if (retrieve == FROM_STDIN)
               (void) unlink(article_name);
           continue;
       }
          
       if (*newsgrp->match) {
           (void) strcpy(inputstring, newsgrp->match);

           if (yyparse()) {
               (void) fprintf(logfp, "Parse failed and returned %d\n",
                                      parser_return_value);
#ifdef NNTP
               /*
               ** Remove the nntp transfer tmpfile if the
               ** newsgroup is being archived via nntp.
               */
               if (retrieve == FROM_NNTP)
                   (void) unlink(article_name);
#endif /*NNTP*/
               /*
               ** Remove the tmpfile if the article came from stdin.
               */
               if (retrieve == FROM_STDIN)
                   (void) unlink(article_name);
               continue;
           }

           /* Did match string produce true as result? */

           if (debug)
               (void) fprintf(logfp, "Parser returned %d\n", parser_return_value);

           if (!parser_return_value) {
#ifdef NNTP
               /*
               ** Remove the nntp transfer tmpfile if the
               ** newsgroup is being archived via nntp.
               */
               if (retrieve == FROM_NNTP)
                   (void) unlink(article_name);
#endif /*NNTP*/
               /*
               ** Remove the tmpfile if the article came from stdin.
               */
               if (retrieve == FROM_STDIN)
                   (void) unlink(article_name);
               continue;
           }
       }

       /*
       ** Archiving from here on out.
       */
 
       if ((new_member = save_article(article_name,newsgrp)) != NULL) {
           /*
           ** To not do any compression of setting of ownership
           ** for newsgroup articles that are archived by an
           ** external application.
           */
           if (newsgrp->type != EXTERNAL_COMMAND) {
               archived_file = compress_file(new_member,newsgrp);
               set_ownership(archived_file,new_member,newsgrp);
           }
           else
               archived_file = new_member;
           
           /*
           ** If a problem has been encountered,
           ** the function do_problem handles
           ** the logging, and notifying.
           */

           if (!problem_article) {
               log_activities(archived_file,newsgrp);
               build_index(archived_file,newsgrp);
               notify_users(archived_file,newsgrp,cct++);
           }
       }
       else {
           if (newsgrp->type != ONLY_ARCHIVE_NAME) {
               (void) fprintf(logfp,"Unable to archive %s/%s!!!\n",
                          newsgrp->ng_path, article_name);
           }
       }
#ifdef NNTP
       /*
       ** Remove the nntp transfer tmpfile if the
       ** newsgroup is being archived via nntp.
       */
       if (retrieve == FROM_NNTP)
           (void) unlink(article_name);
#endif /*NNTP*/
       /*
       ** Remove the tmpfile if the article came from stdin.
       */
       if (retrieve == FROM_STDIN)
           (void) unlink(article_name);
    }

    if (!status_only) {
        /* Mail notification of the archived members to the   */
        /* list of users specified in the configuration file  */
        /* and remove the file containing the archived info.  */

        mail_file(newsgrp->mail_list, tmp_mailfile, newsgrp->ng_name);
        (void) unlink(tmp_mailfile);
    }
    return;
}

/* 
** Notify Users of Archiving.
**      If users have been specified to be informed, check to see
**      if they have requested a specific logging format. If so
**      use the specified format to notify the user. If not, use
**      "file archived at path" message.
*/
void notify_users(filename,ng,num_msgs)
char *filename;
struct group_archive *ng;
int num_msgs;
{
    /*
    ** Are there users specified in the 
    ** newsgroup section ? 
    */
    if ( *(ng->mail_list) ) {
        if ( *(ng->logformat) )
           logit(tmp_mailfile, ng->logformat, filename);
        else
           logit(tmp_mailfile, DEFAULT_LOG_FORMAT, filename);
    }

    /* 
    ** Are there users specified in the 
    ** global section ? 
    */
    if ( *mail ) {
        if (num_msgs == 0) /* print the newsgroup name out */
            logit(global_mailfile, "\n\t\t:%G:\n",filename);
        if (*log_format)
            logit(global_mailfile, log_format,filename);
        else 
            logit(global_mailfile, DEFAULT_LOG_FORMAT, filename);
    }
}

/*
** Log_activities
**
** There are two possible logfiles that need to be written. 
** The group specific logfile (ng->logfile) and the global 
** log. If it has been configured to use a specific format
** for the logging, do so. Else, just record the fact the
** file was sucessfully archived and the date.          
*/
void log_activities(filename,ng)
char *filename;
struct group_archive *ng;
{
   long clock;
   long time();
   char *ctime();
   
   char logbuf[BUFSIZ];
   char dms_date[30];
   
   if ( !*(ng->logformat) || !*log_format) {
       clock = time((long *)0);
       (void) strcpy(dms_date, ctime(&clock));
       *(dms_date+(strlen(dms_date)-1)) = '\0';
       (void) sprintf(logbuf,"%s archived %s",filename, dms_date);
   }

   if ( *(ng->logformat) )
       logit(ng->logfile, ng->logformat, filename);
   else
       logit(ng->logfile, logbuf, filename);

   if ( *log_format )
       logit(log, log_format, filename);
   else
       logit(log, logbuf, filename);
}

/*
** logit
**
** This function is used to append a logfile record 
** if there is a logfile name specified.
**
*/

void logit(filename, format_of_log, arch_file)
char *filename;
char *format_of_log;
char *arch_file;
{
    FILE *fp, *fopen();
    char *qp;

    if (!test) {
        if ( *(filename) ) {   /* Is a logfile specified ? */
            if ((fp = fopen(filename,"a")) != NULL) {
                qp = format_output(format_of_log, arch_file, ARCHIVE);
                (void) fprintf(fp,"%s\n",qp);
                (void) fclose(fp);
            }
        }
    }
}    

/*
** Set_ownership
**
**      This functions is responsible for setting the owner, group
**      and modes of a file just put into the archive. Two file names
**      are passed to this function. "filename" contains the compression
**      suffix if the file is to be compressed. "flname" is the name of
**      the file without the compression suffix. If the ownership or
**      modes can not be set on the target "filename", this function
**      then tries to set the original file, "flname". In this manner,
**      the file permissions get set even if the compression routine fails.
*/     

void set_ownership(filename,flname,ng)
char *filename;             /* filename with compression suffix    */ 
char *flname;               /* filename without compression suffix */
struct group_archive *ng;
{
    int chmod();
    int chown();

    if (verbose)    /* Print out the actions about to be preformed */
        (void) fprintf(logfp,"chmod\t<%o> <%s>\n", ng->modes, filename);

    if (!test) {    /* change the file modes to the specified modes */
        if (chmod(filename,ng->modes) != 0) {
            /* Assume the compress failed and try the original... */
            if (chmod(flname,ng->modes) != 0) 
                record_problem("Can't change modes of %O", filename, ng);
        }
    }

    if (verbose) {  /* Print out the actions about to be preformed */
        (void) fprintf(logfp,"chown\t<%d> <%s>\n", ng->owner, filename);
        (void) fprintf(logfp,"chgrp\t<%d> <%s>\n", ng->group, filename);
    }

    if (!test) {    /* chown the owner/group to the desired values */
        if (chown(filename, ng->owner, ng->group) != 0) {
            /* Assume the compress failed and try the original... */
            if (chown(flname, ng->owner, ng->group) != 0) { 
                /*
                ** Are we on a system that has a braindamaged chown 
                ** and does not let a general user give files away ?
                ** Assume so.  Quota be gone!!
                */
                if (getuid() == 0)
                    record_problem("Can't change ownership of %O",filename,ng);
            }
        }
    }
}

void mail_file(user_list, file_to_mail, nwsgrp)
char *user_list;
char *file_to_mail;
char *nwsgrp;
{
    char  *list, *name;
    char  cmdstr[80];

    /* Is there a list of users to mail to ? */
    if ( !*user_list || (strlen(user_list) == 0))
        return;

    /* Was there a notification file created ? */
    if (stat(file_to_mail, &sbuf) != 0) 
        return;

    name = user_list;
    do {
       if ((list = strchr(name,',')) != NULL) {
            list++;
            *(list-1) = '\0';
        }

#ifdef SUBJECT_LINE
        (void) sprintf(cmdstr, "%s -s '%s' %s < %s", 
                   MAIL, nwsgrp, name, file_to_mail);
#else
        (void) sprintf(cmdstr, "%s %s < %s", MAIL, name, file_to_mail);
#endif
        if (verbose)
            (void) fprintf(logfp,"Mailing %s to %s\n",
                           nwsgrp, name);
        if (!test) 
            (void) system(cmdstr);

        name = list;

    } while (name != NULL);
    return;
}

void build_index(filename,ng)
char *filename;
struct group_archive *ng;
{
    if (*(ng->index)) {        /* Is there a newsgroup index file ?  */
        if (*(ng->indformat))  /* Yes, Is there a index file format? */
            logit(ng->index, ng->indformat, filename);
        else if (*index_format)    /* No, is there a global format ? */
            logit(ng->index, index_format, filename);
        else                   /* No, use the default index format   */
            logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
    }

    if (*mindex) {            /* Is there a global index file ?       */
        if (*index_format)   /* Yes, Is there a global file format ? */
            logit(mindex, index_format, filename);
        else                 /* No, so use the default index format  */
            logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
    }
}


char *compress_file(filename,ng)
char *filename;
struct group_archive *ng;
{
    static char compressed[MAXNAMLEN];

    (void) strcpy(compressed, filename);  /* store the filename */

    /* Check to see if a group specific compress was specified.      */
    /* If so, then execute the command with the filename passed in.  */
    /* Else check to see if a global compress was specified. If so,  */
    /* then execute the command with the filename passed in.         */
    /* If both are NULL, no compression is done.                     */

    if (*(ng->compress) || (*compress)) { 
        if (*(ng->compress)) 
            (void) strcat(compressed, do_compress(ng->compress, filename));
        else if (*compress) 
            (void) strcat(compressed, do_compress(compress, filename));

        /* Check to see if the compression worked. If not, return the */
        /* original file name passed to this function. The check is   */
        /* done by assuring the compressed file exists. If not then   */
        /* it is assumed that it failed.                              */

        if (stat(compressed, &sbuf) == -1) 
            (void) strcpy(compressed, filename);   /* restore filename */
    }
    return(compressed);
}

char *do_compress(packit,filename)
char *packit;
char *filename;
{
    char *comp_cmd;
    char cmd[BUFSIZ];

    (void) sprintf(cmd,"%s %s", packit, filename);

    /* 
    ** get the basename of the command to use.
    */
    comp_cmd = basename(packit);

    if (verbose)
       (void) fprintf(logfp,"%s %s\n", comp_cmd, filename);

    if (!test) 
       (void) system(cmd);

    return(suffix(comp_cmd));
}


/*
** Record_problem()
**	This function is used to log problems encountered
**	to the designated parties.
*/

void record_problem(msg_fmt,filename,ng)
char *msg_fmt;
char *filename;
struct group_archive *ng;
{
    /* 
    ** This function is used in the event that a problem
    ** has occurred during archiving. It mails a message
    ** to the newsgroup specified list and it mails a 
    ** message to the globally specified users.
    ** 
    ** It then logs the fact into both the newsgroup 
    ** and the global logfiles if they have been specified.
    */

    if ( *(ng->mail_list) ) 
        logit(tmp_mailfile, msg_fmt, filename);
    
    if ( *mail ) 
        logit(global_mailfile, msg_fmt,filename);
    
    /*
    ** Assure that the file you are logging information to
    ** has a directory to exist in.. If it fails, oh well..
    */

    (void)mkparents(ng->logfile);
    logit(ng->logfile, msg_fmt, filename);
    (void)mkparents(log);
    logit(log, msg_fmt, filename);
}
