home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Narc -- archive NetNews articles
- **
- ** Geoffrey Leach
- ** LatiCorp Inc.
- ** {att,bellcore,sun,ames,pyramid}!pacbell!laticorp!geoff
- */
-
- #include <stdio.h>
- #include <strings.h>
- #include <sys/file.h>
- #include "version.h"
- #include "patchlevel.h"
-
- #define TRUE 1
- #define FALSE 0
- #define MATCH 0
- #define EXISTS 0
- #define PROMPT 0
- #define SUBJECT 1
- #define INDEX 2
- #define PNULL (char *)0
- #define FNULL (FILE *)0
- #define NGNULL (sNewsGroup *)0
- #define INDEXR "Indexr"
-
- typedef struct
- {
- char * name;
- char * archive;
- char * volume_tag;
- int moderated;
- } sNewsGroup;
-
- extern char * fgets();
- extern char * malloc();
- extern char * getenv();
- extern char * strpbrk();
- extern char * optarg;
- extern int optind;
-
- sNewsGroup * NewsGroups[100];
- sNewsGroup prompted = {PNULL, PNULL, PNULL, FALSE};
- FILE * rc;
- FILE * tty;
- FILE * out;
- FILE * aindex;
- char arcdir[1024];
- char descr[1024];
- char arc_dir[1024];
- char line[1024];
- char head_buf[10240];
- char * head_buf_ptr = head_buf;
- char tmp_file[] = {"Narc.XXXXXX"};
- char arc_file[256];
- char arc_name[256];
- char p_archive[256];
- int debug = FALSE;
- int named = FALSE;
- int repost = FALSE;
- int selected = FALSE;
- int subjected = FALSE;
- int head_buf_lines = 0;
-
- sNewsGroup*
- lookup(name)
- char *name;
- {
- int i = -1;
-
- /*
- * A little surgery: get rid of the leading "Newsgroups: "
- * and remove any secondary newsgroups
- */
- name += 12;
- *(strpbrk(name, ",\n")) = '\0';
-
- /*
- * See if this newsgroup was listed in the user's rc file
- */
- while ( NewsGroups[++i] )
- {
- if ( strcmp(name, NewsGroups[i]->name) == MATCH )
- return NewsGroups[i];
- }
- return NGNULL;
- }
-
- void
- rename_article(tmp_file, name)
- char *tmp_file;
- char *name;
- {
- char ans[10];
- char rcmd[1024];
-
- if ( access(name, R_OK) == EXISTS )
- {
- printf("%s exists! (a)ppend, (i)gnore or (r)eplace [r]? ", arc_file);
- fflush(stdout);
- fgets(ans, 10, tty);
- switch ( ans[0] )
- {
- default:
- case 'r':
- break;
- case 'a':
- sprintf(rcmd, "cat %s >> %s", tmp_file, name);
- if ( debug )
- printf("%s\n", rcmd);
- system(rcmd);
- unlink(tmp_file);
- return;
- case 'i':
- return;
- }
- }
-
- if ( rename(tmp_file, name) )
- {
- sprintf(rcmd, "Unable to rename %s", name);
- perror(rcmd);
- exit(1);
- }
- }
-
- void
- close_article()
- {
- /*
- * The beginning of an article, and not the first.
- * Therefore, we have an article open that must be closed.
- */
- named = FALSE;
- repost = FALSE;
- subjected = FALSE;
- if ( strlen(arc_name) )
- {
- fputs(arc_name, aindex);
- fputs(": ", aindex);
- fputs(descr, aindex);
- }
- fclose(out);
-
- if ( selected )
- {
- rename_article(tmp_file, arc_file);
- out = fopen(tmp_file,"w");
- selected = FALSE;
- }
- else
- rewind(out);
- }
-
- void
- chop(str)
- char * str;
- {
- *(str +strlen(str) - 1) = '\0';
- }
-
- void
- fgets_buffer()
- {
- if ( (head_buf_ptr + strlen(line)) >= (head_buf + sizeof(head_buf)) )
- {
- printf("Could not find newsgroup (or volume) within %d lines\n",
- head_buf_lines);
- exit(1);
- }
-
- fgets(line, sizeof(line), stdin);
- strcpy(head_buf_ptr, line);
- head_buf_ptr += strlen(line) + 1;
- head_buf_lines++;
- }
-
- char *
- fgets_unbuffer()
- {
- if ( head_buf_lines )
- {
- strcpy(line, head_buf_ptr);
- head_buf_ptr += strlen(line) + 1;
- head_buf_lines--;
- return line;
- }
- return fgets(line, sizeof(line), stdin);
- }
-
- void
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int i;
- int opt;
- int vol;
- int mode = INDEX;
- int name_fd = 0;
- int article_name = 0;
- char * cmd;
- char * name;
- char * subj;
- sNewsGroup * NewsGroup;
-
- while ( (opt = getopt(argc, argv, "a:px")) != EOF )
- {
- switch ( opt )
- {
- case 'a':
- prompted.archive = optarg;
- break;
- case 'p':
- mode = PROMPT;
- break;
- case 'x':
- debug = TRUE;
- break;
- default:
- fprintf(stderr, "Usage: narc [-px] [-a archive]\n");
- fprintf(stderr, " version %s level %d\n", VERSION, PATCHLEVEL);
- exit(1);
- }
- }
-
- /*
- * Get the archive directory from the environment
- */
- strcpy(arcdir, getenv("ARCHIVE"));
-
- /*
- * Read the user's ~/.narc file (if he has one) for the
- * newsgroups to watch. What we are looking for is
- * The base of the archive directory (full path) this is the
- * prefix for the archive directory specified for each newsgroup.
- * Then, tab separated fields as follows:
- * newsgroup: what fillows Newsgroups: in the header.
- * archive: the directory under arcdir.
- * volume_tag: the tag for the line that says what the volume is
- * (moderated newsgroups only).
- * moderated: is this a moderated newsgroup? (0/1)
- */
- sprintf(line, "%s/.narcrc", getenv("HOME"));
- if ( (rc = fopen(line, "r")) != FNULL )
- {
- i = 0;
- while ( fgets(line, sizeof(line), rc ) != PNULL )
- {
- NewsGroup =
- NewsGroups[i++] = (sNewsGroup *)malloc(sizeof(sNewsGroup));
-
- *(name = index(line, ':')) = '\0';
- NewsGroup->name = malloc(strlen(line) + 1);
- strcpy(NewsGroup->name, line);
-
- subj = ++name;
- if ( (name = index(subj, ':')) == PNULL )
- {
- NewsGroup->moderated = FALSE;
- subj[strlen(subj) - 1] = '\0';
- }
- else
- {
- NewsGroup->moderated = TRUE;
- *name = '\0';
- }
- NewsGroup->archive = malloc(strlen(subj) + 1);
- strcpy(NewsGroup->archive, subj);
-
- if ( NewsGroup->moderated )
- {
- subj = ++name;
- NewsGroup->volume_tag = malloc(strlen(subj));
- strncpy(NewsGroup->volume_tag, subj, strlen(subj) - 1);
- }
-
- if ( debug )
- printf("%s\t%s\t%s\t%d\n", NewsGroup->name,
- NewsGroup->archive,
- NewsGroup->volume_tag,
- NewsGroup->moderated);
- }
- fclose(rc);
- }
-
- if ( (tty = fopen("/dev/tty", "r")) == FNULL )
- {
- perror("Error opening tty");
- exit(1);
- };
-
- /*
- * If the user has not forced an archive, find one.
- * As we will skip past the first subject line, we need
- * to buffer the head of the file.
- */
- if ( prompted.archive )
- NewsGroup = &prompted;
- else
- {
- /*
- * Look for the Newsgroups line in the header
- */
- do
- {
- fgets_buffer();
- }
- while ( strncmp(line, "Newsgroups: ", 12) != MATCH );
-
- /*
- * Is the newsgroup on the list? Note that we only look
- * at the first newsgroup that's specified.
- */
- if ( (NewsGroup = lookup(line)) == NGNULL )
- {
- printf("Archive directory for %s? ", line);
- fflush(stdout);
- fgets(p_archive, sizeof(p_archive), tty);
- chop(p_archive);
- prompted.archive = p_archive;
- NewsGroup = &prompted;
- }
-
- /*
- * If we find that we have a moderated newsgroup, we assume
- * that somewhere there will be a line in the header that tells
- * us about the current volume and the archive name of the
- * program that's in this article.
- */
- if ( NewsGroup->moderated )
- {
- if ( mode != PROMPT )
- mode = SUBJECT;
-
- do
- {
- fgets_buffer();
- }
- while ( strncmp(line, NewsGroup->volume_tag,
- strlen(NewsGroup->volume_tag)) != MATCH );
-
- vol = atoi(&line[strlen(NewsGroup->volume_tag) + 8 ]);
- }
- }
-
- /*
- * Everything that we need to know about output is determined.
- */
- if ( NewsGroup->moderated )
- sprintf(arc_dir, "%s/%s/v%02d", arcdir, NewsGroup->archive, vol);
- else
- sprintf(arc_dir, "%s/%s", arcdir, NewsGroup->archive);
-
- if ( debug )
- {
- printf("Archive directory selected is: %s\n", arc_dir);
- strcpy(arc_dir, ".");
- }
- else
- {
- if ( chdir(arc_dir) )
- {
- sprintf(arc_file, "Could not chdir to %s", arc_dir);
- perror(arc_file);
- exit(1);
- }
- }
-
- /*
- * The tmp file is created in the archive directory
- */
- mktemp(tmp_file);
- if ( (out = fopen(tmp_file,"w")) == FNULL )
- {
- sprintf(arc_file, "Error opening %s" , tmp_file);
- perror(arc_file);
- exit(1);
- };
-
- /*
- * This is the "scratch" index that gets the raw data from
- * each article subject. The user will extract one line for
- * each submission for the "real" index.
- */
- if ( (aindex = fopen(INDEXR, "a")) == FNULL )
- {
- perror("Error opening index");
- exit(1);
- };
-
- /*
- * If we are not moderated and the user has not requested us
- * to prompt for names, then we need to generate a name. No
- * advance preparation is required for this; if the .names file
- * does not exist, we will start naming at 000.
- */
- if ( !NewsGroup -> moderated && mode != PROMPT )
- {
- if ( (name_fd = open(".names", O_RDWR | O_CREAT, 0666)) == NULL )
- {
- perror("Error opening .names");
- exit(1);
- }
-
- read(name_fd, &article_name, sizeof(int));
- lseek(name_fd, 0L, L_SET);
- }
-
- /*
- * Now that we have somewhere to put the input, restart the input and
- * skip the first line which is (we hope) Path: ..
- */
- head_buf_ptr = head_buf;
- fgets_unbuffer(line);
- fputs(line, out);
-
- /*
- * Process stdin until EOF. We expect to have a sequence of net news
- * articles, each of which has a subject line.
- */
- while ( fgets_unbuffer() != PNULL )
- {
- if ( strncmp(line, "Path: ", 6) == MATCH )
- close_article();
-
- /*
- * Now that we have the new file (if that's what happened),
- * dispose of the current line
- */
- fputs(line, out);
-
- /*
- * Does this line define the archive name?
- */
- if ( !named && (strncmp(line, "Archive-name: ", 13) == MATCH) )
- {
- /*
- * Process only one archive name per article
- */
- named = TRUE;
-
- strcpy(arc_name, &line[14]);
- *(strpbrk(arc_name, "/ \n")) = '\0';
- }
-
- /*
- * Do we have a Subject?
- */
- if ( !subjected && (strncmp(line, "Subject: ", 9) == MATCH) )
- {
- /*
- * Process only one Subject: per article
- */
- selected = TRUE;
- subjected = TRUE;
-
- /*
- * Generate a name, using the subject line is possible
- */
-
- /*
- * First, drop the "Subject: "
- */
- subj = &line[9];
-
- /*
- * Then, if this is a re-posting note the fact so that we
- * don't hassle the user if the first posting happens to
- * be in the archive and skip the "REPOST "
- */
- if ( strncmp(subj, "REPOST ", 7) == MATCH )
- {
- subj += 7;
- repost = TRUE;
- }
-
- /*
- * How we generate the name depends on what kind of newsgroup
- */
- switch ( mode )
- {
- case SUBJECT: /* moderated groups */
- /*
- * Tell the user what's happening
- */
- printf("%s", subj);
-
- /*
- * Name should begin with something like v02i023:
- * Assume this, and use the fist 7 characters for
- * the file name
- */
- strncpy(arc_file, subj, 7);
- break;
- case PROMPT: /* user wants us to prompt for name */
- /*
- * This is the article
- */
- printf("%s", subj);
- fputs("Output file? ", stdout);
- fflush(stdout);
- fgets(arc_name, sizeof(arc_name), tty);
- chop(arc_name);
- if ( strlen(arc_name) == 0 )
- {
- /*
- * User declines to save this item
- */
- selected = FALSE;
- continue;
- }
- strcpy(arc_file, arc_name);
- break;
- case INDEX: /* generate name */
- sprintf(arc_file, "%03d", article_name++);
- strcpy(arc_name, arc_file);
- /*
- * Tell the user what we did
- */
- printf("%s: %s", arc_file, subj);
- break;
- default:
- break;
- }
- strcpy(descr, subj);
- }
- }
-
- close_article();
- unlink(tmp_file);
-
- if ( name_fd )
- {
- write(name_fd, &article_name, sizeof(int));
- close(name_fd);
- }
- }
-