rsalz@uunet.uu.net (Rich Salz) (06/23/89)
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! -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.