v19i064: NN, a Usenet news reader, Part03/15
Rich Salz
rsalz at uunet.uu.net
Sat Jun 24 02:24:06 AEST 1989
Submitted-by: mcvax!tidk!storm at 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 (... at ...) 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 at texas.dk Tel +45 429 174 00
Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
No news is good news, but nn is better!
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list