home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume16
/
narc
/
narc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-01-17
|
12KB
|
539 lines
/*
** 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);
}
}