v19i065: NN, a Usenet news reader, Part04/15
Rich Salz
rsalz at uunet.uu.net
Sat Jun 24 02:24:11 AEST 1989
Submitted-by: mcvax!tidk!storm at uunet.UU.NET (Kim F. Storm)
Posting-number: Volume 19, Issue 65
Archive-name: nn/part04
#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file folder.c continued
#
CurArch=4
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 folder.c"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> folder.c
X
X if (*path == '|') return -1; /* no completion for pipes */
X
X if (*path == '+' || *path == '~') {
X if (!expand_file_name(nbuf, path))
X return 0; /* no completions */
X } else
X strcpy(nbuf, path);
X
X if (base = strrchr(nbuf, '/')) {
X if (base == nbuf) {
X dir = "/";
X base++;
X } else {
X *base++ = NUL;
X dir = nbuf;
X }
X } else {
X base = nbuf;
X dir = ".";
X }
X
X tail_offset = strlen(base);
X
X dir_in_use = list_directory(dir, base);
X
X return dir_in_use;
X }
X
X if (index)
X return help_directory();
X
X if (!next_directory(buffer)) return 0;
X
X strcpy(tail, buffer+tail_offset);
X
X return 1;
X}
X
X
X/*
X * read file names in directory 'dir' starting with 'prefix'
X *
X * this could be speeded up by an order of magnitude by
X * reading the directory directly into an array and sort
X * it internally.
X *
X */
X
Xstatic char dir_path[FILENAME], *dir_tail;
X
X#ifndef HAVE_DIRECTORY
X
Xstatic FILE *dirf;
Xstatic int prefix_lgt;
X
Xstatic list_directory(dir, prefix)
Xchar *dir, *prefix;
X{
X sprintf(dir_path, "cd %s && echo %s* 2>/dev/null", dir, prefix);
X prefix_lgt = strlen(prefix);
X
X if ((dirf = popen(dir_path, "r")) == NULL) return 0;
X
X dir_tail = dir_path;
X while (*dir_tail++ = *dir++);
X dir_tail[-1] = '/';
X
X return 1;
X}
X
Xstatic next_directory(buffer)
Xchar *buffer;
X{
X register char *cp;
X register int c;
X
X cp = buffer;
X while ((c = getc(dirf)) != EOF && (c != SP) && (c != NL))
X *cp++ = c;
X
X if (cp != buffer) {
X *cp = NUL;
X if (strcmp(buffer + prefix_lgt, "*")) {
X
X strcpy(dir_tail, buffer);
X if (file_exist(dir_path, "d")) {
X *cp++ = '/';
X *cp = NUL;
X }
X
X return 1;
X }
X
X }
X
X close_directory();
X return 0;
X}
X
Xhelp_directory()
X{
X return 0;
X}
X
Xstatic close_directory()
X{
X if (dirf) {
X pclose(dirf);
X dirf = NULL;
X }
X}
X#else /* HAVE_DIRECTORY */
X
Xstatic string_marker str_mark;
Xstatic char **completions = NULL;
Xstatic char **comp_iterator;
Xstatic char **comp_help;
X
X/*
X * list_directory scans the directory twice; first time to find out how
X * many matches there are, and second time to save the names, after
X * sufficient memory have been allocated to store it all.
X */
X
Xstatic sort_directory(f1, f2) /* Used by qsort */
X register char **f1;
X register char **f2;
X{
X return strcmp(*f1, *f2);
X}
X
Xstatic list_directory(dir, prefix)
Xchar *dir, *prefix;
X{
X DIR *dirp;
X register Direntry *dp;
X register char *cp;
X register char **comp;
X int pflen = strlen(prefix);
X unsigned nmatch = 1; /* No. of completions plus one */
X
X if ((dirp = opendir(dir)) == NULL)
X return 0; /* tough luck */
X
X while ((dp = readdir(dirp)) != NULL) {
X cp = dp->d_name;
X if (*cp == '.' && (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
X continue;
X if (strncmp(prefix, cp, pflen) == 0)
X nmatch++;
X }
X if (nmatch == 1
X || (completions = (char **)calloc(nmatch, sizeof(char *))) == NULL) {
X closedir(dirp);
X return 0;
X }
X mark_str(&str_mark);
X
X rewinddir(dirp);
X for (comp = completions; (dp = readdir(dirp)) != NULL; ) {
X cp = dp->d_name;
X if (*cp == '.' && (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
X continue;
X if (strncmp(prefix, cp, pflen) == 0)
X strcpy(*comp++ = alloc_str(strlen(cp)), cp);
X }
X closedir(dirp);
X *comp = (char *)0;
X qsort((char *)completions, comp - completions, sizeof(char *), sort_directory);
X comp_iterator = completions;
X comp_help = completions;
X
X dir_tail = dir_path;
X while (*dir_tail++ = *dir++);
X dir_tail[-1] = '/';
X
X return 1;
X}
X
Xstatic next_directory(buffer)
X register char *buffer;
X{
X if (*comp_iterator != NULL) {
X strcpy(dir_tail, *comp_iterator);
X strcpy(buffer, *comp_iterator++);
X
X if ( file_exist(dir_path, "d") )
X strcat(buffer, "/");
X return 1;
X }
X close_directory();
X return 0;
X}
X
Xhelp_directory()
X{
X list_completion((char *)NULL);
X
X if (*comp_help == NULL) comp_help = completions;
X while (*comp_help && list_completion(*comp_help))
X comp_help++;
X
X fl;
X return 1;
X}
X
Xstatic close_directory()
X{
X if (completions) {
X release_str(&str_mark);
X free((char *)completions);
X completions = NULL;
X }
X}
X#endif /* HAVE_DIRECTORY */
X
Xstatic int cancel_count;
X
Xfcancel(ah)
Xarticle_header *ah;
X{
X if (ah->flag & A_CANCEL) {
X cancel_count--;
X ah->flag &= ~A_CANCEL;
X } else {
X cancel_count++;
X ah->flag |= A_CANCEL;
X }
X}
X
Xstatic folder_header()
X{
X so_printxy(0, 0, "Folder: %s", current_group->group_name);
X
X return 1; /* number of header lines */
X}
X
Xfolder_menu(path)
Xchar *path;
X{
X FILE *folder;
X register article_header *ap;
X news_header_buffer dgbuf;
X char buffer[256];
X int more, length, re, menu_cmd, was_raw;
X memory_marker mem_marker;
X group_header fake_group;
X int cc_save;
X
X fake_group.group_name = path;
X fake_group.group_flag = G_RC_UPDATED | G_FOLDER | G_READ;
X init_group(&fake_group);
X
X folder = open_file(group_path_name, OPEN_READ);
X if (folder == NULL) {
X msg("%s not found", path);
X return ME_NO_REDRAW;
X }
X
X was_raw = no_raw();
X s_keyboard = 0;
X
X printf("\rReading: %-.65s", path);
X clrline();
X
X current_group = &fake_group;
X
X mark_memory(&mem_marker);
X
X ap = alloc_art();
X
X more = 1;
X while (more && (more = get_digest_article(folder, dgbuf)) >= 0) {
X if (s_keyboard) break;
X
X ap->a_number = 0;
X ap->flag = A_FOLDER;
X
X ap->lines = digest.dg_lines;
X
X ap->hpos = digest.dg_hpos;
X ap->fpos = digest.dg_fpos;
X ap->lpos = digest.dg_lpos;
X
X if (digest.dg_from) {
X length = pack_name(buffer, digest.dg_from, NAME_LENGTH);
X ap->sender = alloc_str(length);
X strcpy(ap->sender, buffer);
X } else
X ap->sender = "";
X
X if (digest.dg_subj) {
X length = pack_subject(buffer, digest.dg_subj, &re, 255);
X ap->replies = re;
X ap->subject = alloc_str(length);
X strcpy(ap->subject, buffer);
X } else {
X ap->replies = 0;
X ap->subject = "";
X }
X
X add_article(ap);
X ap = alloc_art();
X }
X
X fclose(folder);
X
X if (was_raw) raw();
X
X if (s_keyboard) {
X menu_cmd = ME_NO_REDRAW;
X } else
X if (n_articles == 0) {
X msg("Not a folder (no article header)");
X menu_cmd = ME_NO_REDRAW;
X } else {
X strcpy(buffer, path);
X fake_group.group_name = buffer; /* save for later use */
X
X if (n_articles > 1) {
X clrdisp();
X prompt_line = 2;
X if (!dont_sort_folders) sort_articles();
X }
X
X cc_save = cancel_count;
X cancel_count = 0;
X
X reenter_menu:
X menu_cmd = menu(folder_header);
X
X if (cancel_count) {
X clrdisp();
X printf("Folder: %s\nFile: %s\n\n", buffer, group_path_name);
X printf("Remove %d article%s from folder? ",
X cancel_count, cancel_count == 1 ? "" : "s");
X fl;
X
X switch (yes(1)) {
X case 1:
X printf("\n\n");
X rewrite_folder();
X break;
X case 0:
X break;
X default:
X goto reenter_menu;
X }
X }
X cancel_count = cc_save;
X }
X
X release_memory(&mem_marker);
X
X return menu_cmd;
X}
X
X
Xrewrite_folder()
X{
X register FILE *src, *dst;
X char oldfile[FILENAME], *sp;
X register int c;
X register long cnt;
X register article_header *ah, **ahp;
X register int n;
X
X if ((src = fopen(group_path_name, "r")) == NULL) {
X msg("Cannot open %s", group_path_name);
X return;
X }
X
X strcpy(oldfile, group_path_name);
X sp = strrchr(oldfile, '/');
X if (!sp) goto move_error;
X strcpy(sp+1, "~OLD~FOLDER~");
X
X unlink(oldfile);
X if (link(group_path_name, oldfile) < 0) goto move_error;
X if (unlink(group_path_name) < 0) {
X if (unlink(oldfile) == 0) goto move_error;
X printf("\n\n%s was linked to %s --- cannot proceed\n",
X group_path_name, oldfile);
X sleep(5);
X return;
X }
X
X if ((dst = fopen(group_path_name, "w")) == NULL) {
X fclose(src);
X goto move_back;
X }
X
X unsort_articles(1);
X
X printf("Compressing folder..."); fl;
X
X for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
X ah = *ahp;
X if (ah->flag & A_CANCEL) continue;
X fseek(src, ah->hpos, 0);
X cnt = ah->lpos - ah->hpos;
X while (--cnt >= 0) {
X if ((c = getc(src)) == EOF) break;
X putc(c, dst);
X }
X putc(NL, dst);
X }
X fclose(src);
X if (ferror(dst)) {
X fclose(dst);
X goto move_back;
X }
X return;
X
Xmove_back:
X if (link(oldfile, group_path_name) == 0) {
X unlink(oldfile);
X printf("Cannot create new file -- Folder restored\n");
X sleep(2);
X } else {
X printf("Cannot create new file\n\nFolder saved in %s\n",
X oldfile);
X sleep(10);
X }
X return;
X
Xmove_error:
X fclose(src);
X printf("\n\nCannot move folder %s to %s\n",
X group_path_name, oldfile);
X sleep(3);
X return;
X}
NO_NEWS_IS_GOOD_NEWS
echo "File folder.c is complete"
chmod 0644 folder.c || echo "restore of folder.c fails"
set `wc -c folder.c`;Sum=$1
if test "$Sum" != "11228"
then echo original size 11228, current size $Sum;fi
echo "x - extracting global.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > global.c &&
X#include <signal.h>
X#include <errno.h>
X#include <pwd.h>
X#include "config.h"
X
Xexport char *home_directory;
Xexport char *nn_directory;
Xexport char news_directory[] = NEWS_DIRECTORY; /* /usr/spool/news */
Xexport char lib_directory[] = LIB_DIRECTORY; /* /usr/local/lib/nn */
Xexport char db_directory[] = DB_DIRECTORY; /* /usr/spool/nn */
X
Xexport char *temp_file;
X
Xexport char *pager = PAGER; /* pg or more */
X
Xexport int is_master;
X
X/* signal handler interface */
X
Xexport int s_hangup = 0; /* hangup signal */
Xexport int s_keyboard = 0; /* keyboard interrupt */
Xexport int s_pipe = 0; /* broken pipe */
Xexport int s_redraw = 0; /* redraw signal (if job control) */
X
Xstatic sig_type catch_hangup(n)
X{
X signal(n, SIG_IGN);
X
X s_hangup++;
X}
X
Xstatic sig_type catch_keyboard(n)
X{
X s_keyboard++;
X
X#ifdef RESET_SIGNAL_WHEN_CAUGHT
X signal(n, catch_keyboard);
X#endif
X}
X
Xstatic sig_type catch_pipe(n)
X{
X s_pipe++;
X
X#ifdef RESET_SIGNAL_WHEN_CAUGHT
X signal(n, catch_pipe);
X#endif
X}
X
X#ifdef HAVE_JOBCONTROL
Xstatic sig_type catch_redraw(n)
X{
X s_redraw++;
X
X#ifdef RESET_SIGNAL_WHEN_CAUGHT
X signal(n, catch_redraw);
X#endif
X}
X#endif
X
X
Xinit_global(who)
Xint who;
X{
X char *env;
X unsigned short getuid(), getgid();
X int getpid();
X
X is_master = (who == 1);
X
X signal(SIGTERM, catch_hangup);
X signal(SIGHUP, catch_hangup);
X signal(SIGPIPE, catch_pipe);
X signal(SIGALRM, SIG_IGN);
X
X#ifdef SIGPWR
X signal(SIGPWR, catch_hangup);
X#endif
X
X user_id = getuid();
X group_id = getgid();
X process_id = getpid();
X
X if (is_master) {
X signal(SIGINT, catch_hangup);
X signal(SIGQUIT, catch_hangup);
X return;
X }
X
X signal(SIGINT, catch_keyboard);
X signal(SIGQUIT, catch_keyboard);
X#ifdef HAVE_JOBCONTROL
X signal(SIGCONT, catch_redraw);
X#endif
X
X if ((home_directory = getenv("HOME")) == NULL)
X user_error("No HOME environment variable");
X
X nn_directory = mk_file_name(home_directory, ".nn");
X
X if (!file_exist(nn_directory, "drwx"))
X mkdir(nn_directory, 0755); /* should check here */
X
X if ((env = getenv("TMPDIR")) == NULL) env = TMP_DIRECTORY;
X temp_file = mk_file_name(env, "nn.XXXXXX"); /* dies in ANSI C! */
X mktemp(temp_file);
X}
X
X/*
X * this is for admin K & W commands and for master -w
X */
X
Xkill_master(sig)
Xint sig;
X{
X FILE *m_pid;
X int pid, ok;
X char buf[10];
X
X m_pid = open_file(relative(lib_directory, "MPID"), OPEN_READ);
X if (m_pid == NULL) {
X errno = ESRCH;
X return 0;
X }
X
X ok = 0; /* not yet */
X
X if (fgets(buf, 10, m_pid) == NULL)
X printf("MPID file is empty\n");
X else {
X pid = atoi(buf);
X if (pid <= 2)
X printf("MPID file contains illegal process id: %d\n", pid);
X else
X if (kill(pid, sig) != -1) ok++;
X }
X
X fclose(m_pid);
X
X return ok;
X}
X
X
Xmem_check(addr, size, msg)
Xchar *addr, *msg;
Xint size;
X{
X if (addr == NULL) {
X if (is_master)
X sys_error("Cannot allocate %d %s", size, msg);
X else
X user_error("Cannot allocate %d %s", size, msg);
X }
X}
X
XFILE *open_file(name, mode)
Xchar *name;
Xint mode;
X{
X FILE *f;
X int fd;
X
X if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL))
X return NULL;
X
X switch (mode & 0x0f) {
X
X case OPEN_READ:
X
X f = fopen(name, "r");
X break;
X
X case OPEN_UPDATE:
X
X/* f = fopen(name, "r+"); -- not reliable on many systems (sigh) */
X
X if ((fd = open(name, O_WRONLY)) >= 0) {
X if ((f = fdopen(fd, "w")) != NULL) return f;
X close(fd);
X }
X
X /* fall thru */
X
X case OPEN_CREATE:
X
X f = fopen(name, "w");
X break;
X
X case OPEN_APPEND:
X
X f = fopen(name, "a");
X break;
X
X default:
X
X sys_error("Illegal mode: open_file(%s, %d)", name, mode);
X }
X
X if (f) {
X if (mode & OPEN_UNLINK) unlink(name);
X return f;
X }
X
X if ((mode & MUST_EXIST) == 0) return NULL;
X
X if (is_master)
X sys_error("Cannot open file %s, mode: %d", name, mode);
X else {
X log_entry('R', "Client cannot open file %s, mode: %d", name, mode);
X user_error("Cannot open file %s", name);
X }
X
X return NULL;
X}
X
X
X
X
X/*
X * relative -- concat directory name and file name
X */
X
Xchar *relative(dir, name)
Xchar *dir, *name;
X{
X static char concat_path[FILENAME];
X
X sprintf(concat_path, "%s/%s", dir, name);
X return concat_path;
X}
X
X
Xchar *mk_file_name(dir, name)
Xchar *dir, *name;
X{
X char *buf;
X
X buf = malloc((unsigned)(strlen(dir) + strlen(name) + 2));
X mem_check(buf, 1, "file name");
X sprintf(buf, "%s/%s", dir, name);
X
X return buf;
X}
X
X
Xchar *home_relative(dir)
Xchar *dir;
X{
X if (dir) {
X if (*dir == '/')
X return copy_str(dir);
X else {
X if (*dir == '~' && *++dir == '/') dir++;
X return mk_file_name(home_directory, dir);
X }
X }
X return NULL;
X}
X
X
Xchar *copy_str(str)
Xchar *str;
X{
X char *new;
X
X new = malloc((unsigned)(strlen(str) + 1));
X mem_check(new, 1, "string");
X if (new) strcpy(new, str);
X
X return new;
X}
X
Xtime_t m_time(f)
XFILE *f;
X{
X struct stat st;
X
X if (fstat(fileno(f), &st) < 0) return 0;
X return st.st_mtime;
X}
X
X
Xtime_t file_exist(name, mode)
Xchar *name;
Xchar *mode;
X{
X struct stat statb;
X extern int errno;
X
X if (stat(name, &statb)) return 0;
X
X if (mode == NULL) return statb.st_mtime;
X
X while (*mode) {
X switch (*mode++) {
X case 'd':
X if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
X errno = ENOTDIR;
X return 0;
X case 'f':
X if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
X if ((statb.st_mode & S_IFMT) == 0000000) continue;
X if ((statb.st_mode & S_IFMT) == S_IFDIR) {
X errno = EISDIR;
X return 0;
X }
X break;
X case 'r':
X if ((statb.st_mode & 0400) && statb.st_uid == user_id) continue;
X if ((statb.st_mode & 0040) && statb.st_gid == group_id) continue;
X if ((statb.st_mode & 0004)) continue;
X break;
X case 'w':
X if ((statb.st_mode & 0200) && statb.st_uid == user_id) continue;
X if ((statb.st_mode & 0020) && statb.st_gid == group_id) continue;
X if ((statb.st_mode & 0002)) continue;
X break;
X case 'x':
X if ((statb.st_mode & 0100) && statb.st_uid == user_id) continue;
X if ((statb.st_mode & 0010) && statb.st_gid == group_id) continue;
X if ((statb.st_mode & 0001)) continue;
X break;
X }
X errno = EACCES;
X return 0;
X }
X
X /* all modes are ok */
X return statb.st_mtime;
X}
X
X
X
Xprint_version(fmt)
Xchar *fmt;
X{
X extern int Update_Level, Patch_Level;
X int param;
X
X for (; *fmt; fmt++) {
X
X if (*fmt == '%') {
X switch (*++fmt) {
X case 'R':
X param = RELEASE;
X break;
X case 'V':
X param = VERSION;
X break;
X case 'P':
X param = Patch_Level;
X break;
X case 'U':
X param = Update_Level;
X break;
X default:
X continue;
X }
X printf("%d", param);
X continue;
X }
X putchar(*fmt);
X }
X
X fl;
X}
X
X/*VARARGS*/
Xlog_entry(va_alist)
Xva_dcl
X{
X int type;
X va_list ap;
X
X va_start(ap);
X type = va_arg1(int);
X enter_log(type, va_args2toN);
X va_end(ap);
X}
X
X#ifdef HAVE_SYSLOG
X#include <syslog.h>
X#endif /* HAVE_SYSLOG */
X
X/*VARARGS*/
Xsys_error(va_alist)
Xva_dcl
X{
X va_list ap;
X
X va_start(ap);
X enter_log('E', va_args1toN);
X va_end(ap);
X
X if (is_master) {
X#ifndef HAVE_SYSLOG
X FILE *f;
X
X f = open_file("/dev/console", OPEN_CREATE);
X if (f == NULL) nn_exit(8);
X fprintf(f, "\n\rNNMASTER FATAL ERROR\n\r");
X fclose(f);
X#else /* HAVE_SYSLOG */
X char buf[512];
X char *fmt;
X
X va_start(ap);
X fmt = va_arg1(char *);
X vsprintf(buf, fmt, va_args2toN);
X va_end(ap);
X
X openlog("nnmaster", LOG_CONS, LOG_DAEMON);
X syslog(LOG_ALERT, "%s", buf);
X closelog();
X#endif /* HAVE_SYSLOG */
X }
X nn_exit(7);
X}
X
X
Xstatic enter_log(type, va_tail)
Xchar type;
Xva_tdcl
X{
X FILE *log;
X char *msg, buf[512], logname[512];
X
X msg = va_arg1(char *);
X vsprintf(buf, msg, va_args2toN);
X
X /* cannot use relative: one of the args may be generated by it */
X sprintf(logname, "%s/Log", lib_directory);
X
X log = open_file(logname, OPEN_APPEND);
X if (log == NULL) return 0;
X
X fprintf(log, "%c: %s (%s): %s\n", type,
X date_time((time_t)0), user_name(), buf);
X
X fclose(log);
X return 1;
X}
X
X
Xchar *user_name()
X{
X static char *user = NULL;
X struct passwd *pw, *getpwuid();
X
X if (is_master) return "M";
X
X if (user == NULL) {
X pw = getpwuid((int)user_id);
X if (pw == NULL) user = "?";
X user = copy_str(pw->pw_name);
X }
X
X return user;
X}
X
X
Xchar *date_time(t)
Xtime_t t;
X{
X char *str;
X
X if (t == (time_t)0) time(&t);
X str = ctime(&t);
X
X str[16] = 0;
X return str+4;
X}
X
X
X#ifndef HAVE_MKDIR
X
Xmkdir(path, mode)
Xchar *path;
Xint mode;
X{
X char command[FILENAME*2 + 20];
X
X sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1",
X path, mode, path);
X return system(command);
X}
X
X#endif
NO_NEWS_IS_GOOD_NEWS
chmod 0644 global.c || echo "restore of global.c fails"
set `wc -c global.c`;Sum=$1
if test "$Sum" != "8789"
then echo original size 8789, current size $Sum;fi
echo "x - extracting global.h (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > global.h &&
X/*
X * Marks for global/external variables
X */
X
X#define export /* export variable from module */
X#define import extern /* import variable into module */
X
X/*
X * Various constants and types
X */
X
Xtypedef int32 article_number;
Xtypedef int16 group_number;
Xtypedef uint32 time_stamp;
X
X/* frequently used characters */
X
X#define NUL '\0'
X#define TAB '\t'
X#define NL '\n'
X#define CR '\r'
X#define BS '\b'
X#define SP ' '
X
X/* misc macros */
X
X#define fl fflush(stdout)
X
X#ifdef CTRL
X#undef CTRL
X#endif
X#define CTRL(c) (c&037)
X
X#ifndef HAVE_STRCHR
X#define strrchr rindex
X#define strchr index
X#endif
X
X#ifdef SIGNAL_HANDLERS_ARE_VOID
Xtypedef void sig_type;
X#else
Xtypedef int sig_type;
X#endif
X
X/*
X * Some systems don't define these in <sys/stat.h>
X */
X
X#ifndef S_IFMT
X#define S_IFMT 0170000 /* type of file */
X#define S_IFDIR 0040000 /* directory */
X#define S_IFREG 0100000 /* regular */
X#endif
X
X#ifndef O_RDONLY
X#define O_RDONLY 0
X#define O_WRONLY 1
X#define O_RDWR 2
X#endif
X
X/* define types of library functions */
X
Xchar *malloc(), *calloc();
Xchar *getenv(), *ctime();
Xchar *strchr(), *strrchr();
Xoff_t lseek(), ftell(), tell();
Xtime_t time();
Xint atoi();
Xlong atol();
X
X
X/* define types of own functions */
X
Xchar *mk_file_name(), *home_relative();
Xchar *date_time(), *user_name();
Xchar *copy_str();
X
Xtime_t file_exist(), m_time();
X
Xextern FILE *open_file();
Xchar *relative();
X
X#define OPEN_READ 0 /* open for reading */
X#define OPEN_UPDATE 1 /* open/create for update */
X#define OPEN_CREATE 2 /* create/truncate for write */
X#define OPEN_APPEND 3 /* open for append */
X
X#define DONT_CREATE 0x40 /* return if file does not exist */
X#define MUST_EXIST 0x80 /* fatal error if cannot open */
X#define OPEN_UNLINK 0x100 /* unlink after open (not OPEN_UPDATE) */
X
X
X/*
X * Other external definitions
X *
X * NOTICE: the distinction between pointers and arrays is important
X * here (they are global variables - not function arguments)
X */
X
Xextern char
X
X *home_directory,
X *nn_directory,
X
X news_directory[],
X lib_directory[],
X db_directory[],
X
X *pager;
X
X
Xextern int
X
X s_hangup, /* hangup signal */
X s_keyboard, /* keyboard signal */
X s_pipe, /* broken pipe */
X s_redraw, /* continue signal after stop */
X
X#ifdef NNTP
X use_nntp, /* 1 iff we are using nntp */
X#endif
X
X is_master;
X
X
Xunsigned short /* as they are on most systems... */
X
X user_id,
X group_id;
X
Xint
X
X process_id;
X
Xextern int errno;
X
X#include "vararg.h"
X#include "data.h"
X
X/*
X * db external data
X */
X
Xextern master_header master;
X
X/* group headers */
X
Xextern group_header *active_groups, **sorted_groups;
X
X/* current group information */
X
Xextern char group_path_name[];
Xextern char *group_file_name;
X
Xextern group_header *current_group, *group_sequence;
X
Xextern group_header *lookup();
X
X
X#define Loop_Groups_Number(num) \
X for (num = master.number_of_groups; --num >= 0; )
X
X#define Loop_Groups_Header(gh) \
X for (gh=active_groups+master.number_of_groups; --gh >= active_groups;)
X
Xint l_g_index;
X
X#define Loop_Groups_Sorted(gh) \
X for (l_g_index = 0; \
X (l_g_index < master.number_of_groups) && \
X (gh = sorted_groups[l_g_index]) ;\
X l_g_index++)
X
X#define Loop_Groups_Sequence(gh) \
X for (gh = group_sequence; gh; gh = gh->next_group)
NO_NEWS_IS_GOOD_NEWS
chmod 0644 global.h || echo "restore of global.h fails"
set `wc -c global.h`;Sum=$1
if test "$Sum" != "3245"
then echo original size 3245, current size $Sum;fi
echo "x - extracting group.c (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > group.c &&
X/*
X * group menu
X */
X
X#include "config.h"
X#include "articles.h"
X#include "db.h"
X#include "term.h"
X#include "menu.h"
X#include "keymap.h"
X#include "regexp.h"
X
X
Xexport int dont_split_digests = 0;
Xexport int dont_sort_articles = 0;
X
Ximport int article_limit, also_read_articles;
Ximport int no_update;
Ximport int merged_menu;
X
Xchar *quick_match();
X
X/*
X * completion of group name
X */
X
Xgroup_completion(hbuf, ix)
Xchar *hbuf;
Xint ix;
X{
X static group_number next_group, n1, n2;
X static char *head, *tail, *last;
X static int tail_offset, prev_lgt, l1, l2;
X static group_header *prev_group, *p1, *p2;
X register group_header *gh;
X register char *t1, *t2;
X int order;
X
X if (ix < 0) return 0;
X
X if (hbuf) {
X n2 = next_group = 0;
X p2 = prev_group = NULL;
X l2 = 0;
X
X if (head = strrchr(hbuf, ','))
X head++;
X else
X head = hbuf;
X tail = hbuf + ix;
X tail_offset = ix - (head - hbuf);
X if (last = strrchr(head, '.')) last++; else last = head;
X return 1;
X }
X
X if (ix) {
X n1 = next_group, p1 = prev_group, l1 = prev_lgt;
X next_group = n2, prev_group = p2, prev_lgt = l2;
X list_completion((char *)NULL);
X }
X
X *tail = NUL;
X
X while (next_group < master.number_of_groups) {
X gh = sorted_groups[next_group++];
X if (gh->group_name_length <= tail_offset) continue;
X
X if (prev_group &&
X strncmp(prev_group->group_name, gh->group_name, prev_lgt) == 0)
X continue;
X
X order = strncmp(gh->group_name, head, tail_offset);
X if (order < 0) continue;
X if (order > 0) break;
X
X t1 = gh->group_name + tail_offset;
X if (t2 = strchr(t1, '.')) {
X strncpy(tail, t1, t2 - t1 + 1);
X tail[t2 - t1 + 1] = NUL;
X } else
X strcpy(tail, t1);
X
X prev_group = gh;
X prev_lgt = tail_offset + strlen(tail);
X if (ix) {
X if (list_completion(last) == 0) break;
X } else
X return 1;
X }
X
X if (ix) {
X n2 = next_group, p2 = prev_group, l2 = prev_lgt;
X if (n2 > master.number_of_groups)
X n2 = 0, p2 = NULL, l2 = 0;
X next_group = n1, prev_group = p1, prev_lgt = l1;
X return 1;
X }
X
X next_group = 0;
X prev_group = NULL;
X return 0;
X}
X
X
X/* flags to access_group */
X
X#define ALSO_CROSS_POSTINGS 0x01
X#define DONT_SORT_ARTICLES 0x02
X#define DONT_SPLIT_DIGESTS 0x04 /* only full digest */
X#define ALSO_FULL_DIGEST 0x08 /* also full digest */
X
Xstatic access_group(gh, first_article, last_article, flags, submask, do_kill)
Xregister group_header *gh;
Xarticle_number first_article, last_article;
Xint flags;
Xchar *submask;
Xint do_kill;
X{
X FILE *data;
X data_header hdr;
X off_t data_offset;
X register article_header *ah;
X cross_post_number cross_post;
X int skip_digest;
X int n;
X string_marker str_marker;
X memory_marker mem_marker;
X article_number art_num;
X static regexp *subpattern = NULL;
X static char subptext[80];
X
X if (init_group(gh) <= 0) return -2;
X
X if (first_article < gh->first_l_article)
X first_article = gh->first_l_article;
X
X if (last_article > gh->last_l_article)
X last_article = gh->last_l_article;
X
X if (last_article == 0 || first_article > last_article) return 0;
X
X data = open_data_file(gh, 'd', OPEN_READ);
X if (data == NULL) return -1;
X
X if ((data_offset = get_data_offset(gh, first_article)) == (off_t)(-1))
X return -1;
X
X if (submask && *submask == '/') {
X submask++;
X if (subpattern != NULL) {
X if (strncmp(submask, subptext, 80) != 0) {
X free(subpattern);
X subpattern = NULL;
X }
X }
X if (subpattern == NULL) {
X strncpy(subptext, submask, 80);
X subpattern = regcomp(submask);
X if (subpattern == NULL) return -1;
X }
X submask = NULL;
X } else
X if (subpattern != NULL) {
X free(subpattern);
X subpattern = NULL;
X }
X
X mark_memory(&mem_marker);
X
X ah = alloc_art();
X
X skip_digest = 0;
X
Xskip_to_next:
X fseek(data, data_offset, 0);
X
Xread_next:
X if (data_offset >= gh->data_write_offset) goto out;
X
X if (!db_read_art(data, &hdr, &data_offset)) goto out;
X
X if (hdr.dh_lpos == (off_t)0) /* article not accessible */
X goto read_next;
X
X if (art_num = hdr.dh_number) {
X if (art_num < 0) art_num = -art_num;
X if (art_num > gh->last_l_article ||
X art_num < gh->first_l_article)
X goto data_error;
X }
X
X data_offset += hdr.dh_cross_postings * sizeof(cross_post_number)
X + hdr.dh_sender_length
X + hdr.dh_subject_length;
X
X if (skip_digest && IS_SUB_DIGEST(hdr)) goto skip_to_next;
X
X skip_digest = 0;
X
X if (hdr.dh_cross_postings) {
X if ((flags & ALSO_CROSS_POSTINGS) == 0) {
X n = hdr.dh_cross_postings;
X do {
X if (fread((char *)&cross_post, sizeof(cross_post_number), 1, data) != 1)
X goto data_error;
X#ifdef NETWORK_DATABASE
X#ifndef NETWORK_BYTE_ORDER
X cross_post = ntohl(cross_post);
X#endif
X#endif
X if (active_groups[cross_post].group_flag & G_SUBSCRIPTION)
X break;
X } while (--n > 0);
X if (n > 0) {
X if (IS_DIGEST_HEADER(hdr)) skip_digest++;
X goto skip_to_next;
X }
X } else
X fseek(data, (off_t)(sizeof(cross_post_number)*hdr.dh_cross_postings), 1);
X }
X
X ah->flag = 0;
X
X if (IS_DIGEST_HEADER(hdr)) {
X if ((flags & (DONT_SPLIT_DIGESTS | ALSO_FULL_DIGEST)) == 0)
X goto skip_to_next; /* don't want the full digest when split */
X skip_digest++;
X ah->flag |= A_FULL_DIGEST;
X } else
X if (IS_SUB_DIGEST(hdr))
X ah->flag |= A_DIGEST;
X
X ah->a_number = ARTICLE_NUMBER(hdr);
X if (ah->a_number > last_article) goto out;
X
X mark_str(&str_marker);
X
X if (hdr.dh_sender_length) {
X ah->sender = alloc_str((int)hdr.dh_sender_length);
X if (fread(ah->sender, sizeof(char), (int)hdr.dh_sender_length, data)
X != hdr.dh_sender_length) goto data_error;
X } else
X ah->sender = "";
X
X if (hdr.dh_subject_length) {
X ah->subject = alloc_str((int)hdr.dh_subject_length);
X if (fread(ah->subject, sizeof(char), (int)hdr.dh_subject_length, data)
X != hdr.dh_subject_length) goto data_error;
X } else
X ah->subject = "";
X
X ah->hpos = hdr.dh_hpos;
X ah->fpos = ah->hpos + (off_t)(hdr.dh_fpos);
X ah->lpos = hdr.dh_lpos;
X
X ah->replies = hdr.dh_replies;
X ah->lines = hdr.dh_lines;
X
X ah->t_stamp = hdr.dh_date;
X
X if ((subpattern && !regexec(subpattern, ah->subject)) ||
X (submask && quick_match(ah->subject, submask) == NULL) ||
X (do_kill && kill_article(ah))) {
X killed_articles++;
X release_str(&str_marker);
X goto read_next;
X }
X
X ah->a_group = merged_menu ? current_group : NULL;
X
X add_article(ah);
X ah = alloc_art();
X
X goto read_next;
X
X
Xdata_error:
X log_entry('E', "%s: data inconsistency", gh->group_name);
X fclose(data);
X release_memory(&mem_marker);
X return -1;
X
Xout:
X fclose(data);
X
X if ((flags & DONT_SORT_ARTICLES) == 0)
X sort_articles();
X
X return n_articles;
X}
X
Xstatic article_number current_first_article;
X
X
Xstatic print_header()
X{
X extern long unread_articles;
X extern int unread_groups;
X
X so_printxy(0, 0, "Newsgroup: %s", current_group->group_name);
X clrline();
X
X so_gotoxy(-1, 0, 0);
X
X so_printf("Articles: %d", n_articles);
X
X if (no_update) {
X so_printf(" NO UPDATE");
X } else {
X if (current_first_article > current_group->first_article + 1)
X so_printf((killed_articles > 0) ? "(L-%ld,K-%d)" : "(L-%ld)",
X current_first_article - current_group->first_article - 1,
X killed_articles);
X else
X if (killed_articles > 0)
X so_printf(" (K-%d)", killed_articles);
X
X if (unread_articles > 0) {
X so_printf(" of %ld", unread_articles);
X if (unread_groups > 0)
X so_printf("/%d", unread_groups);
X }
X
X if ((current_group->group_flag & G_SUBSCRIPTION) == 0)
X so_printf(" UNSUB");
X else if (current_group->group_flag & G_NEW)
X so_printf(" NEW");
X
X if (current_group->group_flag & G_READ)
X so_printf(" READ");
X }
X
X so_end();
X
X return 1; /* number of lines in header */
X}
X
X
X
Xgroup_menu(gh, first_art, submask, do_kill, menu)
Xregister group_header *gh;
Xarticle_number first_art;
Xchar *submask;
Xint do_kill;
Xint (*menu)();
X{
X int status, was_unread;
X int menu_cmd, access_mode, did_selection, o_killed;
X article_number o_first_article, prev_last, last_article;
X memory_marker sel_marker; /* for marking old selection */
X
X#define menu_return(cmd) { menu_cmd = (cmd); goto menu_exit; }
X
X o_first_article = current_first_article;
X o_killed = killed_articles;
X mark_memory(&sel_marker);
X
Xafter_selection:
X
X did_selection = 0;
X killed_articles = 0;
X
X prev_last = gh->last_l_article;
X
X was_unread = add_unread(gh, -1);
X
X if (update_group(gh) < 0) {
X status = -2;
X goto access_exception;
X }
X
X if (gh->last_l_article < prev_last) {
X /* expire + renumbering */
X int32 updflag;
X
X if ((gh->last_article = gh->first_l_article - 1) < 0)
X gh->last_article = 0;
X gh->first_article = gh->last_article;
X updflag = gh->group_flag & (G_RC_UPDATED|G_READ);
X gh->group_flag &= ~(G_RC_UPDATED|G_READ);
X update_rc(gh);
X gh->group_flag &= ~(G_RC_UPDATED|G_READ);
X gh->group_flag |= updflag;
X }
X
X if (was_unread)
X add_unread(gh, 1);
X
X gotoxy(0, 0);
X fl;
X
X access_mode = 0;
X if (also_read_articles || submask)
X access_mode |= ALSO_CROSS_POSTINGS;
X if (dont_split_digests)
X access_mode |= DONT_SPLIT_DIGESTS;
X if (dont_sort_articles)
X access_mode |= DONT_SORT_ARTICLES;
X
X if (first_art == -1) {
X if (submask == NULL && !also_read_articles) {
X if (has_selection(gh, ¤t_first_article, &last_article)) {
X status = access_group(gh, current_first_article, last_article,
X DONT_SORT_ARTICLES, (char *)NULL, do_kill);
X do_selections(status >= 0 && n_articles);
X if (status < 0) goto access_exception;
X if (n_articles) {
X did_selection = 1;
X if (!dont_sort_articles) sort_articles();
X goto read_the_articles;
X }
X }
X }
X
X if (also_read_articles || submask)
X current_first_article = gh->first_l_article;
X else
X current_first_article = gh->last_article + 1;
X
X if (article_limit > 0) {
X article_number temp;
X
X temp = gh->last_l_article - article_limit + 1;
X if (current_first_article < temp) current_first_article = temp;
X }
X } else
X current_first_article = first_art;
X
X status = access_group(gh, current_first_article, gh->last_l_article,
X access_mode, submask, do_kill);
X
X access_exception:
X
X if (status < 0) {
X if (status == -1)
X msg("DATABASE CORRUPTED FOR GROUP %s", gh->group_name);
X/* else
X msg("Group %s is blocked - try again later", gh->group_name);
X*/
X menu_return( ME_NEXT );
X }
X
X if (n_articles == 0)
X menu_return( ME_NO_ARTICLES );
X
X read_the_articles:
X
X menu_cmd = (*menu)(print_header);
X
X menu_exit:
X
X if (menu_cmd == ME_QUIT || menu_cmd == ME_NEXT || menu_cmd == ME_PREV)
X if (submask == NULL && !no_update)
X save_selection(gh, current_first_article, gh->last_l_article);
X
X if (menu_cmd == ME_READ || menu_cmd == ME_NO_ARTICLES) {
X if (did_selection) {
X int was_read = gh->group_flag & (G_READ|G_RC_UPDATED);
X
X prev_last = gh->last_l_article;
X gh->last_l_article = last_article;
X update_rc(gh);
X gh->last_l_article = prev_last;
X
X if (last_article < gh->last_l_article) {
X gh->group_flag &= ~ (G_READ|G_RC_UPDATED);
X gh->group_flag |= was_read;
X release_memory(&sel_marker);
X goto after_selection;
X }
X } else
X if (submask == NULL && !also_read_articles &&
X (menu_cmd != ME_NO_ARTICLES ||
X (gh->group_flag & G_NEW) == 0) &&
X (first_art == -1 ||
X current_first_article == gh->first_article + 1))
X update_rc(gh);
X }
X
X current_first_article = o_first_article;
X killed_articles = o_killed;
X
X return menu_cmd;
X}
X
X
Xgoto_group(command, ah)
Xint command;
Xarticle_header *ah;
X{
X register group_header *gh;
X char ans1, *answer, *submask, buffer[FILENAME];
X article_number first, o_current_first;
X memory_marker mem_marker;
X group_header *orig_group;
X int menu_cmd;
X extern int menu(), file_completion();
X extern article_header *get_menu_article();
X extern int get_from_macro;
X extern group_header *jump_to_group;
X
X#define goto_return( cmd ) \
X { menu_cmd = cmd; goto goto_exit; }
X
X m_startinput();
X
X gh = orig_group = current_group;
X
X o_current_first = current_first_article;
X
X submask = NULL;
X
X if (command == K_GOTO_GROUP)
X goto get_group_name;
X
X for (;;) {
X if (command == K_ADVANCE_GROUP)
X gh = gh->next_group;
X else
X gh = gh->prev_group;
X
X if (gh == NULL)
X goto_return(ME_NO_REDRAW);
X
X if (gh->first_l_article >= gh->last_l_article) continue;
X
X prompt("\1Enter\1 %s%s%s ?", gh->group_name,
X gh->group_flag & G_SUBSCRIPTION ? "" : " (UNSUB)",
X gh->group_flag & G_READ ? " (READ)" : "" );
X
X command = get_c();
X if (command & GETC_COMMAND) goto_return(ME_REDRAW);
X if (command == 'y' || command == 'Y') break;
X if (command == 'n' || command == 'N') goto_return(ME_NO_REDRAW);
X
X command = menu_key_map[command];
X if (command == K_CONTINUE) break;
X if (command == K_ADVANCE_GROUP) continue;
X if (command == K_BACK_GROUP) continue;
X if (command == K_GOTO_GROUP) goto get_group_name;
X goto_return(ME_NO_REDRAW);
X }
X
X if (gh == orig_group) goto_return(ME_NO_REDRAW);
X
X goto get_first;
X
X get_group_name:
X
X if (current_group == NULL) {
X prompt("\1Enter Group or Folder\1 (+./~) ");
X answer = get_s(NONE, NONE, "+./~", group_completion);
X } else {
X prompt("\1Group or Folder\1 (+./~ %=N) ");
X answer = get_s(NONE, NONE, "+./0123456789~=%", group_completion);
X }
X
X if (answer == NULL) goto_return(ME_NO_REDRAW);
X
X if ((ans1 = *answer) == NUL) {
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X goto get_first;
X }
X
X sprintf(buffer, "%c", ans1);
X
X switch (ans1) {
X
X case '%':
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X if ((current_group->group_flag & G_FOLDER) == 0) {
X if (!ah) {
X prompt("\1READ\1");
X if ((ah = get_menu_article()) == NULL)
X goto_return(ME_NO_REDRAW);
X }
X if ((ah->flag & A_DIGEST) == 0) {
X *group_file_name = NUL;
X sprintf(buffer, "%s%ld", group_path_name, ah->a_number);
X answer = buffer;
X goto get_folder;
X }
X }
X
X msg("cannot split articles inside a folder or digest");
X goto_return(ME_NO_REDRAW);
X
X case '.':
X case '~':
X strcat(buffer, "/");
X case '+':
X case '/':
X if (!get_from_macro) {
X prompt("\1Folder\1 ");
X answer = get_s(NONE, buffer, NONE, file_completion);
X if (answer == NULL || answer[0] == NUL) goto_return(ME_NO_REDRAW);
X }
X
X get_folder:
X m_endinput();
X if (!expand_file_name(buffer, answer)) goto_return (ME_NO_REDRAW);
X menu_cmd = folder_menu(buffer);
X init_group(orig_group);
X goto goto_exit;
X
X
X case 'a':
X if (answer[1] == NUL || strcmp(answer, "all") == 0) {
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X first = 0;
X goto more_articles;
X }
X break;
X
X case 'u':
X if (answer[1] == NUL || strcmp(answer, "unread") == 0) {
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X first = gh->first_article + 1;
X goto more_articles;
X }
X break;
X
X case 's':
X if (answer[1] == NUL) {
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X goto get_subject;
X }
X
X break;
X
X case '=':
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X goto get_subject;
X
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X if (current_group == NULL) goto_return(ME_NO_REDRAW);
X if (current_first_article <= gh->first_l_article) {
X msg("No extra articles");
X flush_input();
X goto_return(ME_NO_REDRAW);
X }
X
X if (!get_from_macro) {
X prompt("\1Number of extra articles\1 max %ld: ",
X current_first_article - gh->first_l_article);
X sprintf(buffer, "%c", ans1);
X answer = get_s(NONE, buffer, NONE, NO_COMPLETION);
X if (answer == NULL || *answer == NUL) goto_return(ME_NO_REDRAW);
X }
X
X first = current_first_article - atol(answer);
X goto more_articles;
X
X default:
X break;
X }
X
X if ((gh = lookup(answer)) == NULL) {
X msg("No group named %s", answer);
X goto_return(ME_NO_REDRAW);
X }
X
X
X get_first:
X
X m_advinput();
X
X if (gh->last_l_article == 0 ||
X gh->last_l_article < gh->first_l_article) {
X msg("Group %s is empty", answer);
X goto_return(ME_NO_REDRAW);
X }
X
X if (gh == orig_group
X && current_first_article <= gh->first_article) {
X
X if (current_first_article <= gh->first_l_article) {
X /* no more articles to read */
X *answer = '=';
X get_from_macro = 0;
X goto get_subject;
X }
X
X prompt("\1Number of extra articles\1 all %ld, =subject: ",
X (long)(current_first_article - gh->first_l_article));
X
X answer = "a=s";
X } else {
X if (gh != orig_group)
X current_first_article = gh->last_l_article + 1;
X
X prompt("\1Number of%s articles\1 u)nread %ld,%s a)ll %ld, s)ubject: ",
X gh == orig_group ? " extra" : "",
X (long)(current_first_article - gh->first_article - 1),
X (gh->group_flag & G_UNREAD_COUNT) ? " j)ump," : "",
X (long)(current_first_article - gh->first_l_article));
X
X answer = (gh->group_flag & G_UNREAD_COUNT) ? "uja=s" : "ua=s";
X }
X
X answer = get_s(NONE, NONE, answer, NO_COMPLETION);
X if (answer == NULL) goto_return(ME_NO_REDRAW);
X
Xget_subject: /* when *answer == '=' */
X
X switch (*answer) {
X
X case NUL:
X if (gh != orig_group || current_first_article <= gh->first_l_article) {
X first = 0;
X break;
X }
X /* else fall thru */
X
X case 'u':
X first = gh->first_article + 1;
X break;
X
X case 'j':
X jump_to_group = gh;
X goto_return(ME_QUIT);
X
X case 'a':
X first = 0;
X break;
X
X case 's':
X case '=':
X first = 0;
X if (get_from_macro) {
X submask = answer + 1;
X } else {
X prompt("=");
X submask = get_s(ah ? ah->subject : NONE, NONE, ah ? NONE : "%=",
X NO_COMPLETION);
X if (submask == NULL) goto_return(ME_NO_REDRAW);
X if (*submask == '%' || *submask == '=') {
X if ((ah = get_menu_article()) == 0) goto_return(ME_NO_REDRAW);
X *submask = NUL;
X }
X }
X
X if (*submask == NUL)
X if (ah) {
X strncpy(submask, ah->subject, GET_S_BUFFER);
X submask[GET_S_BUFFER-1] = NUL;
X } else
X goto_return(ME_NO_REDRAW);
X
X if (*submask) {
X if (*submask != '/') init_quick_match(submask);
X } else
X submask = NULL;
X break;
X
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X first = current_first_article - atol(answer);
X break;
X
X default:
X ding();
X goto_return(ME_NO_REDRAW);
X }
X
X if (first > gh->last_l_article) goto_return(ME_NO_REDRAW);
X
Xmore_articles:
X if (gh == orig_group && submask == NULL &&
X first < current_first_article) {
X if (current_first_article <= gh->first_l_article) {
X msg("No extra articles");
X goto_return(ME_NO_REDRAW);
X }
X
X if (access_group(gh, first, current_first_article - 1,
X ALSO_CROSS_POSTINGS, (char *)NULL, 0) < 0) {
X msg("Cannot read extra articles (now)");
X goto_return(ME_NO_REDRAW);
X }
X
X current_first_article = first;
X
X goto_return(ME_REDRAW);
X }
X
X mark_memory(&mem_marker);
X m_endinput();
X menu_cmd = group_menu(gh, first, submask, 0, menu);
X release_memory(&mem_marker);
X
X if (gh != orig_group) {
X current_first_article = o_current_first;
X if (orig_group) init_group(orig_group);
X }
X
Xgoto_exit:
X m_endinput();
X return menu_cmd;
X}
X
Xstatic merged_header()
X{
X so_printxy(0, 0, "MERGED NEWS GROUPS: %d ARTICLES", n_articles);
X clrline();
X
X return 1;
X}
X
Xmerge_and_read(submask, do_kill)
Xchar *submask;
Xint do_kill;
X{
X register group_header *cur;
X group_header dummy_group;
X int access_mode;
X
X free_memory();
X
X access_mode = DONT_SORT_ARTICLES;
X if (also_read_articles || submask)
X access_mode |= ALSO_CROSS_POSTINGS;
X if (dont_split_digests)
X access_mode |= DONT_SPLIT_DIGESTS;
X
X for (cur = group_sequence; cur != NULL; cur = cur->next_group) {
X if (s_hangup || s_keyboard) break;
X
X if (cur->group_flag & G_FOLDER) {
X printf("\n\rIgnoring folder: %s\n\r", cur->group_name);
X continue;
X }
X
X if (!also_read_articles)
X if (cur->last_article >= cur->last_l_article)
X continue;
X
X printf("\r%s", cur->group_name); clrline();
X
X access_group(cur, -1, cur->last_l_article, access_mode, submask, do_kill);
X }
X merge_memory();
X if (n_articles == 0) return;
X if (!dont_sort_articles) sort_articles();
X
X dummy_group.group_flag = 0;
X dummy_group.save_file = NULL;
X dummy_group.group_name = "dummy";
X dummy_group.kill_list = NULL;
X
X current_group = &dummy_group;
X
X menu(merged_header);
X
X free_memory();
X}
X
Xunsubscribe(gh)
Xgroup_header *gh;
X{
X if (no_update) {
X msg("nn started in \"no update\" mode");
X return 0;
X }
X
X if (gh->group_flag & G_FOLDER) {
X msg("cannot unsubscribe to a folder");
X return 0;
X }
X
X if (gh->group_flag & G_SUBSCRIPTION) {
X prompt("\1Unsubscribe to\1 %s ? ", gh->group_name);
X if (yes(0) <= 0) return 0;
X
X add_unread(gh, -1);
X gh->group_flag &= ~G_SUBSCRIPTION;
X write_rc_entry(gh, 0);
X return 1;
X }
X
X prompt("Already unsubscribed. \1Resubscribe to\1 %s ? ",
X gh->group_name);
X if (yes(1) <= 0) return 0;
X
X gh->group_flag |= G_SUBSCRIPTION;
X write_rc_entry(gh, 0);
X
X add_unread(gh, 1);
X
X return 1;
X}
X
X
Xgroup_overview(amount)
Xint amount; /* 0=>unread,subscribed, 1=>unread,all, 2=>all,all 3=>unsub*/
X{
X register group_header *gh;
X
X clrdisp();
X
X pg_init(0, 2);
X
X Loop_Groups_Sorted(gh) {
X
X if (gh->group_flag & G_NO_DIRECTORY) continue;
X
X if (amount <= 1 && gh->last_article >= gh->last_l_article)
X continue;
X
X if (amount == 0 && (gh->group_flag & G_SUBSCRIPTION) == 0)
X continue;
X
X if (amount == 3 && (gh->group_flag & G_SUBSCRIPTION))
X continue;
X
X if (pg_next() < 0) break;
X
X printf("%6ld %s%s",
X (long)(gh->last_l_article - gh->last_article),
X gh->group_name,
X gh->group_flag & G_SUBSCRIPTION ? "" : " (!)");
X }
X
X pg_end();
X}
NO_NEWS_IS_GOOD_NEWS
chmod 0644 group.c || echo "restore of group.c fails"
set `wc -c group.c`;Sum=$1
if test "$Sum" != "22057"
then echo original size 22057, current size $Sum;fi
echo "x - extracting help.commands (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.commands &&
X;:ACOMMAND NAMES;:A ;:AMAP COMMAND;:A
X
X;:BNAME MENU MORE FUNCTION
Xadvance-group A advance one group in sequence
Xback-group B go back one group in sequence
Xcancel C C cancel an article
Xcommand : : extenced command prefix
Xcompress c compress text (eliminate extra spaces)
Xcontinue SPACE SPACE the "space bar" command
Xfind / regular expression search
Xfind-next . repeat regular expression search
Xfollow F f F follow up
Xfull-digest H show complete digest
Xgoto-group G G goto group or open folder
Xgoto-menu = go back to menu
Xhelp ? ? online help
Xkill-select K K kill/select handling
Xlayout L change menu layout
Xline+1 down CR next menu line/scroll one line
Xline-1 up previous menu line
Xline=@ g goto specific line
Xmail M m M mail or forward
Xmessage ^P ^P repeat last prompt line message
Xnext-article n skip to next article
Xnext-group N goto to next group without reading current
Xnext-subject k skip to next article with different subject
Xnil unbound key
Xoverview Y Y show groups with unread news
Xpage+1 > goto next page if any
Xpage+1/2 d ^D scroll half page forward
Xpage-1 < DEL goto one page back
Xpage-1/2 u ^U scroll half page backwards
Xpage=$ $ $ goto end of menu/article
Xpage=0 h goto header of article
Xpage=1 ^ ^ goto first menu/article page
Xpage=@ goto specific page of article (not implemented)
Xpost post new article
Xpreview % preview article
Xprevious P P goto previous group/article
Xprint p print article
Xquit Q Q quit nn
Xread-return Z read selected articles and return to menu
Xread-skip X read selected article, skip unseen menu pages
Xredraw ^L ^R ^L ^R redraw screen
Xreply R r R reply
Xrot13 D decrypt rot13 article
Xsave-body W w W save article without header
Xsave-full S s S save article with full header
Xsave-short O o O save article with short header
Xselect . select (or deselect) current menu entry
Xselect-auto + select "auto-selected" articles
Xselect-invert @ invert all selections on current menu page
Xselect-range - select range of articles
Xselect-subject * select all articles with current subject
Xshell ! ! shell command prefix
Xunselect-all ~ unselect all articles
Xunshar unshar article(s)
Xunsub U U unsubscribe (or subscribe) to current group
Xversion V V print release information
NO_NEWS_IS_GOOD_NEWS
chmod 0644 help.commands || echo "restore of help.commands fails"
set `wc -c help.commands`;Sum=$1
if test "$Sum" != "2312"
then echo original size 2312, current size $Sum;fi
echo "x - extracting help.extended (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.extended &&
X;:AEXTENDED COMMANDS;:A
X
X:help COMMAND give help on specific command
X
X:q! quit nn without update (only with -B option)
X:x quit nn, mark current group as read
X
X:mkdir [DIR] create directory DIR (will prompt for DIR if omitted)
X:cd [DIR] change working directory to DIR
X
X:admin enter administration mode
X
X:set OPTION [VALUE] set or unset option (use 'help set' for more info)
X:map MODE KEY COMMAND remap key or command
X:show TABLE show contents of various tables
X:sort MODE sort menu according to subject, age, or arrival
X
X:unread (N) mark current group as unread (last N articles)
X:coredump abort with a core dump
NO_NEWS_IS_GOOD_NEWS
chmod 0644 help.extended || echo "restore of help.extended fails"
set `wc -c help.extended`;Sum=$1
if test "$Sum" != "626"
then echo original size 626, current size $Sum;fi
echo "x - extracting help.help (Text)"
sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.help &&
X;:AHELP COMMAND;:A
X
XSynopsis
X
X :help subject
X
XHelp is available on the the following subjects:
X
Xcommands commands that can be bound to keys
Xextended extended commands (:command)
Xmap key mapping
Xset variable setting
NO_NEWS_IS_GOOD_NEWS
echo "End of part 4"
echo "File help.help is continued in part 5"
echo "5" > 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