home *** CD-ROM | disk | FTP | other *** search
- Subject: v19i064: NN, a Usenet news reader, Part03/15
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: mcvax!tidk!storm@uunet.UU.NET (Kim F. Storm)
- Posting-number: Volume 19, Issue 64
- Archive-name: nn/part03
-
- #!/bin/sh
- # this is part 3 of a multipart archive
- # do not concatenate these parts, unpack them in order with /bin/sh
- # file collect.c continued
- #
- CurArch=3
- if test ! -r s2_seq_.tmp
- then echo "Please unpack part 1 first!"
- exit 1; fi
- ( read Scheck
- if test "$Scheck" != $CurArch
- then echo "Please unpack part $Scheck next!"
- exit 1;
- else exit 0; fi
- ) < s2_seq_.tmp || exit 1
- echo "x - Continuing file collect.c"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> collect.c
- X putc('@', data);
- X hdr.dh_subject_length--;
- X }
- X
- X if (hdr.dh_subject_length)
- X Fwrite(subj_buf, sizeof(char), (int)hdr.dh_subject_length, data);
- X
- X return;
- X}
- NO_NEWS_IS_GOOD_NEWS
- echo "File collect.c is complete"
- chmod 0644 collect.c || echo "restore of collect.c fails"
- set `wc -c collect.c`;Sum=$1
- if test "$Sum" != "6420"
- then echo original size 6420, current size $Sum;fi
- echo "x - extracting config.h-dist (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > config.h-dist &&
- X/**************************** NN CONFIGURATION ***************************
- X *
- X * Configuration file for nn
- X *
- X * You must edit this file to reflect your local configuration
- X * and environment.
- X *
- X * Follow the instructions given in the comments. See the files
- X * INSTALLATION, NNTP, and PROBLEMS for more details.
- X */
- X
- X#define RELEASE 6
- X#define VERSION 3
- X
- X
- X#include <stdio.h>
- X#include <ctype.h>
- X
- X
- X/*********************** NETWORK DEPENDENT DEFINITIONS **********************
- X *
- X * Define NETWORK_DATABASE if you share the database through NFS on
- X * a network with different, non-compatible machines, e.g. SUNs and
- X * VAXen, or SUN-3 and SUN-4, or if you are using different compilers
- X * on the same architecture.
- X *
- X * In a homogenous network, you can leave it undefined for higher
- X * performance (no data conversion is needed).
- X */
- X
- X/* #define NETWORK_DATABASE /* */
- X
- X
- X/********************************** NNTP *********************************
- X *
- X * Define NNTP to enable nntp support.
- X *
- X * With NNTP, the nnmaster still maintains a local database of
- X * all article headers for fast access (and because NNTP does not
- X * support nn - yet), while the articles are fetched from the
- X * nntp server when they are read or saved.
- X *
- X * You may still share this database through NFS locally (see the
- X * description of NETWORK_DATABASE above) if you don't want to
- X * have separate nn databases on all your local systems.
- X *
- X * Consult the file NNTP for further information on the use of NNTP.
- X */
- X
- X/* #define NNTP /* */
- X
- X/*
- X * Define NNTP_SERVER to the name of a file containing the name of the
- X * nntp server.
- X *
- X * It is vital that both the nnmaster and all nn users on a machine
- X * uses the same nntp server, because the nn database is synchronized
- X * with a specific news active file.
- X *
- X * If the file name does not start with a slash, it is relative to
- X * LIB_DIRECTORY defined below.
- X */
- X
- X#define NNTP_SERVER "/usr/lib/nntp_server"
- X
- X
- X/***************** OPERATING SYSTEM DEPENDENT DEFINITIONS *******************
- X *
- X * Include the appropriate s- file for your system below.
- X *
- X * If a file does not exist for your system, you can use s-template.h
- X * as a starting point for writing you own.
- X */
- X
- X#include "s-usg3-1.h"
- X
- X
- X/********************** MACHINE DEPENDENT DEFINITIONS **********************
- X *
- X * Include the appropriate m- file for your system below.
- X *
- X * If a file does not exist for your system, you can use m-template.h
- X * as a starting point for writing you own.
- X */
- X
- X#include "m-m680x0.h"
- X
- X
- X/******************** SITE DEPENDENT DEFINITIONS **********************
- X *
- X * Edit the following part to suit your local system setup
- X */
- X
- X/*
- X * Specify where programs and data should be placed
- X *
- X * BIN_DIRECTORY - the location of the user programs
- X * LIB_DIRECTORY - the location of auxiliary programs and files
- X * DB_DIRECTORY - the directory containing the nn database
- X *
- X *
- X * notice: if you share the news directory accross a network, you should
- X * use something like /usr/spool/news/.nn for DB_DIRECTORY.
- X */
- X
- X#define BIN_DIRECTORY "/usr/local/bin"
- X#define LIB_DIRECTORY "/usr/local/lib/nn"
- X#define DB_DIRECTORY "/usr/spool/nn"
- X
- X/*
- X * Specify directories for the user and system manuals
- X *
- X * Adapt this to your local standards; the manuals will be named
- X * $(MAN_DIR)/program.$(MAN_SECTION)
- X */
- X
- X#define USER_MAN_DIR "/usr/man/man1"
- X#define USER_MAN_SECTION "1"
- X
- X#define SYS_MAN_DIR "/usr/man/man1"
- X#define SYS_MAN_SECTION "1m"
- X
- X/*
- X * Specify where to put temporary files. Overriden by $TMPDIR.
- X * Notice that nn does not create "large" temp files.
- X */
- X
- X#define TMP_DIRECTORY "/tmp"
- X
- X/*
- X * Specify owner and group for files belonging to this package.
- X *
- X * Specifically, the nnmaster will run suid/sgid to this owner and group.
- X *
- X * The only requirements are that the ownership allows the nnmaster to
- X * READ the news related files and directories, and the ordinary users
- X * to read the database and execute the nn* programs.
- X *
- X * Normal choices are: (news, news) and (your uid, your gid)
- X */
- X
- X#define OWNER "news"
- X#define GROUP "news"
- X
- X/*
- X * Define STATISTICS if you want to keep a record of how much
- X * time the users spend on news reading.
- X *
- X * Sessions shorter than the specified number of minutes are not
- X * recorded (don't clutter up the log file).
- X *
- X * This is entered into the file $LIB_DIRECTORY/Log with code U
- X */
- X
- X/* #define STATISTICS 5 /* minutes */
- X
- X/*
- X * Define HAVE_ROUTING if your mailer understands domain based
- X * adresses (...@...) and performs the necessary rerouting (e.g.
- X * Sendmail or Smail).
- X *
- X * Otherwise, nn will provide a simple routing facility using
- X * routing information specified in the file LIB_DIRECTORY/routes.
- X */
- X
- X#define HAVE_ROUTING /* */
- X
- X/*
- X * If HAVE_ROUTING is NOT defined, nn needs to know the name of
- X * your host. To obtain the host name it will use either of the
- X * 'uname' or 'gethostname' system calls as specified in the s- file
- X * included above.
- X *
- X * If neither 'uname' nor 'gethostname' is available, you must
- X * define HOSTNAME to be the name of your host. Otherwise, leave
- X * it undefined (it will not be used anyway).
- X */
- X
- X/* #define HOSTNAME "myhost" /* Not used if HAVE_ROUTING */
- X
- X/*
- X * Specify the location of your news programs and files
- X */
- X
- X#define INEWS_PATH "/usr/lib/news/inews"
- X#define NEWS_ACTIVE "/usr/lib/news/active"
- X#define NEWS_DIRECTORY "/usr/spool/news"
- X
- X/*
- X * Specify a mailer that accepts a letter WITH a header IN THE TEXT.
- X *
- X * A program named 'recmail' program is normally delivered with
- X * the news system.
- X * On BSD systems you can also use "/usr/lib/sendmail -t".
- X */
- X
- X#define REC_MAIL "/usr/lib/news/recmail"
- X
- X/*
- X * Define APPEND_SIGNATURE if you want nn to ask users to append
- X * ~/.signature to mail messages (reply/forward/mail).
- X *
- X * If the mailer defined in REC_MAIL automatically includes .signature
- X * you should not define this (it will fool people to include it twice).
- X *
- X * I think 'recmail' includes .signature, but 'sendmail -t' doesn't.
- X */
- X
- X/* #define APPEND_SIGNATURE /* */
- X
- X/*
- X * Default folder directory
- X */
- X
- X#define FOLDER_DIRECTORY "~/News"
- X
- X/*
- X * Max length of authors name (in "edited" format).
- X * Also size of "Name" field on the article menus.
- X * You may want to increase this if your terminals are wider than
- X * 80 columns.
- X */
- X
- X#define NAME_LENGTH 16
- X
- X/*
- X * Define RESIZING to make nn understand dynamic window-resizing.
- X * (It uses the TIOCGWINSZ ioctl found on most 4.3BSD systems)
- X */
- X
- X/* #define RESIZING /* */
- X
- X
- X/************************ CONFIGURATION COMPLETED ************************
- X *
- X * The rest of this file will not need any changes.
- X */
- X
- X#include "global.h"
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 config.h-dist || echo "restore of config.h-dist fails"
- set `wc -c config.h-dist`;Sum=$1
- if test "$Sum" != "6667"
- then echo original size 6667, current size $Sum;fi
- echo "x - extracting cvt-help.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > cvt-help.c &&
- X#include <stdio.h>
- X
- Xmain()
- X{
- X register int c;
- X
- X while ((c = getchar()) != EOF) {
- X if (c == ';') {
- X c = getchar();
- X if (c == ':') {
- X c = getchar();
- X putchar(c & 0xf);
- X continue;
- X }
- X putchar(';');
- X putchar(c);
- X continue;
- X }
- X if (c >= 1 && c <= 7) {
- X putchar(';');
- X putchar(':');
- X putchar(c | 0x40);
- X continue;
- X }
- X putchar(c);
- X }
- X
- X exit(0);
- X}
- X
- X
- X
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 cvt-help.c || echo "restore of cvt-help.c fails"
- set `wc -c cvt-help.c`;Sum=$1
- if test "$Sum" != "407"
- then echo original size 407, current size $Sum;fi
- echo "x - extracting data.h (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > data.h &&
- X/*
- X * Internal representation of the master, group, and article
- X * information read from the database.
- X *
- X * For each article read from the database, an article_header
- X * structure is initialized.
- X */
- X
- X/*
- X * global master data
- X */
- X
- Xtypedef struct {
- X time_t last_scan; /* age of active file at last scan */
- X group_number number_of_groups;
- X off_t next_group_write_offset; /* in .groups */
- X int free_groups; /* allocated during first visit */
- X} master_header;
- X
- X/*
- X * group information
- X */
- X
- Xtypedef struct group_header {
- X
- X /* this part of the header is read from */
- X /* the .master file */
- X
- X article_number first_l_article;
- X article_number last_l_article;
- X
- X off_t index_write_offset;
- X off_t data_write_offset;
- X
- X int group_name_length;
- X
- X int32 group_flag;
- X
- X# define MF(n) (1<<(n-1))
- X# define CF(n) (1<<(n+15))
- X
- X# define G_MASTER_FLAGS (MF(17)-1) /* flags that are saved on file */
- X
- X /* master flags */
- X
- X
- X# define G_MODERATED MF(1) /* group is moderated */
- X# define G_CONTROL MF(2) /* group is control group */
- X# define G_NO_DIRECTORY MF(3) /* group directory not found */
- X# define G_ALWAYS_DIGEST MF(4) /* always decode articles as digests */
- X# define G_NEVER_DIGEST MF(5) /* never decode articles as digests */
- X# define G_EXPIRE MF(6) /* expire in progress or pending */
- X# define G_BLOCKED MF(7) /* don't trust this entry */
- X
- X /* client flags */
- X
- X# define G_SUBSCRIPTION CF(1) /* from .rc */
- X# define G_READ CF(2) /* group has been read */
- X# define G_RC_UPDATED CF(3) /* .rc is updated */
- X# define G_DONE CF(4) /* finished with this group */
- X# define G_NEW CF(5) /* new group */
- X# define G_FOLDER CF(6) /* "group" is a folder file */
- X# define G_DIRECTORY CF(7) /* "group" is directory */
- X# define G_SELECTION CF(8) /* a selection exist (use it) */
- X# define G_UNREAD_COUNT CF(9) /* is included in unread_articles */
- X# define G_MAILBOX CF(10) /* user's mail box file */
- X
- X /* this part is initialized during reading of the .groups file */
- X
- X /* DO NOT CHANGE THE POSITION OF group_num AS THE FIRST FIELD */
- X /* AFTER THE PART WHICH IS SAVED IN THE MASTER FILE */
- X
- X group_number group_num;
- X
- X char * group_name;
- X
- X /* this part is used by the master to hold active file data */
- X /* and the reader to hold information from the .rc file */
- X
- X article_number first_article;
- X article_number last_article;
- X
- X struct group_header *next_group; /* group sequence */
- X struct group_header *prev_group;
- X
- X char *kill_list;
- X char *save_file; /* default save file from init */
- X
- X off_t rc_offset; /* offset in rc_file */
- X} group_header;
- X
- X
- X/* size of the part of the group header placed on backing storage */
- X
- X
- X#define SAVED_GROUP_HEADER_SIZE(group) \
- X (((char *)(&((group).group_num))) - ((char *)(&(group))))
- X
- X/*
- X * Internal article header information.
- X */
- X
- Xtypedef struct {
- X union {
- X article_number au_number; /* article number in the group */
- X char *au_string;
- X } au_union;
- X /* indexes to header line text */
- X off_t hpos; /* first byte of header */
- X off_t fpos; /* first byte in article text */
- X off_t lpos; /* last pos of article */
- X
- X time_stamp t_stamp; /* encoded time_stamp */
- X
- X char * sender; /* sender's name */
- X char * subject; /* subject (w/o Re:) */
- X
- X int16 replies; /* no of Re: */
- X int16 lines; /* no of lines */
- X
- X group_header *a_group; /* if merged article menu */
- X
- X int flag; /* flags: */
- X
- X# define AF(n) (1<<(n-1))
- X
- X# define A_SELECT AF(1) /* article has been selected */
- X# define A_SAME AF(2) /* same subject as prev. article */
- X# define A_DIGEST AF(3) /* digest sub article */
- X# define A_FULL_DIGEST AF(4) /* full digest */
- X# define A_FAKED AF(5) /* only 'number' is valid */
- X# define A_FOLDER AF(6) /* article file = "folder_path" */
- X# define A_CANCEL AF(7) /* folder entry cancelled */
- X# define A_SEEN AF(8) /* article presented on menu */
- X
- X} article_header;
- X
- X
- X#define a_number au_union.au_number
- X#define a_string au_union.au_string
- X
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 data.h || echo "restore of data.h fails"
- set `wc -c data.h`;Sum=$1
- if test "$Sum" != "4039"
- then echo original size 4039, current size $Sum;fi
- echo "x - extracting date_regexp.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > date_regexp.c &&
- X/*
- X * produce input for nngoback
- X *
- X * generates a regular expression for egrep that will
- X * match the last N days, execute egrep with this pattern
- X * and output a sequence of "group-name article" pairs
- X */
- X
- X#include "config.h"
- X#include <time.h>
- X
- X#define DAYS * 24 * 60 * 60
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X time_t now, then;
- X struct tm *tm, *localtime();
- X int then_year, then_month, then_day;
- X int first;
- X
- X if (argc != 2) {
- X fprintf(stderr, "usage: nngoback1 <days>\n");
- X exit(1);
- X }
- X
- X time(&now);
- X
- X then = now - (atoi(argv[1]) DAYS);
- X tm = localtime(&then);
- X then_year = tm->tm_year;
- X then_month = tm->tm_mon;
- X then_day = tm->tm_mday;
- X
- X tm = localtime(&now);
- X
- X printf("\t(");
- X
- X first = 0;
- X while (tm->tm_year > then_year) {
- X printf("%s%02d", first == 0 ? "../../(" : "|", tm->tm_year);
- X first = 1;
- X
- X tm->tm_year--;
- X tm->tm_mon = 11;
- X tm->tm_mday = 31;
- X }
- X if (first == 1) putchar(')');
- X
- X while (tm->tm_mon > then_month) {
- X printf(first == 0 ? "(" : first == 1 ? "|(" : "|");
- X first = 2;
- X printf("%02d", tm->tm_mon + 1);
- X tm->tm_mon --;
- X tm->tm_mday = 31;
- X }
- X if (first == 2) printf(")/../%02d", then_year);
- X
- X while (tm->tm_mday >= then_day) {
- X if (first != 0)
- X printf("|");
- X if (first != 3)
- X printf("%02d/(", then_month + 1);
- X first = 3;
- X printf("%02d", tm->tm_mday);
- X tm->tm_mday--;
- X }
- X if (first == 3) printf(")/%02d", then_year);
- X
- X printf(")\n");
- X
- X exit(0);
- X}
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 date_regexp.c || echo "restore of date_regexp.c fails"
- set `wc -c date_regexp.c`;Sum=$1
- if test "$Sum" != "1495"
- then echo original size 1495, current size $Sum;fi
- echo "x - extracting db.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > db.c &&
- X/*
- X * database access and update
- X */
- X
- X#include "config.h"
- X#include "db.h"
- X
- Xexport master_header master;
- Xexport group_header *active_groups, **sorted_groups;
- X
- X/*
- X * Init access to a group
- X */
- X
- Xexport group_header *current_group = NULL;
- X
- Xexport char group_path_name[FILENAME];
- Xexport char *group_file_name = NULL;
- X
- Xstatic char *group_position = NULL;
- X
- Xinit_group(gh)
- Xregister group_header *gh;
- X{
- X register char *p, *q;
- X
- X if (gh == NULL) return 0;
- X if (gh == current_group) return 1;
- X
- X current_group = gh;
- X
- X if (gh->group_flag & G_NO_DIRECTORY) return 0;
- X
- X if (gh->group_flag & G_FOLDER) {
- X group_position = NULL;
- X group_file_name = NULL;
- X strcpy(group_path_name, gh->group_name);
- X return 1;
- X }
- X
- X#ifdef NNTP
- X if (use_nntp && nntp_set_group(gh) < 0)
- X return 0;
- X#endif /* NNTP */
- X
- X if (group_position == NULL)
- X if (is_master)
- X group_position = group_path_name;
- X else {
- X strcpy(group_path_name, news_directory);
- X group_position = group_path_name + strlen(group_path_name);
- X *group_position++ = '/';
- X }
- X
- X for (p = group_position, q = gh->group_name; *q; q++)
- X *p++ = (*q == '.') ? '/' : *q;
- X
- X if (is_master) {
- X
- X /*
- X * The master will chdir to the group's directory to
- X * get better performance (can use relative path names).
- X *
- X * We cannot do the same for the user client, because of
- X * the 'save' commands.
- X */
- X
- X *p++ = NUL;
- X
- X#ifdef NNTP
- X if (!use_nntp) {
- X#endif
- X if (chdir(news_directory) < 0)
- X sys_error(news_directory);
- X
- X if (chdir(group_path_name) < 0)
- X return 0;
- X#ifdef NNTP
- X }
- X#endif /* NNTP */
- X
- X group_file_name = group_path_name;
- X
- X } else {
- X
- X /* client */
- X
- X *p++ = '/';
- X group_file_name = p;
- X }
- X
- X return 1;
- X}
- X
- X
- XFILE *open_groups(mode)
- X{
- X return open_file(relative(db_directory, "GROUPS"), mode);
- X}
- X
- X
- X
- X/*
- X * Open master file; read it in if first open.
- X */
- X
- XFILE *master_file = NULL;
- X
- Xopen_master(mode)
- X{
- X FILE *g;
- X int entries, n, cur_group;
- X char *strings;
- X register group_header *gh;
- X static int first_open = 1;
- X
- X
- X master_file = open_file(relative(db_directory, "MASTER"), mode|MUST_EXIST);
- X
- X if (mode == OPEN_CREATE || !first_open) return;
- X
- X first_open = 0;
- X
- X if (!db_read_master(master_file, &master))
- X sys_error("Incomplete MASTER");
- X
- X master.free_groups = master.number_of_groups / 10;
- X
- X entries = master.free_groups + master.number_of_groups;
- X
- X active_groups = (group_header *) calloc(entries, sizeof(group_header));
- X mem_check(active_groups, entries, "group headers");
- X
- X sorted_groups = (group_header **)
- X calloc(entries, sizeof(group_header *));
- X mem_check(sorted_groups, entries, "sorted group header pointers");
- X
- X strings = malloc((int)master.next_group_write_offset);
- X mem_check(strings, (int)master.next_group_write_offset,
- X "bytes for group names");
- X
- X g = open_groups(OPEN_READ|MUST_EXIST);
- X
- X n = fread(strings, sizeof(char), (int)master.next_group_write_offset, g);
- X if (n != (int)master.next_group_write_offset)
- X sys_error("Incomplete GROUPS file");
- X fclose(g);
- X
- X for (cur_group = 0, gh = active_groups;
- X cur_group < master.number_of_groups;
- X cur_group++, gh++) {
- X
- X sorted_groups[cur_group] = gh;
- X
- X if (!db_read_group(master_file, gh, -1))
- X sys_error("Incomplete MASTER file");
- X
- X gh->group_num = cur_group;
- X gh->group_name = strings;
- X strings += gh->group_name_length;
- X *strings++ = NUL;
- X }
- X
- X sort_groups();
- X}
- X
- X
- Xclose_master()
- X{
- X if (master_file != NULL) {
- X fclose(master_file);
- X master_file = NULL;
- X }
- X}
- X
- X
- Xupdate_group(gh)
- Xgroup_header *gh;
- X{
- X int flag;
- X
- X flag = gh->group_flag & ~G_MASTER_FLAGS;
- X
- X if (!db_read_group(master_file, gh, gh->group_num)) return -1;
- X
- X gh->group_flag |= flag;
- X
- X if (gh->group_flag & G_BLOCKED) return -1;
- X
- X return 1;
- X}
- X
- X
- Xstatic group_name_cmp(g1, g2)
- Xgroup_header **g1, **g2;
- X{
- X return strcmp((*g1)->group_name, (*g2)->group_name);
- X}
- X
- X
- Xsort_groups()
- X{
- X qsort(sorted_groups, master.number_of_groups,
- X sizeof(group_header *), group_name_cmp);
- X}
- X
- X
- Xgroup_header *lookup(name)
- Xchar *name;
- X{
- X register i, j, k, t;
- X
- X i = 0; j = master.number_of_groups - 1;
- X
- X while (i <= j) {
- X k = (i + j) / 2;
- X
- X if ( (t=strcmp(name, sorted_groups[k]->group_name)) > 0)
- X i = k+1;
- X else
- X if (t < 0)
- X j = k-1;
- X else
- X return sorted_groups[k];
- X }
- X
- X return NULL;
- X}
- X
- X
- Xart_collected(gh, art_num)
- Xgroup_header *gh;
- Xarticle_number art_num;
- X{
- X return gh->first_l_article <= art_num && gh->last_l_article >= art_num;
- X}
- X
- X
- XFILE *open_data_file(gh, d_or_x, mode)
- Xgroup_header *gh;
- Xchar d_or_x;
- Xint mode;
- X{
- X char data_file[FILENAME];
- X
- X sprintf(data_file, "%s/DATA/%d.%c", db_directory, gh->group_num, d_or_x);
- X
- X if (mode == -1) {
- X unlink(data_file);
- X return (FILE *)NULL;
- X } else
- X return open_file(data_file, mode);
- X}
- X
- X
- X#ifdef NETWORK_DATABASE
- X
- X#define MASTER_FIELDS 3
- X#define GROUP_FIELDS 6
- X#define ARTICLE_FIELDS 10
- X
- X
- Xtypedef int32 net_long;
- X
- X
- X#ifdef NETWORK_BYTE_ORDER
- X
- X#define net_to_host(buf, n)
- X#define host_to_net(buf, n)
- X
- X#else
- X
- Xstatic net_to_host(buf, lgt)
- Xregister net_long *buf;
- Xint lgt;
- X{
- X while (--lgt >= 0) {
- X *buf = ntohl(*buf);
- X buf++;
- X }
- X}
- X
- Xstatic host_to_net(buf, lgt)
- Xregister net_long *buf;
- Xint lgt;
- X{
- X while (--lgt >= 0) {
- X *buf = htonl(*buf);
- X buf++;
- X }
- X}
- X#endif /* not NETWORK_BYTE_ORDER */
- X#endif /* NETWORK_DATABASE */
- X
- X
- Xdb_read_master(f, masterp)
- XFILE *f;
- Xmaster_header *masterp;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[MASTER_FIELDS];
- X
- X if (fread(buf, sizeof(net_long), MASTER_FIELDS, f) != MASTER_FIELDS)
- X return 0;
- X
- X net_to_host(buf, MASTER_FIELDS);
- X
- X masterp->last_scan = buf[0];
- X masterp->number_of_groups = buf[1];
- X masterp->next_group_write_offset = buf[2];
- X#else
- X
- X if (fread(masterp, sizeof(master_header), 1, f) != 1) return 0;
- X#endif
- X return 1;
- X}
- X
- X
- Xdb_write_master(f, masterp)
- XFILE *f;
- Xmaster_header *masterp;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[MASTER_FIELDS];
- X
- X buf[0] = masterp->last_scan;
- X buf[1] = masterp->number_of_groups;
- X buf[2] = masterp->next_group_write_offset;
- X
- X host_to_net(buf, MASTER_FIELDS);
- X if (fwrite(buf, sizeof(net_long), MASTER_FIELDS, f) != MASTER_FIELDS) return 0;
- X#else
- X
- X if (fwrite(masterp, sizeof(master_header), 1, f) != 1) return 0;
- X#endif
- X return 1;
- X}
- X
- Xdb_read_group(f, gh, n)
- XFILE *f;
- Xregister group_header *gh;
- Xgroup_number n;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[GROUP_FIELDS];
- X
- X if (n >= 0)
- X fseek(f, MASTER_FIELDS * sizeof(net_long) + GROUP_FIELDS * sizeof(net_long) * n, 0);
- X
- X if (fread(buf, sizeof(net_long), GROUP_FIELDS, f) != GROUP_FIELDS)
- X return 0;
- X
- X net_to_host(buf, GROUP_FIELDS);
- X
- X gh->first_l_article = buf[0];
- X gh->last_l_article = buf[1];
- X gh->index_write_offset = buf[2];
- X gh->data_write_offset = buf[3];
- X gh->group_name_length = buf[4];
- X gh->group_flag = buf[5];
- X#else
- X
- X if (n >= 0)
- X fseek(f, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * n), 0);
- X
- X if (fread(gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, f) != 1)
- X return 0;
- X
- X#endif
- X return 1;
- X}
- X
- X
- Xdb_write_group(f, gh, n)
- XFILE *f;
- Xregister group_header *gh;
- Xgroup_number n;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[GROUP_FIELDS];
- X
- X if (n >= 0)
- X fseek(f, MASTER_FIELDS * sizeof(net_long) + GROUP_FIELDS * sizeof(net_long) * n, 0);
- X
- X buf[0] = gh->first_l_article;
- X buf[1] = gh->last_l_article;
- X buf[2] = gh->index_write_offset;
- X buf[3] = gh->data_write_offset;
- X buf[4] = gh->group_name_length;
- X buf[5] = gh->group_flag;
- X
- X host_to_net(buf, GROUP_FIELDS);
- X if (fwrite(buf, sizeof(net_long), GROUP_FIELDS, f) != GROUP_FIELDS)
- X return 0;
- X
- X#else
- X if (n >= 0)
- X fseek(f, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * n), 0);
- X
- X if (fwrite(gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, f) != 1)
- X return 0;
- X#endif
- X
- X return 1;
- X}
- X
- X
- Xdb_read_art(f, dh, offset)
- XFILE *f;
- Xdata_header *dh;
- Xoff_t *offset;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[ARTICLE_FIELDS];
- X
- X if (fread(buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
- X return 0;
- X
- X net_to_host(buf, ARTICLE_FIELDS);
- X
- X dh->dh_number = buf[0];
- X dh->dh_date = buf[1];
- X dh->dh_hpos = buf[2];
- X dh->dh_lpos = buf[3];
- X dh->dh_fpos = buf[4];
- X dh->dh_lines = buf[5];
- X dh->dh_replies = buf[6];
- X dh->dh_cross_postings = buf[7];
- X dh->dh_subject_length = buf[8];
- X dh->dh_sender_length = buf[9];
- X
- X if (offset) *offset += ARTICLE_FIELDS * sizeof(net_long);
- X#else
- X
- X if (fread(dh, sizeof(data_header), 1, f) != 1) return 0;
- X if (offset) *offset += sizeof(data_header);
- X#endif
- X return 1;
- X}
- X
- Xdb_write_art(f, dh)
- XFILE *f;
- Xdata_header *dh;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long buf[ARTICLE_FIELDS];
- X
- X buf[0] = dh->dh_number;
- X buf[1] = dh->dh_date;
- X buf[2] = dh->dh_hpos;
- X buf[3] = dh->dh_lpos;
- X buf[4] = dh->dh_fpos;
- X buf[5] = dh->dh_lines;
- X buf[6] = dh->dh_replies;
- X buf[7] = dh->dh_cross_postings;
- X buf[8] = dh->dh_subject_length;
- X buf[9] = dh->dh_sender_length;
- X
- X host_to_net(buf, ARTICLE_FIELDS);
- X
- X if (fwrite(buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
- X return 0;
- X#else
- X
- X if (fwrite(dh, sizeof(data_header), 1, f) != 1) return 0;
- X
- X#endif
- X
- X return 1;
- X}
- X
- X
- X
- Xoff_t get_index_offset(gh, art_num)
- Xgroup_header *gh;
- Xarticle_number art_num;
- X{
- X#ifdef NETWORK_DATABASE
- X return (off_t)((art_num - gh->first_l_article) * sizeof(net_long));
- X#else
- X return (off_t)((art_num - gh->first_l_article) * sizeof(off_t));
- X#endif
- X}
- X
- Xoff_t get_data_offset(gh, art_num)
- Xgroup_header *gh;
- Xarticle_number art_num;
- X{
- X FILE *index;
- X off_t data_offset;
- X
- X if (gh->first_l_article == art_num) return (off_t)0;
- X
- X index = open_data_file(gh, 'x', OPEN_READ);
- X if (index == NULL) return (off_t)(-1);
- X
- X fseek(index, get_index_offset(gh, art_num), 0);
- X if (!db_read_offset(index, &data_offset))
- X return (off_t)(-1);
- X
- X fclose(index);
- X
- X return data_offset;
- X}
- X
- X
- Xdb_read_offset(f, offset)
- XFILE *f;
- Xoff_t *offset;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long temp;
- X
- X if (fread(&temp, sizeof(net_long), 1, f) != 1) return 0;
- X
- X#ifndef NETWORK_BYTE_ORDER
- X temp = ntohl(temp);
- X#endif
- X *offset = temp;
- X#else
- X
- X if (fread((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
- X#endif
- X return 1;
- X}
- X
- Xdb_write_offset(f, offset)
- XFILE *f;
- Xoff_t *offset;
- X{
- X#ifdef NETWORK_DATABASE
- X net_long temp;
- X
- X temp = *offset;
- X
- X#ifndef NETWORK_BYTE_ORDER
- X temp = htonl(temp);
- X#endif
- X if (fwrite(&temp, sizeof(net_long), 1, f) != 1) return 0;
- X
- X#else
- X
- X if (fwrite((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
- X#endif
- X return 1;
- X}
- X
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 db.c || echo "restore of db.c fails"
- set `wc -c db.c`;Sum=$1
- if test "$Sum" != "10668"
- then echo original size 10668, current size $Sum;fi
- echo "x - extracting db.h (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > db.h &&
- X/*
- X
- X * DATABASE ORGANIZATION:
- X *
- X * The central nn information is contained in following files:
- X * DB_DIRECTORY/MASTER
- X * DB_DIRECTORY/GROUPS
- X * DB_DIRECTORY/DATA/nnn.x
- X * DB_DIRECTORY/DATA/nnn.d
- X *
- X * The MASTER file consists of a header and one entry for each news
- X * group. The sequence of the group headers defines the group
- X * number associated with the group.
- X *
- X * The GROUPS file contains the names of the news groups; the names
- X * occur in the same sequence as in the MASTER file.
- X *
- X * For each news group, the DATA directory contains two files whose
- X * name is constructed from the group number 'nnn':
- X *
- X * nnn.x Index file
- X * nnn.d Data file
- X *
- X * The index file provides a a mapping from article numbers to offsets
- X * in the data file.
- X *
- X * The data file contains the actual header data. Each article is
- X * represented by a Header, an array of Cross Postings, and the
- X * strings representing the sender name and the article subject:
- X *
- X * header
- X * group_number 1 [ if cross posted ]
- X * group_number 2
- X * ...
- X * sender name (null terminated) [if sender_length > 0]
- X * subject (null terminated) [if subject_length > 0]
- X *
- X * For a digest, cross posted groups are only specified for first
- X * the entry.
- X *
- X * The format of the MASTER file is specifed in the data.h
- X * file. The format of the index and data files are specified below.
- X *
- X * Unless NETWORK_DATABASE is defined, the database will
- X * will contain machine dependent binary data.
- X */
- X
- Xtypedef struct {
- X off_t data_offset;
- X} index_entry;
- X
- Xtypedef struct {
- X article_number dh_number;
- X
- X time_stamp dh_date; /* encoded Date: filed (not a time_t value!!) */
- X
- X off_t dh_hpos; /* absolute offset for first byte of header */
- X off_t dh_lpos; /* absolute offset for last byte of article */
- X int16 dh_fpos; /* relative offset for first byte in article text */
- X
- X int16 dh_lines;
- X int8 dh_replies;
- X
- X int8 dh_cross_postings;
- X int8 dh_subject_length;
- X int8 dh_sender_length;
- X} data_header;
- X
- X/*
- X * The article_number is negative for digest article header and
- X * zero for following sub articles.
- X */
- X
- Xarticle_number current_digest_article;
- X
- X#define IS_DIGEST_HEADER(e1) \
- X ((e1).dh_number < 0 && (current_digest_article = -((e1).dh_number)))
- X#define IS_SUB_DIGEST(e1) \
- X (((e1).dh_number) == 0)
- X#define ARTICLE_NUMBER(e1) \
- X (((e1).dh_number <= 0) ? current_digest_article : ((e1).dh_number))
- X
- X
- X
- X#ifdef NETWORK_DATABASE
- Xtypedef int32 cross_post_number;
- X#else
- Xtypedef group_number cross_post_number;
- X#endif
- X
- X
- X/* open database files */
- X
- XFILE *open_groups(), *open_data_file();
- X
- X/* data access */
- X
- Xoff_t get_index_offset(), get_data_offset();
- X
- X
- X
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 db.h || echo "restore of db.h fails"
- set `wc -c db.h`;Sum=$1
- if test "$Sum" != "2670"
- then echo original size 2670, current size $Sum;fi
- echo "x - extracting debug.h (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > debug.h &&
- X/*
- X * Debug flags and defines
- X *
- X * Notice: no modules are conditioned by this file in the
- X * makefile. touch the source file to have a change
- X * in debugging setup to reflect the program behaviour
- X */
- X
- X
- X/* debugging */
- X
- X#define RC_TEST 1 /* rc file updates */
- X#define DG_TEST 2 /* digest decoding */
- X#define SEQ_TEST 4 /* sequence file decoding */
- X#define SEQ_DUMP 8 /* dump sequence after read */
- X
- Xextern int Debug;
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 debug.h || echo "restore of debug.h fails"
- set `wc -c debug.h`;Sum=$1
- if test "$Sum" != "427"
- then echo original size 427, current size $Sum;fi
- echo "x - extracting digest.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > digest.c &&
- X/*
- X * digest article handling
- X */
- X
- X#include "config.h"
- X#include "news.h"
- X#include "match.h"
- X#include "debug.h"
- X
- X#ifdef DG_TEST
- X
- X#define TEST(fmt, x, y) if (Debug & DG_TEST) printf(fmt, x, y)
- X
- X#else
- X
- X#define TEST(fmt, x, y)
- X
- X#endif
- X
- X
- X
- X/*
- X * test if global 'news' header is header of a digest.
- X * body points to a buffer (NUL term)
- X * containing the first part of the article.
- X */
- X
- Xstatic char match_digest[128] = {
- X
- X/* NUL SOH STX ETX EOT ENQ ACK BEL BS TAB NL VT FF CR SO SI */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- X
- X/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- X
- X/* SP ! " # $ % & ' ( ) * + , - . / */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- X
- X/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- X 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 00, 00, 00, 00, 00, 00,
- X
- X/* @ A B C D E F G H I J K L M N O */
- X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- X
- X/* P Q R S T U V W X Y Z [ \ ] ^ _ */
- X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00,
- X
- X/* ` a b c d e f g h i j k l m n o */
- X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- X
- X/* p q r s t u v w x y z { | } ~ DEL */
- X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
- X
- X};
- X
- Xstatic char digest_pattern[] = "digest";
- X
- Xinit_digest_parsing()
- X{
- X init_quick_match(digest_pattern);
- X}
- X
- X
- Xis_digest(body)
- Xregister char *body;
- X{
- X char *dpos, *quick_match();
- X register char *sp;
- X register int l;
- X
- X /* articles without a subject line are not digests (per definition) */
- X if (news.ng_subj == NULL) return 0;
- X
- X
- X if (dpos = quick_match(news.ng_subj, digest_pattern)) {
- X int lgt = dpos - news.ng_subj;
- X int maxl = 10;
- X
- X /* look for a line matching the subject */
- X while (*body && maxl) {
- X sp = news.ng_subj;
- X l = lgt;
- X if (*body == *sp && strncmp(body, sp, l) == 0)
- X goto ok;
- X while (*body && *body != NL) {
- X while (*sp && MATCH_DROP(match_digest, *sp)) {
- X if (--l == 0) goto ok;
- X ++sp;
- X }
- X
- X if (MATCH_DROP(match_digest, *body)) {
- X ++body;
- X continue;
- X }
- X
- X if (*sp && MATCH_EQ(match_digest, *body, *sp)) {
- X if (--l == 0) goto ok;
- X ++sp;
- X }
- X ++body;
- X }
- X
- X if (*body) ++body, --maxl;
- X }
- X }
- X return 0;
- X
- X ok:
- X TEST("is_digest: %s\n", news.ng_subj, 0);
- X return 1;
- X}
- X
- X
- X/*
- X * expect that f is positioned at header of an article
- X */
- X
- Xget_digest_article(f, hdrbuf)
- XFILE *f;
- Xnews_header_buffer hdrbuf;
- X{
- X int cont;
- X
- X digest.dg_hpos = ftell(f);
- X TEST("GET DIGEST hp=%ld\n", digest.dg_hpos, 0);
- X
- X do {
- X if (!parse_digest_header(f, 0, hdrbuf)) return -1;
- X digest.dg_fpos = ftell(f);
- X TEST("END HEADER hp=%ld fp=%ld\n", digest.dg_hpos, digest.dg_fpos);
- X } while ((cont = skip_digest_body(f)) < 0);
- X
- X TEST("END BODY lp=%ld next=%ld\n", digest.dg_lpos, ftell(f));
- X
- X return cont;
- X}
- X
- X#define BACKUP_LINES 50 /* remember class + offset for parsed lines */
- X
- X#define LN_BLANK 0x01 /* blank line */
- X#define LN_DASHED 0x02 /* dash line */
- X#define LN_HEADER 0x04 /* (possible) header line */
- X#define LN_ASTERISK 0x08 /* asterisk line (near end) */
- X#define LN_END_OF 0x10 /* End of ... line */
- X#define LN_TEXT 0x20 /* unclassified line */
- X
- X
- X/*
- X * skip until 'Subject: ' (or End of digest) line is found
- X * then backup till start of header
- X */
- X
- X/*
- X * Tuning parameters:
- X *
- X * MIN_HEADER_LINES: number of known header lines that must
- X * be found in a block to identify a new
- X * header
- X *
- X * MAX_BLANKS_DASH max no of blanks on a 'dash line'
- X *
- X * MIN_DASHES min no of dashes on a 'dash line'
- X *
- X * MAX_BLANKS_ASTERISKS max no of blanks on an 'asterisk line'
- X *
- X * MIN_ASTERISKS min no of asterisks on an 'asterisk line'
- X *
- X * MAX_BLANKS_END_OF max no of blanks before "End of "
- X */
- X
- X#define MIN_HEADER_LINES 2
- X#define MAX_BLANKS_DASH 3
- X#define MIN_DASHES 16
- X#define MAX_BLANKS_ASTERISK 1
- X#define MIN_ASTERISKS 10
- X#define MAX_BLANKS_END_OF 1
- X
- Xskip_digest_body(f)
- Xregister FILE *f;
- X{
- X off_t backup_p[BACKUP_LINES];
- X int line_type[BACKUP_LINES];
- X register int backup_index, backup_count;
- X int more_header_lines, end_or_asterisks, blanks;
- X char line[1024];
- X register char *cp;
- X char **dg_hdr_field();
- X
- X#define decrease_index() \
- X if (--backup_index < 0) backup_index = BACKUP_LINES - 1
- X
- X backup_index = -1;
- X backup_count = 0;
- X end_or_asterisks = 0;
- X
- X digest.dg_lines = 0;
- X
- X
- X next_line:
- X more_header_lines = 0;
- X
- X next_possible_header_line:
- X digest.dg_lines++;
- X
- X if (++backup_index == BACKUP_LINES) backup_index = 0;
- X if (backup_count < BACKUP_LINES) backup_count++;
- X
- X backup_p[backup_index] = ftell(f);
- X line_type[backup_index] = LN_TEXT;
- X
- X if (fgets(line, 1024, f) == NULL) {
- X TEST("end_of_file, bc=%d, lines=%d\n", backup_count, digest.dg_lines);
- X
- X /* end of file => look for "****" or "End of" line */
- X
- X if (end_or_asterisks)
- X while (--backup_count >= 0) {
- X --digest.dg_lines;
- X decrease_index();
- X if (line_type[backup_index] & (LN_ASTERISK | LN_END_OF)) break;
- X }
- X
- X if (digest.dg_lines == 0) return 0;
- X
- X while (--backup_count >= 0) {
- X --digest.dg_lines;
- X digest.dg_lpos = backup_p[backup_index];
- X decrease_index();
- X if ((line_type[backup_index] &
- X (LN_ASTERISK | LN_END_OF | LN_BLANK | LN_DASHED)) == 0)
- X break;
- X }
- X
- X return 0; /* no article follows */
- X }
- X
- X TEST("\n>>%-.50s ==>>", line, 0);
- X
- X for (cp = line; *cp && isascii(*cp) && isspace(*cp); cp++);
- X
- X if (*cp == NUL) {
- X TEST("BLANK", 0, 0);
- X line_type[backup_index] = LN_BLANK;
- X goto next_line;
- X }
- X
- X blanks = cp - line;
- X
- X if (*cp == '-') {
- X if (blanks > MAX_BLANKS_DASH) goto next_line;
- X
- X while (*cp == '-') cp++;
- X if (cp - line - blanks > MIN_DASHES) {
- X while (*cp && (*cp == '-' || (isascii(*cp) && isspace(*cp)))) cp++;
- X if (*cp == NUL) {
- X TEST("DASHED", 0, 0);
- X
- X line_type[backup_index] = LN_DASHED;
- X }
- X
- X }
- X goto next_line;
- X }
- X
- X if (*cp == '*') {
- X if (blanks > MAX_BLANKS_ASTERISK) goto next_line;
- X
- X while (*cp == '*') cp++;
- X if (cp - line - blanks > MIN_ASTERISKS) {
- X while (*cp && (*cp == '*' || (isascii(*cp) && isspace(*cp)))) cp++;
- X if (*cp == NUL) {
- X TEST("ASTERISK", 0, 0);
- X line_type[backup_index] = LN_ASTERISK;
- X end_or_asterisks++;
- X }
- X }
- X goto next_line;
- X }
- X
- X if (blanks <= MAX_BLANKS_END_OF &&
- X *cp == 'E' && strncmp(cp, "End of ", 7) == 0) {
- X TEST("END_OF_", 0, 0);
- X line_type[backup_index] = LN_END_OF;
- X end_or_asterisks++;
- X goto next_line;
- X }
- X
- X if (blanks == 0) {
- X if (dg_hdr_field(line, 0)) {
- X TEST("HEADER", 0, 0);
- X
- X line_type[backup_index] = LN_HEADER;
- X if (++more_header_lines < MIN_HEADER_LINES)
- X goto next_possible_header_line;
- X
- X /* found block with MIN_HEADER_LINES */
- X
- X /* search for beginning of header */
- X
- X TEST("\nSearch for start of header\n", 0, 0);
- X
- X for (;;) {
- X fseek(f, backup_p[backup_index], 0);
- X --digest.dg_lines;
- X if (--backup_count == 0) break;
- X decrease_index();
- X if ((line_type[backup_index] & (LN_HEADER | LN_TEXT)) == 0)
- X break;
- X }
- X
- X if (digest.dg_lines == 0) {
- X TEST("Skipped empty article\n", 0, 0);
- X return 0;
- X }
- X
- X for (;;) {
- X digest.dg_lpos = backup_p[backup_index];
- X if (--backup_count < 0) break;
- X decrease_index();
- X if ((line_type[backup_index] & (LN_BLANK | LN_DASHED)) == 0)
- X break;
- X --digest.dg_lines;
- X }
- X
- X return (digest.dg_lines == 0) ? -1 : 1;
- X }
- X goto next_possible_header_line;
- X }
- X
- X goto next_line;
- X}
- X
- X
- Xparse_digest_header(f, all, hdrbuf)
- XFILE *f;
- Xint all;
- Xnews_header_buffer hdrbuf;
- X{
- X extern char *parse_header(), **dg_hdr_field();
- X
- X digest.dg_date = digest.dg_from = digest.dg_subj = digest.dg_to = NULL;
- X
- X parse_header(f, dg_hdr_field, all, hdrbuf);
- X
- X return digest.dg_from || digest.dg_subj;
- X}
- X
- X
- Xstatic char **dg_hdr_field(lp, all)
- Xregister char *lp;
- Xint all;
- X{
- X
- X#define check(name, lgt, field) \
- X if (strncmp(name, lp, lgt) == 0) { \
- X TEST("MATCH: field ", 0, 0); \
- X return &digest.field; \
- X }
- X
- X
- X TEST("\nPARSE[%.20s] ==>> ", lp, 0);
- X
- X switch (*lp++) {
- X
- X case 'D':
- X case 'd':
- X check("ate: ", 5, dg_date);
- X break;
- X
- X case 'F':
- X case 'f':
- X check("rom: ", 5, dg_from);
- X break;
- X
- X case 'R':
- X case 'r':
- X if (!all) break;
- X check("e: ", 3, dg_subj);
- X break;
- X
- X case 'S':
- X case 's':
- X check("ubject", 6, dg_subj);
- X break;
- X
- X case 'T':
- X case 't':
- X check("itle: ", 6, dg_subj);
- X if (!all) break;
- X check("o: ", 3, dg_to);
- X break;
- X }
- X
- X#undef check
- X TEST("NOT MATCHED ", 0, 0);
- X
- X return NULL;
- X}
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 digest.c || echo "restore of digest.c fails"
- set `wc -c digest.c`;Sum=$1
- if test "$Sum" != "8891"
- then echo original size 8891, current size $Sum;fi
- echo "x - extracting execute.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > execute.c &&
- X#include <signal.h>
- X#include <errno.h>
- X#include "config.h"
- X#include "term.h"
- X
- Xchar *user_shell;
- X
- Xinit_execute()
- X{
- X if ((user_shell = getenv("SHELL")) == NULL)
- X user_shell = SHELL;
- X}
- X
- Xexecute(path, args)
- Xchar *path, **args;
- X{
- X int was_raw, pid, i, status;
- X sig_type (*quit)(), (*intr)(), (*cont)();
- X extern int errno;
- X
- X was_raw = no_raw();
- X
- X while ((pid = fork()) == -1) sleep(1);
- X
- X if (pid == 0) {
- X for (i = 3 ; i < 20 ; i++)
- X close(i);
- X
- X execv(path, args);
- X
- X fprintf(stderr, "%s: not found\n", path);
- X nn_exit(20);
- X }
- X quit = signal(SIGQUIT, SIG_IGN);
- X intr = signal(SIGINT, SIG_IGN);
- X#ifdef HAVE_JOBCONTROL
- X cont = signal(SIGCONT, SIG_DFL);
- X#endif
- X while ((i = wait(&status)) != pid && (i != -1 || errno == EINTR));
- X
- X signal(SIGQUIT, quit);
- X signal(SIGINT, intr);
- X#ifdef HAVE_JOBCONTROL
- X signal(SIGCONT, cont);
- X#endif
- X
- X if (was_raw) raw();
- X
- X return status != 0;
- X}
- X
- X
- Xshell_escape()
- X{
- X static char command[FILENAME] = "";
- X char *cmd;
- X int first = 1;
- X
- X prompt("!");
- X
- Xagain:
- X
- X cmd = get_s(command, NONE, NONE, NO_COMPLETION);
- X if (cmd == NULL) return !first;
- X
- X strcpy(command, cmd);
- X
- X if (!run_shell(command, first)) return !first;
- X first = 0;
- X
- X if (any_key(0) == '!') { /* should use key map here */
- X putchar(CR);
- X putchar('!');
- X clrline();
- X goto again;
- X }
- X
- X return 1;
- X}
- X
- X
- Xstatic char *exec_sh_args[] = {
- X "nnsh",
- X "-c",
- X (char *)NULL, /* cmdstring */
- X (char *)NULL
- X};
- X
- Xrun_shell(command, clear)
- Xchar *command;
- Xint clear;
- X{
- X char cmdstring[512];
- X
- X if (!expand_file_name(cmdstring, command))
- X return 0;
- X
- X if (clear) {
- X clrdisp();
- X fl;
- X } else {
- X putchar(CR);
- X putchar(NL);
- X }
- X
- X exec_sh_args[2] = cmdstring;
- X
- X execute(user_shell, exec_sh_args);
- X return 1;
- X}
- X
- X#ifndef HAVE_JOBCONTROL
- Xstatic char *exec_suspend_args[] = {
- X "nnsh",
- X "-i",
- X (char *)NULL
- X};
- X#endif
- X
- Xsuspend_nn()
- X{
- X int was_raw;
- X
- X was_raw = no_raw();
- X gotoxy(0, Lines-1);
- X clrline();
- X
- X#ifdef HAVE_JOBCONTROL
- X kill(process_id, SIGTSTP);
- X#else
- X execute(user_shell, exec_suspend_args);
- X#endif
- X
- X s_redraw++;
- X if (was_raw) raw();
- X
- X return 1;
- X}
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 execute.c || echo "restore of execute.c fails"
- set `wc -c execute.c`;Sum=$1
- if test "$Sum" != "2254"
- then echo original size 2254, current size $Sum;fi
- echo "x - extracting expire.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > expire.c &&
- X/*
- X * Expire will remove all entries in the index and data files
- X * corresponding to the articles before the first article registered
- X * in the active file. No attempt is made to eliminate other
- X * expired articles.
- X */
- X
- X#include "config.h"
- X#include "db.h"
- X
- Ximport int trace;
- X
- X#define expire_error(msg) { \
- X err_message = msg; \
- X goto error_handler; \
- X}
- X
- Xexpire_group(gh)
- Xregister group_header *gh;
- X{
- X FILE *old_x, *old_d;
- X FILE *new;
- X off_t index_offset, data_offset, new_offset;
- X long count, expire_count;
- X char *err_message;
- X
- X old_x = old_d = new = NULL;
- X
- X
- X if (trace)
- X log_entry('T', "Exp %s (%d -> %d)",
- X gh->group_name, gh->first_l_article, gh->first_article);
- X
- X /*
- X * check whether new first article is collected
- X */
- X
- X if (!art_collected(gh, gh->first_article)) {
- X expire_count = gh->first_l_article - gh->last_l_article + 1;
- X err_message = NULL;
- X goto error_handler; /* renumbering, collect from start */
- X }
- X
- X expire_count = gh->first_article - gh->first_l_article;
- X
- X new = NULL;
- X
- X /*
- X * Open old files, unlink after open
- X */
- X
- X old_x = open_data_file(gh, 'x', OPEN_READ|OPEN_UNLINK);
- X old_d = open_data_file(gh, 'd', OPEN_READ|OPEN_UNLINK);
- X
- X if (old_x == NULL || old_d == NULL)
- X expire_error("INDEX or DATA file missing");
- X
- X /*
- X * Create new index file; copy from old
- X */
- X
- X new = open_data_file(gh, 'x', OPEN_CREATE);
- X if (new == NULL)
- X expire_error("INDEX: cannot create");
- X
- X /*
- X * index_offset is the offset into the old index file for the
- X * first entry in the new index file
- X */
- X
- X index_offset = get_index_offset(gh, gh->first_article);
- X
- X /*
- X * adjust the group's index write offset (the next free entry)
- X */
- X
- X gh->index_write_offset -= index_offset;
- X
- X /*
- X * calculate the number of entries to copy
- X */
- X
- X count = gh->index_write_offset / sizeof(off_t);
- X
- X /*
- X * data offset is the offset into the old data file for the
- X * first byte in the new data file; it is initialized in the
- X * loop below, by reading the entry in the old index file at
- X * offset 'index_offset'.
- X */
- X
- X data_offset = (off_t)0;
- X
- X /*
- X * read 'count' entries from the old index file starting from
- X * index_offset, subtract the 'data_offset', and output the
- X * new offset to the new index file.
- X */
- X
- X fseek(old_x, index_offset, 0);
- X
- X while (--count >= 0) {
- X if (!db_read_offset(old_x, &new_offset))
- X expire_error("INDEX: too short");
- X
- X if (data_offset == (off_t)0) data_offset = new_offset;
- X
- X new_offset -= data_offset;
- X if (!db_write_offset(new, &new_offset))
- X expire_error("NEW INDEX: cannot write");
- X }
- X
- X fclose(new);
- X fclose(old_x); old_x = NULL;
- X
- X /*
- X * copy from old data file to new data file
- X */
- X
- X new = open_data_file(gh, 'd', OPEN_CREATE);
- X if (new == NULL)
- X expire_error("DATA: cannot create");
- X
- X /*
- X * calculate offset for next free entry in the new data file
- X */
- X
- X gh->data_write_offset -= data_offset;
- X
- X /*
- X * calculate number of bytes to copy (piece of cake)
- X */
- X
- X count = gh->data_write_offset;
- X
- X /*
- X * copy 'count' bytes from the old data file, starting at offset
- X * 'data_offset', to the new data file
- X */
- X
- X fseek(old_d, data_offset, 0);
- X while (count > 0) {
- X char block[1024];
- X int count1;
- X
- X count1 = fread(block, sizeof(char), 1024, old_d);
- X if (count1 <= 0)
- X expire_error("DATA: read error");
- X
- X if (fwrite(block, sizeof(char), count1, new) != count1)
- X expire_error("DATA: write error");
- X
- X count -= count1;
- X }
- X
- X fclose(new);
- X fclose(old_d);
- X
- X /*
- X * Update group entry
- X */
- X
- X gh->first_l_article = gh->first_article;
- X
- X /*
- X * Return number of expired articles
- X */
- X
- X return expire_count;
- X
- X
- X
- X /*
- X * Errors end up here.
- X * We simply recollect the whole group once more.
- X */
- X
- Xerror_handler:
- X
- X if (new) fclose(new);
- X if (old_x) fclose(old_x);
- X if (old_d) fclose(old_d);
- X
- X if (err_message)
- X log_entry('E', "Expire Error (%s): %s", gh->group_name, err_message);
- X
- X clean_group(gh);
- X
- X /* will be saved & unblocked later */
- X
- X /*
- X * We cannot say whether any articles actually had to be expired,
- X * but then we must guess...
- X */
- X
- X return expire_count;
- X}
- NO_NEWS_IS_GOOD_NEWS
- chmod 0644 expire.c || echo "restore of expire.c fails"
- set `wc -c expire.c`;Sum=$1
- if test "$Sum" != "4363"
- then echo original size 4363, current size $Sum;fi
- echo "x - extracting folder.c (Text)"
- sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > folder.c &&
- X/*
- X * folder handling
- X */
- X
- X#include <errno.h>
- X#include "config.h"
- X#include "articles.h"
- X#include "news.h"
- X#include "term.h"
- X#include "menu.h"
- X
- X
- Xexport int dont_sort_folders = 0;
- Xexport char *folder_directory = NULL;
- X
- X/*
- X * file name completion and expansion
- X */
- X
- X
- Xexpand_file_name(dest, src)
- Xchar *dest, *src;
- X{
- X register char *cp, *dp, c;
- X int parse, remap;
- X char *cur_grp, *cur_art;
- X
- X cur_grp = current_group ? current_group->group_name : NULL;
- X cur_art = (group_file_name && *group_file_name) ? group_path_name : NULL;
- X
- X for (dp = dest, parse = 1; c = *src; src++) {
- X
- X if (parse) {
- X
- X if (c == '+') {
- X if (folder_directory == NULL) {
- X if (!(cp = getenv("FOLDER")))
- X cp = FOLDER_DIRECTORY;
- X folder_directory = home_relative(cp);
- X }
- X
- X cp = folder_directory;
- X goto cp_str;
- X }
- X
- X if (c == '~') {
- X if (src[1] != '/') {
- X msg("Can't handle ~user expansion (yet)");
- X return 0;
- X }
- X
- X cp = home_directory;
- X
- X cp_str:
- X while (*cp) *dp++ = *cp++;
- X if (dp[-1] != '/') *dp++ = '/';
- X goto no_parse;
- X }
- X
- X if (cur_art && c == '%' && (src[1] == ' ' || src[1] == NUL)) {
- X cp = cur_art;
- X while (*cp) *dp++ = *cp++;
- X goto no_parse;
- X }
- X
- X }
- X
- X if (c == '$' && !isalnum(src[2])) {
- X remap = 0;
- X cp = NULL;
- X
- X switch (src[1]) {
- X case 'A':
- X cp = cur_art;
- X break;
- X case 'F':
- X cp = cur_grp;
- X remap = 1;
- X break;
- X case 'G':
- X cp = cur_grp;
- X break;
- X case 'L':
- X if (cp = strrchr(cur_grp, '.'))
- X cp++;
- X else
- X cp = cur_grp;
- X break;
- X case 'N':
- X if (cur_art) cp = group_file_name;
- X break;
- X default:
- X goto copy;
- X }
- X src++;
- X
- X if (!cp) {
- X msg("$%c not defined on this level", c);
- X return 0;
- X }
- X
- X while (*cp)
- X if (remap && *cp == '.')
- X cp++, *dp++ = '/';
- X else
- X *dp++ = *cp++;
- X goto no_parse;
- X }
- X
- X if (c == '/')
- X if (dp != dest && dp[-1] == '/') goto no_parse;
- X
- X copy:
- X *dp++ = c;
- X parse = isspace(c);
- X continue;
- X
- X no_parse:
- X parse = 0;
- X }
- X
- X *dp = NUL;
- X
- X return 1;
- X}
- X
- X
- Xfile_completion(path, index)
- Xchar *path;
- Xint index;
- X{
- X static dir_in_use = 0;
- X static char *head, *tail = NULL;
- X static int tail_offset;
- X
- X char nbuf[FILENAME], buffer[FILENAME];
- X char *dir, *base;
- X
- X if (path) {
- X if (dir_in_use) {
- X close_directory();
- X dir_in_use = 0;
- X }
- X
- X if (index < 0) return 0;
- X
- X head = path;
- X tail = path + index;
- X }
- X
- X if (!dir_in_use) {
- X path = head;
- X *tail = NUL;
- NO_NEWS_IS_GOOD_NEWS
- echo "End of part 3"
- echo "File folder.c is continued in part 4"
- echo "4" > s2_seq_.tmp
- exit 0
- ---
- Kim F. Storm storm@texas.dk Tel +45 429 174 00
- Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
- No news is good news, but nn is better!
-
-