home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / nn / part04 < prev    next >
Encoding:
Internet Message Format  |  1989-06-22  |  49.6 KB

  1. Subject:  v19i065:  NN, a Usenet news reader, Part04/15
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: mcvax!tidk!storm@uunet.UU.NET (Kim F. Storm)
  7. Posting-number: Volume 19, Issue 65
  8. Archive-name: nn/part04
  9.  
  10. #!/bin/sh
  11. # this is part 4 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file folder.c continued
  14. #
  15. CurArch=4
  16. if test ! -r s2_seq_.tmp
  17. then echo "Please unpack part 1 first!"
  18.      exit 1; fi
  19. ( read Scheck
  20.   if test "$Scheck" != $CurArch
  21.   then echo "Please unpack part $Scheck next!"
  22.        exit 1;
  23.   else exit 0; fi
  24. ) < s2_seq_.tmp || exit 1
  25. echo "x - Continuing file folder.c"
  26. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> folder.c
  27. X    
  28. X    if (*path == '|') return -1;    /* no completion for pipes */
  29. X    
  30. X    if (*path == '+' || *path == '~') {
  31. X        if (!expand_file_name(nbuf, path))
  32. X        return 0;    /* no completions */
  33. X    } else
  34. X        strcpy(nbuf, path);
  35. X    
  36. X    if (base = strrchr(nbuf, '/')) {
  37. X        if (base == nbuf) {
  38. X        dir = "/";
  39. X        base++;
  40. X        } else {
  41. X        *base++ = NUL;
  42. X        dir = nbuf;
  43. X        }
  44. X    } else {
  45. X        base = nbuf;
  46. X        dir = ".";
  47. X    }
  48. X    
  49. X    tail_offset = strlen(base);
  50. X
  51. X    dir_in_use = list_directory(dir, base);
  52. X
  53. X    return dir_in_use;
  54. X    }
  55. X
  56. X    if (index)
  57. X    return help_directory();
  58. X    
  59. X    if (!next_directory(buffer)) return 0;
  60. X    
  61. X    strcpy(tail, buffer+tail_offset);
  62. X    
  63. X    return 1;
  64. X}
  65. X
  66. X
  67. X/*
  68. X *     read file names in directory 'dir' starting with 'prefix'
  69. X *
  70. X *    this could be speeded up by an order of magnitude by
  71. X *    reading the directory directly into an array and sort
  72. X *    it internally.
  73. X *    
  74. X */
  75. X
  76. Xstatic char dir_path[FILENAME], *dir_tail;
  77. X
  78. X#ifndef HAVE_DIRECTORY
  79. X
  80. Xstatic FILE *dirf;
  81. Xstatic int prefix_lgt;
  82. X
  83. Xstatic list_directory(dir, prefix)
  84. Xchar *dir, *prefix;
  85. X{
  86. X    sprintf(dir_path, "cd %s && echo %s* 2>/dev/null", dir, prefix);
  87. X    prefix_lgt = strlen(prefix);
  88. X    
  89. X    if ((dirf = popen(dir_path, "r")) == NULL) return 0;
  90. X
  91. X    dir_tail = dir_path; 
  92. X    while (*dir_tail++ = *dir++); 
  93. X    dir_tail[-1] = '/';
  94. X
  95. X    return 1;
  96. X}
  97. X
  98. Xstatic next_directory(buffer)
  99. Xchar *buffer;
  100. X{
  101. X    register char *cp;
  102. X    register int c;
  103. X    
  104. X    cp = buffer;
  105. X    while ((c = getc(dirf)) != EOF && (c != SP) && (c != NL))
  106. X    *cp++ = c;
  107. X    
  108. X    if (cp != buffer) {
  109. X    *cp = NUL;
  110. X    if (strcmp(buffer + prefix_lgt, "*")) {
  111. X
  112. X        strcpy(dir_tail, buffer);
  113. X            if (file_exist(dir_path, "d")) {
  114. X        *cp++ = '/';
  115. X        *cp = NUL;
  116. X        }
  117. X
  118. X        return 1;
  119. X    }
  120. X    
  121. X    }
  122. X    
  123. X    close_directory();
  124. X    return 0;
  125. X}
  126. X
  127. Xhelp_directory()
  128. X{
  129. X    return 0;
  130. X}
  131. X
  132. Xstatic close_directory()
  133. X{
  134. X    if (dirf) {
  135. X    pclose(dirf);
  136. X    dirf = NULL;
  137. X    }
  138. X}    
  139. X#else /* HAVE_DIRECTORY */
  140. X
  141. Xstatic string_marker str_mark;
  142. Xstatic char **completions = NULL;
  143. Xstatic char **comp_iterator;
  144. Xstatic char **comp_help;
  145. X
  146. X/* 
  147. X * list_directory scans the directory twice; first time to find out how
  148. X * many matches there are, and second time to save the names, after
  149. X * sufficient memory have been allocated to store it all.
  150. X */
  151. X
  152. Xstatic sort_directory(f1, f2)        /* Used by qsort */
  153. X    register char **f1;
  154. X    register char **f2;
  155. X{
  156. X    return strcmp(*f1, *f2);
  157. X}
  158. X
  159. Xstatic list_directory(dir, prefix)
  160. Xchar *dir, *prefix;
  161. X{
  162. X    DIR *dirp;
  163. X    register Direntry *dp;
  164. X    register char *cp;
  165. X    register char **comp;
  166. X    int pflen = strlen(prefix);
  167. X    unsigned nmatch = 1;    /* No. of completions plus one */
  168. X
  169. X    if ((dirp = opendir(dir)) == NULL)
  170. X    return 0;            /* tough luck */
  171. X
  172. X    while ((dp = readdir(dirp)) != NULL) {
  173. X    cp = dp->d_name;
  174. X    if (*cp == '.' && (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
  175. X        continue;
  176. X    if (strncmp(prefix, cp, pflen) == 0)
  177. X        nmatch++;
  178. X    }
  179. X    if (nmatch == 1
  180. X    || (completions = (char **)calloc(nmatch, sizeof(char *))) == NULL) {
  181. X    closedir(dirp);
  182. X    return 0;
  183. X    }
  184. X    mark_str(&str_mark);
  185. X
  186. X    rewinddir(dirp);
  187. X    for (comp = completions; (dp = readdir(dirp)) != NULL; ) {
  188. X    cp = dp->d_name;
  189. X    if (*cp == '.' && (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0')))
  190. X        continue;
  191. X    if (strncmp(prefix, cp, pflen) == 0)
  192. X        strcpy(*comp++ = alloc_str(strlen(cp)), cp);
  193. X    }
  194. X    closedir(dirp);
  195. X    *comp = (char *)0;
  196. X    qsort((char *)completions, comp - completions, sizeof(char *), sort_directory);
  197. X    comp_iterator = completions;
  198. X    comp_help = completions;
  199. X    
  200. X    dir_tail = dir_path; 
  201. X    while (*dir_tail++ = *dir++); 
  202. X    dir_tail[-1] = '/';
  203. X    
  204. X    return 1;
  205. X}
  206. X
  207. Xstatic next_directory(buffer)
  208. X    register char *buffer;
  209. X{
  210. X    if (*comp_iterator != NULL) {
  211. X    strcpy(dir_tail, *comp_iterator);
  212. X    strcpy(buffer, *comp_iterator++);
  213. X    
  214. X    if ( file_exist(dir_path, "d") ) 
  215. X        strcat(buffer, "/");
  216. X    return 1;
  217. X    }
  218. X    close_directory();
  219. X    return 0;
  220. X}
  221. X
  222. Xhelp_directory()
  223. X{
  224. X    list_completion((char *)NULL);
  225. X
  226. X    if (*comp_help == NULL) comp_help = completions;
  227. X    while (*comp_help && list_completion(*comp_help))
  228. X        comp_help++;
  229. X    
  230. X    fl;
  231. X    return 1;
  232. X}
  233. X
  234. Xstatic close_directory()
  235. X{
  236. X    if (completions) {
  237. X    release_str(&str_mark);
  238. X    free((char *)completions);
  239. X    completions = NULL;
  240. X    }
  241. X}    
  242. X#endif /* HAVE_DIRECTORY */
  243. X
  244. Xstatic int cancel_count;
  245. X
  246. Xfcancel(ah)
  247. Xarticle_header *ah;
  248. X{
  249. X    if (ah->flag & A_CANCEL) {
  250. X    cancel_count--;
  251. X    ah->flag &= ~A_CANCEL;
  252. X    } else {
  253. X    cancel_count++;
  254. X    ah->flag |= A_CANCEL;
  255. X    } 
  256. X}
  257. X
  258. Xstatic folder_header()
  259. X{
  260. X    so_printxy(0, 0, "Folder: %s", current_group->group_name);
  261. X
  262. X    return 1;    /* number of header lines */
  263. X}    
  264. X
  265. Xfolder_menu(path)
  266. Xchar *path;
  267. X{
  268. X    FILE             *folder;
  269. X    register article_header    *ap;
  270. X    news_header_buffer         dgbuf;
  271. X    char             buffer[256];
  272. X    int                more, length, re, menu_cmd, was_raw;
  273. X    memory_marker        mem_marker;
  274. X    group_header         fake_group;
  275. X    int                cc_save;
  276. X    
  277. X    fake_group.group_name = path;
  278. X    fake_group.group_flag = G_RC_UPDATED | G_FOLDER | G_READ;
  279. X    init_group(&fake_group);
  280. X    
  281. X    folder = open_file(group_path_name, OPEN_READ);
  282. X    if (folder == NULL) {
  283. X    msg("%s not found", path);
  284. X    return ME_NO_REDRAW;
  285. X    }
  286. X
  287. X    was_raw = no_raw();
  288. X    s_keyboard = 0;
  289. X    
  290. X    printf("\rReading: %-.65s", path);
  291. X    clrline();
  292. X    
  293. X    current_group = &fake_group;
  294. X
  295. X    mark_memory(&mem_marker);
  296. X    
  297. X    ap = alloc_art();
  298. X    
  299. X    more = 1;
  300. X    while (more && (more = get_digest_article(folder, dgbuf)) >= 0) {
  301. X    if (s_keyboard) break;
  302. X    
  303. X    ap->a_number = 0;
  304. X    ap->flag = A_FOLDER;
  305. X
  306. X    ap->lines = digest.dg_lines;
  307. X
  308. X    ap->hpos = digest.dg_hpos;
  309. X    ap->fpos = digest.dg_fpos;
  310. X    ap->lpos = digest.dg_lpos;
  311. X
  312. X    if (digest.dg_from) {
  313. X        length = pack_name(buffer, digest.dg_from, NAME_LENGTH);
  314. X        ap->sender = alloc_str(length);
  315. X        strcpy(ap->sender, buffer);
  316. X    } else
  317. X        ap->sender = "";
  318. X
  319. X    if (digest.dg_subj) {
  320. X        length = pack_subject(buffer, digest.dg_subj, &re, 255);
  321. X        ap->replies = re;
  322. X        ap->subject = alloc_str(length);
  323. X        strcpy(ap->subject, buffer);
  324. X    } else {
  325. X        ap->replies = 0;
  326. X        ap->subject = "";
  327. X    }
  328. X    
  329. X    add_article(ap);
  330. X    ap = alloc_art();
  331. X    }
  332. X    
  333. X    fclose(folder);
  334. X
  335. X    if (was_raw) raw();
  336. X    
  337. X    if (s_keyboard) {
  338. X    menu_cmd = ME_NO_REDRAW;
  339. X    } else
  340. X    if (n_articles == 0) {
  341. X    msg("Not a folder (no article header)");
  342. X    menu_cmd = ME_NO_REDRAW;
  343. X    } else {
  344. X    strcpy(buffer, path);
  345. X    fake_group.group_name = buffer;    /* save for later use */
  346. X    
  347. X    if (n_articles > 1) {
  348. X        clrdisp();
  349. X        prompt_line = 2;
  350. X        if (!dont_sort_folders) sort_articles();
  351. X    }
  352. X    
  353. X    cc_save = cancel_count;
  354. X    cancel_count = 0;
  355. X
  356. X     reenter_menu:
  357. X    menu_cmd = menu(folder_header);
  358. X
  359. X    if (cancel_count) {
  360. X        clrdisp();
  361. X        printf("Folder: %s\nFile:   %s\n\n", buffer, group_path_name);
  362. X        printf("Remove %d article%s from folder? ", 
  363. X           cancel_count, cancel_count == 1 ? "" : "s");
  364. X        fl;
  365. X        
  366. X        switch (yes(1)) {
  367. X         case 1:
  368. X        printf("\n\n");
  369. X        rewrite_folder();
  370. X        break;
  371. X         case 0:
  372. X        break;
  373. X         default:
  374. X        goto reenter_menu;
  375. X        }
  376. X    }
  377. X    cancel_count = cc_save;
  378. X    }
  379. X    
  380. X    release_memory(&mem_marker);
  381. X
  382. X    return menu_cmd;
  383. X}
  384. X
  385. X
  386. Xrewrite_folder()
  387. X{
  388. X    register FILE *src, *dst;
  389. X    char oldfile[FILENAME], *sp;
  390. X    register int c;
  391. X    register long cnt;
  392. X    register article_header *ah, **ahp;
  393. X    register int n;
  394. X    
  395. X    if ((src = fopen(group_path_name, "r")) == NULL) {
  396. X    msg("Cannot open %s", group_path_name);
  397. X    return;
  398. X    }
  399. X    
  400. X    strcpy(oldfile, group_path_name);
  401. X    sp = strrchr(oldfile, '/');
  402. X    if (!sp) goto move_error;
  403. X    strcpy(sp+1, "~OLD~FOLDER~");
  404. X
  405. X    unlink(oldfile);
  406. X    if (link(group_path_name, oldfile) < 0) goto move_error;
  407. X    if (unlink(group_path_name) < 0) {
  408. X    if (unlink(oldfile) == 0) goto move_error;
  409. X    printf("\n\n%s was linked to %s --- cannot proceed\n",
  410. X           group_path_name, oldfile);
  411. X    sleep(5);
  412. X    return;
  413. X    }
  414. X    
  415. X    if ((dst = fopen(group_path_name, "w")) == NULL) {
  416. X    fclose(src);
  417. X    goto move_back;
  418. X    }
  419. X
  420. X    unsort_articles(1);
  421. X
  422. X    printf("Compressing folder..."); fl;
  423. X    
  424. X    for (ahp = articles, n = n_articles; --n >= 0; ahp++) {
  425. X    ah = *ahp;
  426. X    if (ah->flag & A_CANCEL) continue;
  427. X    fseek(src, ah->hpos, 0);
  428. X    cnt = ah->lpos - ah->hpos;
  429. X    while (--cnt >= 0) {
  430. X        if ((c = getc(src)) == EOF) break;
  431. X        putc(c, dst);
  432. X    }
  433. X    putc(NL, dst);
  434. X    }
  435. X    fclose(src);
  436. X    if (ferror(dst)) {
  437. X    fclose(dst);
  438. X    goto move_back;
  439. X    }
  440. X    return;
  441. X
  442. Xmove_back:
  443. X    if (link(oldfile, group_path_name) == 0) {
  444. X    unlink(oldfile);
  445. X    printf("Cannot create new file -- Folder restored\n");
  446. X    sleep(2);
  447. X    } else {
  448. X    printf("Cannot create new file\n\nFolder saved in %s\n",
  449. X           oldfile);
  450. X    sleep(10);
  451. X    }
  452. X    return;
  453. X    
  454. Xmove_error:
  455. X    fclose(src);
  456. X    printf("\n\nCannot move folder %s to %s\n",
  457. X       group_path_name, oldfile);
  458. X    sleep(3);
  459. X    return;
  460. X}
  461. NO_NEWS_IS_GOOD_NEWS
  462. echo "File folder.c is complete"
  463. chmod 0644 folder.c || echo "restore of folder.c fails"
  464. set `wc -c folder.c`;Sum=$1
  465. if test "$Sum" != "11228"
  466. then echo original size 11228, current size $Sum;fi
  467. echo "x - extracting global.c (Text)"
  468. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > global.c &&
  469. X#include <signal.h>
  470. X#include <errno.h>
  471. X#include <pwd.h>
  472. X#include "config.h"
  473. X
  474. Xexport char *home_directory;
  475. Xexport char *nn_directory;
  476. Xexport char news_directory[] = NEWS_DIRECTORY;    /* /usr/spool/news */
  477. Xexport char lib_directory[]  = LIB_DIRECTORY;    /* /usr/local/lib/nn     */
  478. Xexport char db_directory[]   = DB_DIRECTORY;    /* /usr/spool/nn    */
  479. X
  480. Xexport char *temp_file;
  481. X
  482. Xexport char *pager = PAGER;        /* pg or more        */
  483. X
  484. Xexport int is_master;
  485. X
  486. X/* signal handler interface */
  487. X
  488. Xexport int s_hangup        = 0;    /* hangup signal */
  489. Xexport int s_keyboard        = 0;    /* keyboard interrupt */
  490. Xexport int s_pipe        = 0;    /* broken pipe */
  491. Xexport int s_redraw        = 0;    /* redraw signal (if job control) */
  492. X
  493. Xstatic sig_type catch_hangup(n)
  494. X{
  495. X    signal(n, SIG_IGN);
  496. X    
  497. X    s_hangup++;
  498. X}
  499. X
  500. Xstatic sig_type catch_keyboard(n)
  501. X{
  502. X    s_keyboard++;
  503. X    
  504. X#ifdef RESET_SIGNAL_WHEN_CAUGHT
  505. X    signal(n, catch_keyboard);
  506. X#endif    
  507. X}
  508. X
  509. Xstatic sig_type catch_pipe(n)
  510. X{
  511. X    s_pipe++;
  512. X    
  513. X#ifdef RESET_SIGNAL_WHEN_CAUGHT
  514. X    signal(n, catch_pipe);
  515. X#endif    
  516. X}
  517. X
  518. X#ifdef HAVE_JOBCONTROL
  519. Xstatic sig_type catch_redraw(n)
  520. X{
  521. X    s_redraw++;
  522. X
  523. X#ifdef RESET_SIGNAL_WHEN_CAUGHT
  524. X    signal(n, catch_redraw);
  525. X#endif
  526. X}
  527. X#endif
  528. X
  529. X
  530. Xinit_global(who)
  531. Xint who;
  532. X{
  533. X    char *env;
  534. X    unsigned short getuid(), getgid();
  535. X    int getpid();
  536. X
  537. X    is_master = (who == 1);
  538. X    
  539. X    signal(SIGTERM, catch_hangup);
  540. X    signal(SIGHUP,  catch_hangup);
  541. X    signal(SIGPIPE, catch_pipe);
  542. X    signal(SIGALRM, SIG_IGN);
  543. X    
  544. X#ifdef SIGPWR
  545. X    signal(SIGPWR, catch_hangup);
  546. X#endif
  547. X
  548. X    user_id = getuid();
  549. X    group_id = getgid();
  550. X    process_id = getpid();
  551. X    
  552. X    if (is_master) {
  553. X    signal(SIGINT,  catch_hangup);
  554. X    signal(SIGQUIT, catch_hangup);
  555. X    return;
  556. X    }
  557. X    
  558. X    signal(SIGINT,  catch_keyboard);
  559. X    signal(SIGQUIT, catch_keyboard);
  560. X#ifdef HAVE_JOBCONTROL
  561. X    signal(SIGCONT, catch_redraw);
  562. X#endif    
  563. X
  564. X    if ((home_directory = getenv("HOME")) == NULL) 
  565. X    user_error("No HOME environment variable");
  566. X
  567. X    nn_directory = mk_file_name(home_directory, ".nn");
  568. X    
  569. X    if (!file_exist(nn_directory, "drwx"))
  570. X    mkdir(nn_directory, 0755);    /* should check here */
  571. X
  572. X    if ((env = getenv("TMPDIR")) == NULL) env = TMP_DIRECTORY;
  573. X    temp_file = mk_file_name(env, "nn.XXXXXX");    /* dies in ANSI C! */
  574. X    mktemp(temp_file);
  575. X}
  576. X
  577. X/*
  578. X * this is for admin K & W commands and for master -w
  579. X */
  580. X
  581. Xkill_master(sig)
  582. Xint sig;
  583. X{
  584. X    FILE *m_pid;
  585. X    int  pid, ok;
  586. X    char buf[10];
  587. X    
  588. X    m_pid = open_file(relative(lib_directory, "MPID"), OPEN_READ);
  589. X    if (m_pid == NULL) {
  590. X    errno = ESRCH;
  591. X    return 0;
  592. X    }
  593. X    
  594. X    ok = 0;    /* not yet */
  595. X
  596. X    if (fgets(buf, 10, m_pid) == NULL)
  597. X    printf("MPID file is empty\n");
  598. X    else {
  599. X    pid = atoi(buf);
  600. X    if (pid <= 2)
  601. X        printf("MPID file contains illegal process id: %d\n", pid);
  602. X    else
  603. X        if (kill(pid, sig) != -1) ok++;
  604. X    }
  605. X
  606. X    fclose(m_pid);
  607. X
  608. X    return ok;
  609. X}    
  610. X
  611. X    
  612. Xmem_check(addr, size, msg)
  613. Xchar *addr, *msg;
  614. Xint size;
  615. X{
  616. X    if (addr == NULL) {
  617. X    if (is_master)
  618. X        sys_error("Cannot allocate %d %s", size, msg);
  619. X    else    
  620. X        user_error("Cannot allocate %d %s", size, msg);
  621. X    }
  622. X}
  623. X
  624. XFILE *open_file(name, mode)
  625. Xchar *name;
  626. Xint mode;
  627. X{
  628. X    FILE *f;
  629. X    int fd;
  630. X    
  631. X    if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL))
  632. X    return NULL;
  633. X
  634. X    switch (mode & 0x0f) {
  635. X
  636. X     case OPEN_READ:
  637. X    
  638. X    f = fopen(name, "r");
  639. X    break;
  640. X    
  641. X     case OPEN_UPDATE:
  642. X    
  643. X/*    f = fopen(name, "r+");     -- not reliable on many systems (sigh) */
  644. X
  645. X    if ((fd = open(name, O_WRONLY)) >= 0) {
  646. X        if ((f = fdopen(fd, "w")) != NULL) return f;
  647. X        close(fd);
  648. X    }
  649. X
  650. X    /* fall thru */
  651. X    
  652. X     case OPEN_CREATE:
  653. X    
  654. X    f = fopen(name, "w");
  655. X    break;
  656. X    
  657. X     case OPEN_APPEND:
  658. X    
  659. X    f = fopen(name, "a");
  660. X    break;
  661. X
  662. X     default:
  663. X    
  664. X    sys_error("Illegal mode: open_file(%s, %d)", name, mode);
  665. X    }
  666. X
  667. X    if (f) {
  668. X    if (mode & OPEN_UNLINK) unlink(name);
  669. X    return f;
  670. X    }
  671. X    
  672. X    if ((mode & MUST_EXIST) == 0) return NULL;
  673. X    
  674. X    if (is_master)
  675. X    sys_error("Cannot open file %s, mode: %d", name, mode);
  676. X    else {
  677. X    log_entry('R', "Client cannot open file %s, mode: %d", name, mode);
  678. X    user_error("Cannot open file %s", name);
  679. X    }
  680. X    
  681. X    return NULL;
  682. X}
  683. X
  684. X
  685. X
  686. X
  687. X/*
  688. X *     relative -- concat directory name and file name
  689. X */
  690. X
  691. Xchar *relative(dir, name)
  692. Xchar *dir, *name;
  693. X{
  694. X    static char concat_path[FILENAME];
  695. X
  696. X    sprintf(concat_path, "%s/%s", dir, name);
  697. X    return concat_path;
  698. X}
  699. X
  700. X
  701. Xchar *mk_file_name(dir, name)
  702. Xchar *dir, *name;
  703. X{
  704. X    char *buf;
  705. X    
  706. X    buf = malloc((unsigned)(strlen(dir) + strlen(name) + 2));
  707. X    mem_check(buf, 1, "file name");
  708. X    sprintf(buf, "%s/%s", dir, name);
  709. X
  710. X    return buf;
  711. X}
  712. X
  713. X
  714. Xchar *home_relative(dir)
  715. Xchar *dir;
  716. X{
  717. X    if (dir) {
  718. X    if (*dir == '/') 
  719. X        return copy_str(dir);
  720. X    else {
  721. X        if (*dir == '~' && *++dir == '/') dir++;
  722. X        return mk_file_name(home_directory, dir);
  723. X    }
  724. X    }
  725. X    return NULL;
  726. X}
  727. X
  728. X    
  729. Xchar *copy_str(str)
  730. Xchar *str;
  731. X{
  732. X    char *new;
  733. X    
  734. X    new = malloc((unsigned)(strlen(str) + 1));
  735. X    mem_check(new, 1, "string");
  736. X    if (new) strcpy(new, str);
  737. X
  738. X    return new;
  739. X}
  740. X
  741. Xtime_t m_time(f)
  742. XFILE *f;
  743. X{
  744. X    struct stat st;
  745. X    
  746. X    if (fstat(fileno(f), &st) < 0) return 0;
  747. X    return st.st_mtime;
  748. X}
  749. X
  750. X
  751. Xtime_t file_exist(name, mode)
  752. Xchar *name;
  753. Xchar *mode;
  754. X{
  755. X    struct stat statb;
  756. X    extern int errno;
  757. X    
  758. X    if (stat(name, &statb)) return 0;
  759. X
  760. X    if (mode == NULL) return statb.st_mtime;
  761. X    
  762. X    while (*mode) {
  763. X    switch (*mode++) {
  764. X    case 'd':
  765. X        if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
  766. X        errno = ENOTDIR;
  767. X        return 0;
  768. X    case 'f':
  769. X        if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
  770. X        if ((statb.st_mode & S_IFMT) == 0000000) continue;
  771. X        if ((statb.st_mode & S_IFMT) == S_IFDIR) {
  772. X        errno = EISDIR;
  773. X        return 0;
  774. X        }
  775. X        break;
  776. X    case 'r':
  777. X        if ((statb.st_mode & 0400) && statb.st_uid == user_id) continue;
  778. X        if ((statb.st_mode & 0040) && statb.st_gid == group_id) continue;
  779. X            if ((statb.st_mode & 0004)) continue;
  780. X        break;
  781. X    case 'w':
  782. X        if ((statb.st_mode & 0200) && statb.st_uid == user_id) continue;
  783. X        if ((statb.st_mode & 0020) && statb.st_gid == group_id) continue;
  784. X            if ((statb.st_mode & 0002)) continue;
  785. X        break;
  786. X    case 'x':
  787. X        if ((statb.st_mode & 0100) && statb.st_uid == user_id) continue;
  788. X        if ((statb.st_mode & 0010) && statb.st_gid == group_id) continue;
  789. X            if ((statb.st_mode & 0001)) continue;
  790. X        break;
  791. X    }        
  792. X    errno = EACCES;
  793. X    return 0;
  794. X    }
  795. X
  796. X    /* all modes are ok */
  797. X    return statb.st_mtime;
  798. X}
  799. X
  800. X
  801. X
  802. Xprint_version(fmt)
  803. Xchar *fmt;
  804. X{
  805. X    extern int Update_Level, Patch_Level;
  806. X    int param;
  807. X    
  808. X    for (; *fmt; fmt++) {
  809. X
  810. X    if (*fmt == '%') {
  811. X        switch (*++fmt) {
  812. X         case 'R':
  813. X        param = RELEASE;
  814. X        break;
  815. X         case 'V':
  816. X        param = VERSION;
  817. X        break;
  818. X         case 'P':
  819. X        param = Patch_Level;
  820. X        break;
  821. X         case 'U':
  822. X        param = Update_Level;
  823. X        break;
  824. X         default:
  825. X        continue;
  826. X        }
  827. X        printf("%d", param);
  828. X        continue;
  829. X    } 
  830. X    putchar(*fmt);
  831. X    }
  832. X
  833. X    fl;
  834. X}
  835. X
  836. X/*VARARGS*/
  837. Xlog_entry(va_alist)
  838. Xva_dcl
  839. X{
  840. X    int type;
  841. X    va_list ap;
  842. X
  843. X    va_start(ap);
  844. X    type = va_arg1(int);
  845. X    enter_log(type, va_args2toN);
  846. X    va_end(ap);
  847. X}
  848. X
  849. X#ifdef HAVE_SYSLOG
  850. X#include <syslog.h>
  851. X#endif /* HAVE_SYSLOG */
  852. X
  853. X/*VARARGS*/
  854. Xsys_error(va_alist)
  855. Xva_dcl
  856. X{
  857. X    va_list ap;
  858. X
  859. X    va_start(ap);
  860. X    enter_log('E', va_args1toN);
  861. X    va_end(ap);
  862. X
  863. X    if (is_master) {
  864. X#ifndef HAVE_SYSLOG
  865. X    FILE *f;
  866. X    
  867. X    f = open_file("/dev/console", OPEN_CREATE);
  868. X    if (f == NULL) nn_exit(8);
  869. X    fprintf(f, "\n\rNNMASTER FATAL ERROR\n\r");
  870. X    fclose(f);
  871. X#else /* HAVE_SYSLOG */
  872. X    char buf[512];
  873. X    char *fmt;
  874. X
  875. X    va_start(ap);
  876. X    fmt = va_arg1(char *);
  877. X    vsprintf(buf, fmt, va_args2toN);
  878. X    va_end(ap);
  879. X
  880. X    openlog("nnmaster", LOG_CONS, LOG_DAEMON);
  881. X    syslog(LOG_ALERT, "%s", buf);
  882. X    closelog();
  883. X#endif /* HAVE_SYSLOG */
  884. X    }    
  885. X    nn_exit(7);
  886. X}
  887. X
  888. X
  889. Xstatic enter_log(type, va_tail)
  890. Xchar type;
  891. Xva_tdcl
  892. X{
  893. X    FILE *log;
  894. X    char *msg, buf[512], logname[512];
  895. X
  896. X    msg  = va_arg1(char *);
  897. X    vsprintf(buf, msg, va_args2toN);
  898. X
  899. X    /* cannot use relative: one of the args may be generated by it */
  900. X    sprintf(logname, "%s/Log", lib_directory);
  901. X
  902. X    log = open_file(logname, OPEN_APPEND);
  903. X    if (log == NULL) return 0;
  904. X
  905. X    fprintf(log, "%c: %s (%s): %s\n", type,
  906. X        date_time((time_t)0), user_name(), buf);
  907. X    
  908. X    fclose(log);
  909. X    return 1;
  910. X}
  911. X
  912. X
  913. Xchar *user_name()
  914. X{
  915. X    static char *user = NULL;
  916. X    struct passwd *pw, *getpwuid();
  917. X
  918. X    if (is_master) return "M";
  919. X    
  920. X    if (user == NULL) {
  921. X    pw = getpwuid((int)user_id);
  922. X    if (pw == NULL) user = "?";
  923. X    user = copy_str(pw->pw_name);
  924. X    }
  925. X    
  926. X    return user;
  927. X}
  928. X
  929. X
  930. Xchar *date_time(t)
  931. Xtime_t t;
  932. X{
  933. X    char *str;
  934. X    
  935. X    if (t == (time_t)0) time(&t);
  936. X    str = ctime(&t);
  937. X    
  938. X    str[16] = 0;
  939. X    return str+4;
  940. X}
  941. X
  942. X
  943. X#ifndef HAVE_MKDIR
  944. X
  945. Xmkdir(path, mode)
  946. Xchar *path;
  947. Xint mode;
  948. X{
  949. X    char command[FILENAME*2 + 20];
  950. X    
  951. X    sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1",
  952. X        path, mode, path);
  953. X    return system(command);
  954. X}
  955. X
  956. X#endif
  957. NO_NEWS_IS_GOOD_NEWS
  958. chmod 0644 global.c || echo "restore of global.c fails"
  959. set `wc -c global.c`;Sum=$1
  960. if test "$Sum" != "8789"
  961. then echo original size 8789, current size $Sum;fi
  962. echo "x - extracting global.h (Text)"
  963. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > global.h &&
  964. X/*
  965. X *    Marks for global/external variables
  966. X */
  967. X
  968. X#define    export            /* export variable from module */
  969. X#define import    extern        /* import variable into module */
  970. X
  971. X/*
  972. X *    Various constants and types
  973. X */
  974. X
  975. Xtypedef int32    article_number;
  976. Xtypedef int16    group_number;
  977. Xtypedef uint32    time_stamp;
  978. X
  979. X/* frequently used characters */
  980. X
  981. X#define NUL    '\0'
  982. X#define TAB    '\t'
  983. X#define NL    '\n'
  984. X#define CR    '\r'
  985. X#define BS    '\b'
  986. X#define SP    ' '
  987. X
  988. X/* misc macros */
  989. X
  990. X#define fl fflush(stdout)
  991. X
  992. X#ifdef CTRL
  993. X#undef CTRL
  994. X#endif
  995. X#define CTRL(c)    (c&037)
  996. X
  997. X#ifndef HAVE_STRCHR
  998. X#define    strrchr        rindex
  999. X#define strchr        index
  1000. X#endif
  1001. X
  1002. X#ifdef SIGNAL_HANDLERS_ARE_VOID
  1003. Xtypedef void sig_type;
  1004. X#else
  1005. Xtypedef int sig_type;
  1006. X#endif
  1007. X
  1008. X/*
  1009. X *    Some systems don't define these in <sys/stat.h>
  1010. X */
  1011. X
  1012. X#ifndef S_IFMT
  1013. X#define    S_IFMT    0170000            /* type of file */
  1014. X#define S_IFDIR    0040000            /* directory */
  1015. X#define S_IFREG    0100000            /* regular */
  1016. X#endif
  1017. X
  1018. X#ifndef O_RDONLY
  1019. X#define    O_RDONLY    0
  1020. X#define    O_WRONLY    1
  1021. X#define    O_RDWR        2
  1022. X#endif
  1023. X
  1024. X/* define types of library functions */
  1025. X
  1026. Xchar     *malloc(), *calloc();
  1027. Xchar     *getenv(), *ctime();
  1028. Xchar     *strchr(), *strrchr();
  1029. Xoff_t     lseek(), ftell(), tell();
  1030. Xtime_t     time();
  1031. Xint    atoi();
  1032. Xlong    atol();
  1033. X
  1034. X
  1035. X/* define types of own functions */
  1036. X
  1037. Xchar *mk_file_name(), *home_relative();
  1038. Xchar *date_time(), *user_name();
  1039. Xchar *copy_str();
  1040. X
  1041. Xtime_t    file_exist(), m_time();
  1042. X
  1043. Xextern FILE *open_file();
  1044. Xchar *relative();
  1045. X
  1046. X#define    OPEN_READ    0    /* open for reading */
  1047. X#define    OPEN_UPDATE    1    /* open/create for update */
  1048. X#define    OPEN_CREATE    2    /* create/truncate for write */
  1049. X#define    OPEN_APPEND    3    /* open for append */
  1050. X
  1051. X#define    DONT_CREATE    0x40    /* return if file does not exist */
  1052. X#define    MUST_EXIST    0x80    /* fatal error if cannot open */
  1053. X#define    OPEN_UNLINK    0x100    /* unlink after open (not OPEN_UPDATE) */
  1054. X
  1055. X
  1056. X/*
  1057. X *    Other external definitions
  1058. X *
  1059. X *    NOTICE: the distinction between pointers and arrays is important
  1060. X *        here (they are global variables - not function arguments)
  1061. X */
  1062. X
  1063. Xextern char
  1064. X
  1065. X    *home_directory,
  1066. X    *nn_directory,
  1067. X
  1068. X    news_directory[],
  1069. X    lib_directory[],
  1070. X    db_directory[],
  1071. X
  1072. X    *pager;
  1073. X
  1074. X
  1075. Xextern int
  1076. X
  1077. X    s_hangup,    /* hangup signal */
  1078. X    s_keyboard,    /* keyboard signal */
  1079. X    s_pipe,    /* broken pipe */
  1080. X    s_redraw,    /* continue signal after stop */
  1081. X
  1082. X#ifdef NNTP
  1083. X    use_nntp,   /* 1 iff we are using nntp */
  1084. X#endif
  1085. X
  1086. X    is_master;
  1087. X
  1088. X
  1089. Xunsigned short            /* as they are on most systems... */
  1090. X
  1091. X    user_id,
  1092. X    group_id;
  1093. X
  1094. Xint
  1095. X
  1096. X    process_id;
  1097. X
  1098. Xextern int errno;
  1099. X
  1100. X#include "vararg.h"
  1101. X#include "data.h"
  1102. X
  1103. X/*
  1104. X *    db external data
  1105. X */
  1106. X
  1107. Xextern master_header master;
  1108. X
  1109. X/* group headers */
  1110. X
  1111. Xextern group_header *active_groups, **sorted_groups;
  1112. X
  1113. X/* current group information */
  1114. X
  1115. Xextern char     group_path_name[];
  1116. Xextern char    *group_file_name;
  1117. X
  1118. Xextern group_header *current_group, *group_sequence;
  1119. X
  1120. Xextern group_header *lookup();
  1121. X
  1122. X
  1123. X#define    Loop_Groups_Number(num) \
  1124. X    for (num = master.number_of_groups; --num >= 0; )
  1125. X
  1126. X#define Loop_Groups_Header(gh) \
  1127. X    for (gh=active_groups+master.number_of_groups; --gh >= active_groups;)
  1128. X
  1129. Xint l_g_index;
  1130. X
  1131. X#define Loop_Groups_Sorted(gh) \
  1132. X    for (l_g_index = 0; \
  1133. X     (l_g_index < master.number_of_groups) && \
  1134. X     (gh = sorted_groups[l_g_index]) ;\
  1135. X     l_g_index++)
  1136. X
  1137. X#define    Loop_Groups_Sequence(gh) \
  1138. X    for (gh = group_sequence; gh; gh = gh->next_group)
  1139. NO_NEWS_IS_GOOD_NEWS
  1140. chmod 0644 global.h || echo "restore of global.h fails"
  1141. set `wc -c global.h`;Sum=$1
  1142. if test "$Sum" != "3245"
  1143. then echo original size 3245, current size $Sum;fi
  1144. echo "x - extracting group.c (Text)"
  1145. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > group.c &&
  1146. X/*
  1147. X *    group menu
  1148. X */
  1149. X
  1150. X#include "config.h"
  1151. X#include "articles.h"
  1152. X#include "db.h"
  1153. X#include "term.h"
  1154. X#include "menu.h"
  1155. X#include "keymap.h"
  1156. X#include "regexp.h"
  1157. X
  1158. X
  1159. Xexport int  dont_split_digests = 0;
  1160. Xexport int  dont_sort_articles = 0;
  1161. X
  1162. Ximport int  article_limit, also_read_articles;
  1163. Ximport int  no_update;
  1164. Ximport int  merged_menu;
  1165. X
  1166. Xchar *quick_match();
  1167. X
  1168. X/*
  1169. X * completion of group name
  1170. X */
  1171. X
  1172. Xgroup_completion(hbuf, ix)
  1173. Xchar *hbuf;
  1174. Xint ix;
  1175. X{
  1176. X    static group_number next_group, n1, n2;
  1177. X    static char *head, *tail, *last;
  1178. X    static int  tail_offset, prev_lgt, l1, l2;
  1179. X    static group_header *prev_group, *p1, *p2;
  1180. X    register group_header *gh;
  1181. X    register char *t1, *t2;
  1182. X    int order;
  1183. X
  1184. X    if (ix < 0) return 0;
  1185. X
  1186. X    if (hbuf) {
  1187. X    n2 = next_group = 0;
  1188. X    p2 = prev_group = NULL;
  1189. X    l2 = 0;
  1190. X    
  1191. X    if (head = strrchr(hbuf, ',')) 
  1192. X        head++;
  1193. X    else
  1194. X        head = hbuf;
  1195. X    tail = hbuf + ix;
  1196. X    tail_offset = ix - (head - hbuf);
  1197. X    if (last = strrchr(head, '.')) last++; else last = head;
  1198. X    return 1;
  1199. X    }
  1200. X
  1201. X    if (ix) {
  1202. X    n1 = next_group, p1 = prev_group, l1 = prev_lgt;
  1203. X    next_group = n2, prev_group = p2, prev_lgt = l2;
  1204. X    list_completion((char *)NULL);
  1205. X    }
  1206. X    
  1207. X    *tail = NUL;
  1208. X    
  1209. X    while (next_group < master.number_of_groups) {
  1210. X    gh = sorted_groups[next_group++]; 
  1211. X    if (gh->group_name_length <= tail_offset) continue;
  1212. X
  1213. X    if (prev_group &&
  1214. X        strncmp(prev_group->group_name, gh->group_name, prev_lgt) == 0)
  1215. X        continue;
  1216. X    
  1217. X    order = strncmp(gh->group_name, head, tail_offset);
  1218. X    if (order < 0) continue;
  1219. X    if (order > 0) break;
  1220. X    
  1221. X    t1 = gh->group_name + tail_offset;
  1222. X    if (t2 = strchr(t1, '.')) {
  1223. X        strncpy(tail, t1, t2 - t1 + 1);
  1224. X        tail[t2 - t1 + 1] = NUL;
  1225. X    } else
  1226. X        strcpy(tail, t1);
  1227. X
  1228. X    prev_group = gh;
  1229. X    prev_lgt = tail_offset + strlen(tail);
  1230. X    if (ix) {
  1231. X        if (list_completion(last) == 0) break;
  1232. X    } else
  1233. X        return 1;
  1234. X    }
  1235. X
  1236. X    if (ix) {
  1237. X    n2 = next_group, p2 = prev_group, l2 = prev_lgt;
  1238. X    if (n2 > master.number_of_groups) 
  1239. X        n2 = 0, p2 = NULL, l2 = 0;
  1240. X    next_group = n1, prev_group = p1, prev_lgt = l1;
  1241. X    return 1;
  1242. X    }
  1243. X
  1244. X    next_group = 0;
  1245. X    prev_group = NULL;
  1246. X    return 0;
  1247. X}
  1248. X
  1249. X
  1250. X/* flags to access_group */
  1251. X
  1252. X#define    ALSO_CROSS_POSTINGS    0x01
  1253. X#define    DONT_SORT_ARTICLES    0x02
  1254. X#define    DONT_SPLIT_DIGESTS    0x04    /* only full digest */
  1255. X#define    ALSO_FULL_DIGEST    0x08    /* also full digest */
  1256. X
  1257. Xstatic access_group(gh, first_article, last_article, flags, submask, do_kill)
  1258. Xregister group_header    *gh;
  1259. Xarticle_number    first_article, last_article;
  1260. Xint        flags;
  1261. Xchar         *submask;
  1262. Xint        do_kill;
  1263. X{
  1264. X    FILE            *data;
  1265. X    data_header            hdr;
  1266. X    off_t              data_offset;
  1267. X    register article_header    *ah;
  1268. X    cross_post_number        cross_post;
  1269. X    int                skip_digest;
  1270. X    int                n;
  1271. X    string_marker        str_marker;
  1272. X    memory_marker        mem_marker;
  1273. X    article_number        art_num;
  1274. X    static regexp        *subpattern = NULL;
  1275. X    static char            subptext[80];
  1276. X    
  1277. X    if (init_group(gh) <= 0) return -2;
  1278. X
  1279. X    if (first_article < gh->first_l_article) 
  1280. X    first_article = gh->first_l_article;
  1281. X
  1282. X    if (last_article > gh->last_l_article) 
  1283. X    last_article = gh->last_l_article;
  1284. X
  1285. X    if (last_article == 0 || first_article > last_article) return 0;
  1286. X
  1287. X    data = open_data_file(gh, 'd', OPEN_READ);
  1288. X    if (data == NULL) return -1;
  1289. X
  1290. X    if ((data_offset = get_data_offset(gh, first_article)) == (off_t)(-1))
  1291. X    return -1;
  1292. X
  1293. X    if (submask && *submask == '/') {
  1294. X    submask++;
  1295. X    if (subpattern != NULL) {
  1296. X        if (strncmp(submask, subptext, 80) != 0) {
  1297. X        free(subpattern);
  1298. X        subpattern = NULL;
  1299. X        }
  1300. X    }
  1301. X    if (subpattern == NULL) {
  1302. X        strncpy(subptext, submask, 80);
  1303. X        subpattern = regcomp(submask);
  1304. X        if (subpattern == NULL) return -1;
  1305. X    }
  1306. X    submask = NULL;
  1307. X    } else
  1308. X    if (subpattern != NULL) {
  1309. X        free(subpattern);
  1310. X        subpattern = NULL;
  1311. X    }
  1312. X    
  1313. X    mark_memory(&mem_marker);
  1314. X    
  1315. X    ah = alloc_art();
  1316. X
  1317. X    skip_digest = 0;
  1318. X
  1319. Xskip_to_next:
  1320. X    fseek(data, data_offset, 0);
  1321. X    
  1322. Xread_next:
  1323. X    if (data_offset >= gh->data_write_offset) goto out;
  1324. X    
  1325. X    if (!db_read_art(data, &hdr, &data_offset)) goto out;
  1326. X
  1327. X    if (hdr.dh_lpos == (off_t)0)    /* article not accessible */
  1328. X    goto read_next;
  1329. X
  1330. X    if (art_num = hdr.dh_number) {
  1331. X    if (art_num < 0) art_num = -art_num;
  1332. X    if (art_num > gh->last_l_article ||
  1333. X        art_num < gh->first_l_article)
  1334. X        goto data_error;
  1335. X    }
  1336. X    
  1337. X    data_offset += hdr.dh_cross_postings * sizeof(cross_post_number)
  1338. X         + hdr.dh_sender_length 
  1339. X         + hdr.dh_subject_length;
  1340. X
  1341. X    if (skip_digest && IS_SUB_DIGEST(hdr)) goto skip_to_next;
  1342. X    
  1343. X    skip_digest = 0;
  1344. X    
  1345. X    if (hdr.dh_cross_postings) {
  1346. X    if ((flags & ALSO_CROSS_POSTINGS) == 0) {
  1347. X        n = hdr.dh_cross_postings;
  1348. X        do {
  1349. X        if (fread((char *)&cross_post, sizeof(cross_post_number), 1, data) != 1)
  1350. X            goto data_error;
  1351. X#ifdef NETWORK_DATABASE
  1352. X#ifndef NETWORK_BYTE_ORDER
  1353. X        cross_post = ntohl(cross_post);
  1354. X#endif
  1355. X#endif        
  1356. X        if (active_groups[cross_post].group_flag & G_SUBSCRIPTION)
  1357. X            break;
  1358. X        } while (--n > 0);
  1359. X        if (n > 0) {
  1360. X        if (IS_DIGEST_HEADER(hdr)) skip_digest++;
  1361. X        goto skip_to_next;
  1362. X        } 
  1363. X    } else
  1364. X        fseek(data, (off_t)(sizeof(cross_post_number)*hdr.dh_cross_postings), 1);
  1365. X    }    
  1366. X
  1367. X    ah->flag = 0;
  1368. X
  1369. X    if (IS_DIGEST_HEADER(hdr)) {
  1370. X    if ((flags & (DONT_SPLIT_DIGESTS | ALSO_FULL_DIGEST)) == 0) 
  1371. X        goto skip_to_next;    /* don't want the full digest when split */
  1372. X    skip_digest++;
  1373. X    ah->flag |= A_FULL_DIGEST;
  1374. X    } else
  1375. X    if (IS_SUB_DIGEST(hdr))
  1376. X        ah->flag |= A_DIGEST;
  1377. X    
  1378. X    ah->a_number = ARTICLE_NUMBER(hdr);
  1379. X    if (ah->a_number > last_article) goto out;
  1380. X    
  1381. X    mark_str(&str_marker);
  1382. X    
  1383. X    if (hdr.dh_sender_length) {
  1384. X    ah->sender = alloc_str((int)hdr.dh_sender_length);
  1385. X    if (fread(ah->sender, sizeof(char), (int)hdr.dh_sender_length, data)
  1386. X        != hdr.dh_sender_length) goto data_error;
  1387. X    } else
  1388. X    ah->sender = "";
  1389. X    
  1390. X    if (hdr.dh_subject_length) {
  1391. X    ah->subject = alloc_str((int)hdr.dh_subject_length);
  1392. X    if (fread(ah->subject, sizeof(char), (int)hdr.dh_subject_length, data)
  1393. X        !=  hdr.dh_subject_length) goto data_error;
  1394. X    } else
  1395. X    ah->subject = "";
  1396. X
  1397. X    ah->hpos    = hdr.dh_hpos;
  1398. X    ah->fpos    = ah->hpos + (off_t)(hdr.dh_fpos);
  1399. X    ah->lpos    = hdr.dh_lpos;
  1400. X    
  1401. X    ah->replies    = hdr.dh_replies;
  1402. X    ah->lines    = hdr.dh_lines;
  1403. X
  1404. X    ah->t_stamp = hdr.dh_date;
  1405. X    
  1406. X    if ((subpattern && !regexec(subpattern, ah->subject)) ||
  1407. X    (submask && quick_match(ah->subject, submask) == NULL) ||
  1408. X    (do_kill && kill_article(ah))) {
  1409. X    killed_articles++;
  1410. X    release_str(&str_marker);
  1411. X    goto read_next;
  1412. X    }
  1413. X
  1414. X    ah->a_group = merged_menu ? current_group : NULL;
  1415. X    
  1416. X    add_article(ah);
  1417. X    ah = alloc_art();
  1418. X
  1419. X    goto read_next;
  1420. X
  1421. X    
  1422. Xdata_error:
  1423. X    log_entry('E', "%s: data inconsistency", gh->group_name);
  1424. X    fclose(data);
  1425. X    release_memory(&mem_marker);
  1426. X    return -1;
  1427. X    
  1428. Xout:
  1429. X    fclose(data);
  1430. X    
  1431. X    if ((flags & DONT_SORT_ARTICLES) == 0)
  1432. X    sort_articles();
  1433. X    
  1434. X    return n_articles;
  1435. X}
  1436. X
  1437. Xstatic article_number current_first_article;
  1438. X
  1439. X
  1440. Xstatic print_header()
  1441. X{
  1442. X    extern long unread_articles;
  1443. X    extern int unread_groups;
  1444. X    
  1445. X    so_printxy(0, 0, "Newsgroup: %s", current_group->group_name);
  1446. X    clrline();
  1447. X    
  1448. X    so_gotoxy(-1, 0, 0);
  1449. X    
  1450. X    so_printf("Articles: %d", n_articles);
  1451. X
  1452. X    if (no_update) {
  1453. X    so_printf(" NO UPDATE");
  1454. X    } else {
  1455. X    if (current_first_article > current_group->first_article + 1)
  1456. X        so_printf((killed_articles > 0) ? "(L-%ld,K-%d)" : "(L-%ld)", 
  1457. X              current_first_article - current_group->first_article - 1,
  1458. X              killed_articles);
  1459. X    else
  1460. X        if (killed_articles > 0)
  1461. X        so_printf(" (K-%d)", killed_articles);
  1462. X    
  1463. X    if (unread_articles > 0) {
  1464. X        so_printf(" of %ld", unread_articles);
  1465. X        if (unread_groups > 0) 
  1466. X        so_printf("/%d", unread_groups);
  1467. X    }
  1468. X    
  1469. X    if ((current_group->group_flag & G_SUBSCRIPTION) == 0)
  1470. X        so_printf(" UNSUB");
  1471. X    else if (current_group->group_flag & G_NEW) 
  1472. X        so_printf(" NEW"); 
  1473. X    
  1474. X    if (current_group->group_flag & G_READ) 
  1475. X        so_printf(" READ");
  1476. X    }
  1477. X    
  1478. X    so_end();
  1479. X
  1480. X    return 1;    /* number of lines in header */
  1481. X}
  1482. X
  1483. X
  1484. X
  1485. Xgroup_menu(gh, first_art, submask, do_kill, menu)
  1486. Xregister group_header *gh;
  1487. Xarticle_number first_art;
  1488. Xchar *submask;
  1489. Xint do_kill;
  1490. Xint (*menu)();
  1491. X{
  1492. X    int status, was_unread;
  1493. X    int menu_cmd, access_mode, did_selection, o_killed;
  1494. X    article_number o_first_article, prev_last, last_article;
  1495. X    memory_marker sel_marker;    /* for marking old selection */
  1496. X    
  1497. X#define    menu_return(cmd) { menu_cmd = (cmd); goto menu_exit; }
  1498. X    
  1499. X    o_first_article = current_first_article;
  1500. X    o_killed = killed_articles;
  1501. X    mark_memory(&sel_marker);
  1502. X    
  1503. Xafter_selection:
  1504. X
  1505. X    did_selection = 0;
  1506. X    killed_articles = 0;
  1507. X
  1508. X    prev_last = gh->last_l_article;
  1509. X
  1510. X    was_unread = add_unread(gh, -1);
  1511. X    
  1512. X    if (update_group(gh) < 0) {
  1513. X    status = -2;
  1514. X    goto access_exception;
  1515. X    }
  1516. X    
  1517. X    if (gh->last_l_article < prev_last) {
  1518. X    /* expire + renumbering */
  1519. X    int32 updflag;
  1520. X
  1521. X    if ((gh->last_article = gh->first_l_article - 1) < 0)
  1522. X        gh->last_article = 0;
  1523. X    gh->first_article = gh->last_article;
  1524. X    updflag = gh->group_flag & (G_RC_UPDATED|G_READ);
  1525. X    gh->group_flag &= ~(G_RC_UPDATED|G_READ);
  1526. X    update_rc(gh);
  1527. X    gh->group_flag &= ~(G_RC_UPDATED|G_READ);
  1528. X    gh->group_flag |= updflag;
  1529. X    }
  1530. X
  1531. X    if (was_unread)
  1532. X    add_unread(gh, 1);
  1533. X
  1534. X    gotoxy(0, 0);
  1535. X    fl;
  1536. X
  1537. X    access_mode = 0;
  1538. X    if (also_read_articles || submask)
  1539. X    access_mode |= ALSO_CROSS_POSTINGS;
  1540. X    if (dont_split_digests)
  1541. X    access_mode |= DONT_SPLIT_DIGESTS;
  1542. X    if (dont_sort_articles)
  1543. X    access_mode |= DONT_SORT_ARTICLES;
  1544. X    
  1545. X    if (first_art == -1) {
  1546. X    if (submask == NULL && !also_read_articles) {
  1547. X        if (has_selection(gh, ¤t_first_article, &last_article)) {
  1548. X        status = access_group(gh, current_first_article, last_article,
  1549. X                      DONT_SORT_ARTICLES, (char *)NULL, do_kill);
  1550. X        do_selections(status >= 0 && n_articles);
  1551. X        if (status < 0) goto access_exception;
  1552. X        if (n_articles) {
  1553. X            did_selection = 1;
  1554. X            if (!dont_sort_articles) sort_articles();
  1555. X            goto read_the_articles;
  1556. X        }
  1557. X        }
  1558. X    }
  1559. X    
  1560. X    if (also_read_articles || submask)
  1561. X        current_first_article = gh->first_l_article;
  1562. X    else
  1563. X        current_first_article = gh->last_article + 1;
  1564. X    
  1565. X    if (article_limit > 0) {
  1566. X        article_number temp;
  1567. X        
  1568. X        temp = gh->last_l_article - article_limit + 1;
  1569. X        if (current_first_article < temp) current_first_article = temp;
  1570. X    }
  1571. X    } else
  1572. X    current_first_article = first_art;
  1573. X    
  1574. X    status = access_group(gh, current_first_article, gh->last_l_article,
  1575. X              access_mode, submask, do_kill);
  1576. X
  1577. X access_exception:
  1578. X
  1579. X    if (status < 0) {
  1580. X    if (status == -1)
  1581. X        msg("DATABASE CORRUPTED FOR GROUP %s", gh->group_name);
  1582. X/*    else
  1583. X        msg("Group %s is blocked - try again later", gh->group_name);
  1584. X*/
  1585. X    menu_return( ME_NEXT );
  1586. X    }
  1587. X    
  1588. X    if (n_articles == 0)
  1589. X    menu_return( ME_NO_ARTICLES );
  1590. X
  1591. X read_the_articles:
  1592. X
  1593. X    menu_cmd = (*menu)(print_header);
  1594. X    
  1595. X menu_exit:
  1596. X
  1597. X    if (menu_cmd == ME_QUIT || menu_cmd == ME_NEXT || menu_cmd == ME_PREV)
  1598. X    if (submask == NULL && !no_update) 
  1599. X        save_selection(gh, current_first_article, gh->last_l_article);
  1600. X
  1601. X    if (menu_cmd == ME_READ || menu_cmd == ME_NO_ARTICLES) {
  1602. X    if (did_selection) {
  1603. X        int was_read = gh->group_flag & (G_READ|G_RC_UPDATED);
  1604. X
  1605. X        prev_last = gh->last_l_article;
  1606. X        gh->last_l_article = last_article;
  1607. X        update_rc(gh);
  1608. X        gh->last_l_article = prev_last;
  1609. X        
  1610. X        if (last_article < gh->last_l_article) {
  1611. X        gh->group_flag &= ~ (G_READ|G_RC_UPDATED);
  1612. X        gh->group_flag |= was_read;
  1613. X        release_memory(&sel_marker);
  1614. X        goto after_selection;
  1615. X        }
  1616. X    } else
  1617. X        if (submask == NULL && !also_read_articles &&
  1618. X        (menu_cmd != ME_NO_ARTICLES || 
  1619. X         (gh->group_flag & G_NEW) == 0) &&
  1620. X        (first_art == -1 || 
  1621. X         current_first_article == gh->first_article + 1))
  1622. X        update_rc(gh);
  1623. X    }
  1624. X    
  1625. X    current_first_article = o_first_article;
  1626. X    killed_articles = o_killed;
  1627. X    
  1628. X    return menu_cmd;
  1629. X}
  1630. X
  1631. X
  1632. Xgoto_group(command, ah)
  1633. Xint command;
  1634. Xarticle_header *ah;
  1635. X{
  1636. X    register group_header *gh;
  1637. X    char ans1, *answer, *submask, buffer[FILENAME];
  1638. X    article_number first, o_current_first;
  1639. X    memory_marker mem_marker;
  1640. X    group_header *orig_group;
  1641. X    int menu_cmd;
  1642. X    extern int menu(), file_completion();
  1643. X    extern article_header *get_menu_article();
  1644. X    extern int get_from_macro;
  1645. X    extern group_header *jump_to_group;
  1646. X
  1647. X#define goto_return( cmd ) \
  1648. X    { menu_cmd = cmd; goto goto_exit; }
  1649. X
  1650. X    m_startinput();
  1651. X    
  1652. X    gh = orig_group = current_group;
  1653. X    
  1654. X    o_current_first = current_first_article;
  1655. X    
  1656. X    submask = NULL;
  1657. X
  1658. X    if (command == K_GOTO_GROUP) 
  1659. X    goto get_group_name;
  1660. X    
  1661. X    for (;;) {
  1662. X    if (command == K_ADVANCE_GROUP)
  1663. X        gh = gh->next_group;
  1664. X    else
  1665. X        gh = gh->prev_group;
  1666. X    
  1667. X    if (gh == NULL)
  1668. X        goto_return(ME_NO_REDRAW);
  1669. X    
  1670. X    if (gh->first_l_article >= gh->last_l_article) continue;
  1671. X    
  1672. X    prompt("\1Enter\1 %s%s%s ?", gh->group_name,
  1673. X           gh->group_flag & G_SUBSCRIPTION ? "" : " (UNSUB)",
  1674. X           gh->group_flag & G_READ ? " (READ)" : "" );
  1675. X    
  1676. X    command = get_c();
  1677. X    if (command & GETC_COMMAND) goto_return(ME_REDRAW);
  1678. X    if (command == 'y' || command == 'Y') break;
  1679. X    if (command == 'n' || command == 'N') goto_return(ME_NO_REDRAW);
  1680. X    
  1681. X    command = menu_key_map[command];
  1682. X    if (command == K_CONTINUE) break;
  1683. X    if (command == K_ADVANCE_GROUP) continue;
  1684. X    if (command == K_BACK_GROUP) continue;
  1685. X    if (command == K_GOTO_GROUP) goto get_group_name;
  1686. X    goto_return(ME_NO_REDRAW);
  1687. X    }
  1688. X    
  1689. X    if (gh == orig_group) goto_return(ME_NO_REDRAW);
  1690. X    
  1691. X    goto get_first;
  1692. X    
  1693. X get_group_name:
  1694. X
  1695. X    if (current_group == NULL) {
  1696. X    prompt("\1Enter Group or Folder\1 (+./~) ");
  1697. X    answer = get_s(NONE, NONE, "+./~", group_completion);
  1698. X    } else {
  1699. X    prompt("\1Group or Folder\1 (+./~ %=N) ");
  1700. X    answer = get_s(NONE, NONE, "+./0123456789~=%", group_completion);
  1701. X    }
  1702. X    
  1703. X    if (answer == NULL) goto_return(ME_NO_REDRAW);
  1704. X
  1705. X    if ((ans1 = *answer) == NUL) {
  1706. X    if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1707. X    goto get_first;
  1708. X    }
  1709. X    
  1710. X    sprintf(buffer, "%c", ans1);
  1711. X    
  1712. X    switch (ans1) {
  1713. X
  1714. X     case '%':
  1715. X    if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1716. X    if ((current_group->group_flag & G_FOLDER) == 0) {
  1717. X        if (!ah) {
  1718. X        prompt("\1READ\1");
  1719. X        if ((ah = get_menu_article()) == NULL) 
  1720. X            goto_return(ME_NO_REDRAW);
  1721. X        }
  1722. X        if ((ah->flag & A_DIGEST) == 0) {
  1723. X        *group_file_name = NUL;
  1724. X        sprintf(buffer, "%s%ld", group_path_name, ah->a_number);
  1725. X        answer = buffer;
  1726. X        goto get_folder;
  1727. X        }
  1728. X    }
  1729. X    
  1730. X    msg("cannot split articles inside a folder or digest");
  1731. X    goto_return(ME_NO_REDRAW);
  1732. X
  1733. X     case '.':
  1734. X     case '~':
  1735. X    strcat(buffer, "/");
  1736. X     case '+':
  1737. X     case '/':
  1738. X    if (!get_from_macro) {
  1739. X        prompt("\1Folder\1 ");
  1740. X        answer = get_s(NONE, buffer, NONE, file_completion);
  1741. X        if (answer == NULL || answer[0] == NUL) goto_return(ME_NO_REDRAW);
  1742. X    }
  1743. X    
  1744. X     get_folder:
  1745. X    m_endinput();
  1746. X    if (!expand_file_name(buffer, answer)) goto_return (ME_NO_REDRAW);
  1747. X    menu_cmd = folder_menu(buffer);
  1748. X    init_group(orig_group);
  1749. X    goto goto_exit;
  1750. X
  1751. X    
  1752. X     case 'a':
  1753. X    if (answer[1] == NUL || strcmp(answer, "all") == 0) {
  1754. X        if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1755. X        first = 0;
  1756. X        goto more_articles;
  1757. X    }
  1758. X    break;
  1759. X    
  1760. X     case 'u':
  1761. X    if (answer[1] == NUL || strcmp(answer, "unread") == 0) {
  1762. X        if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1763. X        first = gh->first_article + 1;
  1764. X        goto more_articles;
  1765. X    }
  1766. X    break;
  1767. X
  1768. X     case 's':
  1769. X    if (answer[1] == NUL) {
  1770. X        if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1771. X        goto get_subject;
  1772. X    }
  1773. X    
  1774. X    break;
  1775. X    
  1776. X     case '=':
  1777. X    if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1778. X    goto get_subject;
  1779. X    
  1780. X     case '0':
  1781. X     case '1':
  1782. X     case '2':
  1783. X     case '3':
  1784. X     case '4':
  1785. X     case '5':
  1786. X     case '6':
  1787. X     case '7':
  1788. X     case '8':
  1789. X     case '9':
  1790. X    if (current_group == NULL) goto_return(ME_NO_REDRAW);
  1791. X    if (current_first_article <= gh->first_l_article) {
  1792. X        msg("No extra articles");
  1793. X        flush_input();
  1794. X        goto_return(ME_NO_REDRAW);
  1795. X    }
  1796. X    
  1797. X    if (!get_from_macro) {
  1798. X        prompt("\1Number of extra articles\1 max %ld: ",
  1799. X           current_first_article - gh->first_l_article);
  1800. X        sprintf(buffer, "%c", ans1);
  1801. X        answer = get_s(NONE, buffer, NONE, NO_COMPLETION);
  1802. X        if (answer == NULL || *answer ==  NUL) goto_return(ME_NO_REDRAW);
  1803. X    }
  1804. X    
  1805. X    first = current_first_article - atol(answer);
  1806. X    goto more_articles;
  1807. X    
  1808. X     default:
  1809. X    break;
  1810. X    }
  1811. X
  1812. X    if ((gh = lookup(answer)) == NULL) {
  1813. X    msg("No group named %s", answer);
  1814. X    goto_return(ME_NO_REDRAW);
  1815. X    }
  1816. X
  1817. X
  1818. X get_first:
  1819. X
  1820. X    m_advinput();
  1821. X
  1822. X    if (gh->last_l_article == 0 ||
  1823. X    gh->last_l_article < gh->first_l_article) {
  1824. X    msg("Group %s is empty", answer);
  1825. X    goto_return(ME_NO_REDRAW);
  1826. X    }
  1827. X
  1828. X    if (gh == orig_group
  1829. X    && current_first_article <= gh->first_article) {
  1830. X
  1831. X    if (current_first_article <= gh->first_l_article) {
  1832. X        /* no more articles to read */
  1833. X        *answer = '=';
  1834. X        get_from_macro = 0;
  1835. X        goto get_subject;
  1836. X    }
  1837. X        
  1838. X    prompt("\1Number of extra articles\1 all %ld, =subject: ",
  1839. X           (long)(current_first_article - gh->first_l_article));
  1840. X
  1841. X    answer = "a=s";
  1842. X    } else {
  1843. X    if (gh != orig_group)
  1844. X        current_first_article = gh->last_l_article + 1;
  1845. X    
  1846. X        prompt("\1Number of%s articles\1 u)nread %ld,%s a)ll %ld, s)ubject: ",
  1847. X           gh == orig_group ? " extra" : "",
  1848. X           (long)(current_first_article - gh->first_article - 1),
  1849. X           (gh->group_flag & G_UNREAD_COUNT) ? " j)ump," : "",
  1850. X           (long)(current_first_article - gh->first_l_article));
  1851. X
  1852. X    answer = (gh->group_flag & G_UNREAD_COUNT) ? "uja=s" : "ua=s";
  1853. X    }
  1854. X    
  1855. X    answer = get_s(NONE, NONE, answer, NO_COMPLETION);
  1856. X    if (answer == NULL) goto_return(ME_NO_REDRAW);
  1857. X
  1858. Xget_subject:    /* when *answer == '=' */
  1859. X    
  1860. X    switch (*answer) {
  1861. X    
  1862. X     case NUL:
  1863. X    if (gh != orig_group || current_first_article <= gh->first_l_article) {
  1864. X        first = 0;
  1865. X        break;
  1866. X    }
  1867. X    /* else fall thru */
  1868. X    
  1869. X     case 'u':
  1870. X    first = gh->first_article + 1;
  1871. X    break;
  1872. X
  1873. X     case 'j':
  1874. X    jump_to_group = gh;
  1875. X    goto_return(ME_QUIT);
  1876. X    
  1877. X     case 'a':
  1878. X    first = 0;
  1879. X    break;
  1880. X    
  1881. X     case 's':
  1882. X     case '=':
  1883. X    first = 0;
  1884. X    if (get_from_macro) {
  1885. X        submask = answer + 1;
  1886. X    } else {
  1887. X        prompt("=");
  1888. X        submask = get_s(ah ? ah->subject : NONE, NONE, ah ? NONE : "%=",
  1889. X                NO_COMPLETION);
  1890. X        if (submask == NULL) goto_return(ME_NO_REDRAW);
  1891. X        if (*submask == '%' || *submask == '=') {
  1892. X        if ((ah = get_menu_article()) == 0) goto_return(ME_NO_REDRAW);
  1893. X        *submask = NUL;
  1894. X        }
  1895. X    }
  1896. X    
  1897. X    if (*submask == NUL)
  1898. X        if (ah) {
  1899. X        strncpy(submask, ah->subject, GET_S_BUFFER);
  1900. X        submask[GET_S_BUFFER-1] = NUL;
  1901. X        } else
  1902. X        goto_return(ME_NO_REDRAW);
  1903. X    
  1904. X    if (*submask) {
  1905. X        if (*submask != '/') init_quick_match(submask);
  1906. X    } else
  1907. X        submask = NULL;
  1908. X    break;
  1909. X    
  1910. X     case '0':
  1911. X     case '1':
  1912. X     case '2':
  1913. X     case '3':
  1914. X     case '4':
  1915. X     case '5':
  1916. X     case '6':
  1917. X     case '7':
  1918. X     case '8':
  1919. X     case '9':
  1920. X    first = current_first_article - atol(answer);
  1921. X    break;
  1922. X
  1923. X     default:
  1924. X    ding();
  1925. X    goto_return(ME_NO_REDRAW);
  1926. X    }
  1927. X    
  1928. X    if (first > gh->last_l_article) goto_return(ME_NO_REDRAW);
  1929. X
  1930. Xmore_articles:
  1931. X    if (gh == orig_group && submask == NULL && 
  1932. X    first < current_first_article) {
  1933. X    if (current_first_article <= gh->first_l_article) {
  1934. X        msg("No extra articles");
  1935. X        goto_return(ME_NO_REDRAW);
  1936. X    }
  1937. X    
  1938. X    if (access_group(gh, first, current_first_article - 1,
  1939. X                  ALSO_CROSS_POSTINGS, (char *)NULL, 0) < 0) {
  1940. X        msg("Cannot read extra articles (now)");
  1941. X        goto_return(ME_NO_REDRAW);
  1942. X    }
  1943. X
  1944. X    current_first_article = first;
  1945. X    
  1946. X    goto_return(ME_REDRAW);
  1947. X    }
  1948. X
  1949. X    mark_memory(&mem_marker);
  1950. X    m_endinput();
  1951. X    menu_cmd = group_menu(gh, first, submask, 0, menu);
  1952. X    release_memory(&mem_marker);
  1953. X
  1954. X    if (gh != orig_group) {
  1955. X    current_first_article = o_current_first;
  1956. X    if (orig_group) init_group(orig_group);
  1957. X    }
  1958. X    
  1959. Xgoto_exit:
  1960. X    m_endinput();
  1961. X    return menu_cmd;
  1962. X}
  1963. X
  1964. Xstatic merged_header()
  1965. X{
  1966. X    so_printxy(0, 0, "MERGED NEWS GROUPS:  %d ARTICLES", n_articles);
  1967. X    clrline();
  1968. X
  1969. X    return 1;
  1970. X}
  1971. X
  1972. Xmerge_and_read(submask, do_kill)
  1973. Xchar *submask;
  1974. Xint do_kill;
  1975. X{
  1976. X    register group_header *cur;
  1977. X    group_header dummy_group;
  1978. X    int access_mode;
  1979. X    
  1980. X    free_memory();
  1981. X
  1982. X    access_mode = DONT_SORT_ARTICLES;
  1983. X    if (also_read_articles || submask)
  1984. X    access_mode |= ALSO_CROSS_POSTINGS;
  1985. X    if (dont_split_digests)
  1986. X    access_mode |= DONT_SPLIT_DIGESTS;
  1987. X    
  1988. X    for (cur = group_sequence; cur != NULL; cur = cur->next_group) {
  1989. X    if (s_hangup || s_keyboard) break;
  1990. X
  1991. X    if (cur->group_flag & G_FOLDER) {
  1992. X        printf("\n\rIgnoring folder: %s\n\r", cur->group_name);
  1993. X        continue;
  1994. X    }
  1995. X
  1996. X    if (!also_read_articles)
  1997. X        if (cur->last_article >= cur->last_l_article)
  1998. X        continue;
  1999. X    
  2000. X    printf("\r%s", cur->group_name); clrline();
  2001. X    
  2002. X    access_group(cur, -1, cur->last_l_article, access_mode, submask, do_kill);
  2003. X    }
  2004. X    merge_memory();
  2005. X    if (n_articles == 0) return;
  2006. X    if (!dont_sort_articles) sort_articles();
  2007. X
  2008. X    dummy_group.group_flag = 0;
  2009. X    dummy_group.save_file = NULL;
  2010. X    dummy_group.group_name = "dummy";
  2011. X    dummy_group.kill_list = NULL;
  2012. X
  2013. X    current_group = &dummy_group;
  2014. X    
  2015. X    menu(merged_header);
  2016. X    
  2017. X    free_memory();
  2018. X}
  2019. X
  2020. Xunsubscribe(gh)
  2021. Xgroup_header *gh;
  2022. X{
  2023. X    if (no_update) {
  2024. X    msg("nn started in \"no update\" mode");
  2025. X    return 0;
  2026. X    }
  2027. X    
  2028. X    if (gh->group_flag & G_FOLDER) {
  2029. X    msg("cannot unsubscribe to a folder");
  2030. X    return 0;
  2031. X    }
  2032. X
  2033. X    if (gh->group_flag & G_SUBSCRIPTION) {
  2034. X    prompt("\1Unsubscribe to\1 %s ? ", gh->group_name);
  2035. X    if (yes(0) <= 0) return 0;
  2036. X
  2037. X    add_unread(gh, -1);
  2038. X    gh->group_flag &= ~G_SUBSCRIPTION;
  2039. X    write_rc_entry(gh, 0);
  2040. X    return 1;
  2041. X    }
  2042. X
  2043. X    prompt("Already unsubscribed.  \1Resubscribe to\1 %s ? ",
  2044. X       gh->group_name);
  2045. X    if (yes(1) <= 0) return 0;
  2046. X    
  2047. X    gh->group_flag |= G_SUBSCRIPTION;
  2048. X    write_rc_entry(gh, 0);
  2049. X
  2050. X    add_unread(gh, 1);
  2051. X
  2052. X    return 1;
  2053. X}
  2054. X
  2055. X
  2056. Xgroup_overview(amount)
  2057. Xint amount;    /* 0=>unread,subscribed, 1=>unread,all, 2=>all,all 3=>unsub*/
  2058. X{
  2059. X    register group_header *gh;
  2060. X    
  2061. X    clrdisp();
  2062. X
  2063. X    pg_init(0, 2);
  2064. X
  2065. X    Loop_Groups_Sorted(gh) {
  2066. X
  2067. X    if (gh->group_flag & G_NO_DIRECTORY) continue;
  2068. X
  2069. X    if (amount <= 1 && gh->last_article >= gh->last_l_article)
  2070. X        continue;
  2071. X
  2072. X    if (amount == 0 && (gh->group_flag & G_SUBSCRIPTION) == 0)
  2073. X        continue;
  2074. X
  2075. X    if (amount == 3 && (gh->group_flag & G_SUBSCRIPTION))
  2076. X        continue;
  2077. X
  2078. X    if (pg_next() < 0) break;
  2079. X
  2080. X    printf("%6ld %s%s",
  2081. X           (long)(gh->last_l_article - gh->last_article),
  2082. X           gh->group_name,
  2083. X           gh->group_flag & G_SUBSCRIPTION ? "" : " (!)");
  2084. X    }
  2085. X
  2086. X    pg_end();
  2087. X}
  2088. NO_NEWS_IS_GOOD_NEWS
  2089. chmod 0644 group.c || echo "restore of group.c fails"
  2090. set `wc -c group.c`;Sum=$1
  2091. if test "$Sum" != "22057"
  2092. then echo original size 22057, current size $Sum;fi
  2093. echo "x - extracting help.commands (Text)"
  2094. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.commands &&
  2095. X;:ACOMMAND NAMES;:A                        ;:AMAP COMMAND;:A
  2096. X
  2097. X;:BNAME        MENU    MORE    FUNCTION
  2098. Xadvance-group    A         advance one group in sequence
  2099. Xback-group    B         go back one group in sequence
  2100. Xcancel        C    C    cancel an article
  2101. Xcommand        :    :    extenced command prefix
  2102. Xcompress        c    compress text (eliminate extra spaces)
  2103. Xcontinue    SPACE    SPACE    the "space bar" command
  2104. Xfind            /    regular expression search
  2105. Xfind-next        .    repeat regular expression search
  2106. Xfollow        F     f F    follow up
  2107. Xfull-digest        H    show complete digest
  2108. Xgoto-group    G    G    goto group or open folder
  2109. Xgoto-menu        =    go back to menu
  2110. Xhelp        ?    ?    online help
  2111. Xkill-select    K    K    kill/select handling
  2112. Xlayout        L        change menu layout
  2113. Xline+1        down    CR    next menu line/scroll one line 
  2114. Xline-1        up        previous menu line
  2115. Xline=@            g    goto specific line
  2116. Xmail        M    m M    mail or forward
  2117. Xmessage        ^P    ^P    repeat last prompt line message
  2118. Xnext-article        n    skip to next article
  2119. Xnext-group    N        goto to next group without reading current
  2120. Xnext-subject        k    skip to next article with different subject
  2121. Xnil                unbound key
  2122. Xoverview    Y    Y    show groups with unread news
  2123. Xpage+1        >        goto next page if any
  2124. Xpage+1/2        d ^D    scroll half page forward
  2125. Xpage-1        <    DEL    goto one page back
  2126. Xpage-1/2        u ^U    scroll half page backwards
  2127. Xpage=$        $    $    goto end of menu/article
  2128. Xpage=0            h    goto header of article    
  2129. Xpage=1        ^    ^    goto first menu/article page
  2130. Xpage=@                goto specific page of article (not implemented)
  2131. Xpost                post new article
  2132. Xpreview        %        preview article
  2133. Xprevious    P    P    goto previous group/article
  2134. Xprint            p    print article
  2135. Xquit        Q    Q    quit nn
  2136. Xread-return    Z        read selected articles and return to menu
  2137. Xread-skip    X        read selected article, skip unseen menu pages
  2138. Xredraw        ^L ^R    ^L ^R    redraw screen
  2139. Xreply        R    r R    reply
  2140. Xrot13            D    decrypt rot13 article
  2141. Xsave-body    W    w W    save article without header
  2142. Xsave-full    S    s S    save article with full header
  2143. Xsave-short    O    o O    save article with short header
  2144. Xselect          .        select (or deselect) current menu entry
  2145. Xselect-auto     +        select "auto-selected" articles
  2146. Xselect-invert     @        invert all selections on current menu page
  2147. Xselect-range    -        select range of articles
  2148. Xselect-subject    *        select all articles with current subject
  2149. Xshell             !    !    shell command prefix
  2150. Xunselect-all      ~        unselect all articles
  2151. Xunshar                unshar article(s)
  2152. Xunsub             U    U    unsubscribe (or subscribe) to current group
  2153. Xversion           V    V    print release information
  2154. NO_NEWS_IS_GOOD_NEWS
  2155. chmod 0644 help.commands || echo "restore of help.commands fails"
  2156. set `wc -c help.commands`;Sum=$1
  2157. if test "$Sum" != "2312"
  2158. then echo original size 2312, current size $Sum;fi
  2159. echo "x - extracting help.extended (Text)"
  2160. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.extended &&
  2161. X;:AEXTENDED COMMANDS;:A
  2162. X
  2163. X:help COMMAND        give help on specific command
  2164. X
  2165. X:q!            quit nn without update (only with -B option)
  2166. X:x            quit nn, mark current group as read
  2167. X
  2168. X:mkdir [DIR]        create directory DIR (will prompt for DIR if omitted)
  2169. X:cd [DIR]        change working directory to DIR
  2170. X
  2171. X:admin            enter administration mode
  2172. X
  2173. X:set OPTION [VALUE]    set or unset option (use 'help set' for more info)
  2174. X:map MODE KEY COMMAND    remap key or command
  2175. X:show TABLE        show contents of various tables
  2176. X:sort MODE        sort menu according to subject, age, or arrival
  2177. X
  2178. X:unread (N)        mark current group as unread (last N articles)
  2179. X:coredump        abort with a core dump
  2180. NO_NEWS_IS_GOOD_NEWS
  2181. chmod 0644 help.extended || echo "restore of help.extended fails"
  2182. set `wc -c help.extended`;Sum=$1
  2183. if test "$Sum" != "626"
  2184. then echo original size 626, current size $Sum;fi
  2185. echo "x - extracting help.help (Text)"
  2186. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > help.help &&
  2187. X;:AHELP COMMAND;:A
  2188. X
  2189. XSynopsis
  2190. X
  2191. X    :help subject
  2192. X
  2193. XHelp is available on the the following subjects:
  2194. X
  2195. Xcommands    commands that can be bound to keys
  2196. Xextended    extended commands (:command)
  2197. Xmap        key mapping
  2198. Xset        variable setting
  2199. NO_NEWS_IS_GOOD_NEWS
  2200. echo "End of part 4"
  2201. echo "File help.help is continued in part 5"
  2202. echo "5" > s2_seq_.tmp
  2203. exit 0
  2204. ---
  2205. Kim F. Storm        storm@texas.dk        Tel +45 429 174 00
  2206. Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
  2207.       No news is good news, but nn is better!
  2208.  
  2209.