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 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@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.