home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume31 / tin / part05 < prev    next >
Encoding:
Text File  |  1992-07-07  |  54.3 KB  |  2,448 lines

  1. Newsgroups: comp.sources.misc
  2. From: iain%anl433.uucp@Germany.EU.net (Iain Lea)
  3. Subject:  v31i005:  tin - threaded full screen newsreader v1.1 PL4, Part05/15
  4. Message-ID: <1992Jul7.181431.6697@sparky.imd.sterling.com>
  5. X-Md4-Signature: f124bfa5a160f466091f007ff4865b35
  6. Date: Tue, 7 Jul 1992 18:14:31 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: iain%anl433.uucp@Germany.EU.net (Iain Lea)
  10. Posting-number: Volume 31, Issue 5
  11. Archive-name: tin/part05
  12. Environment: BSD, SCO, ISC, SUNOS, SYSVR3, SYSVR4, ULTRIX, XENIX
  13. Supersedes: tin: Volume 30, Issue 1-14
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then feed it
  17. # into a shell via "sh file" or similar.  To overwrite existing files,
  18. # type "sh file -c".
  19. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  20. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  21. # Contents:  group.c patchlev.h thread.c
  22. # Wrapped by kent@sparky on Mon Jun 29 23:35:11 1992
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. echo If this archive is complete, you will see the following message:
  25. echo '          "shar: End of archive 5 (of 15)."'
  26. if test -f 'group.c' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'group.c'\"
  28. else
  29.   echo shar: Extracting \"'group.c'\" \(29136 characters\)
  30.   sed "s/^X//" >'group.c' <<'END_OF_FILE'
  31. X/*
  32. X *  Project   : tin - a threaded Netnews reader
  33. X *  Module    : group.c
  34. X *  Author    : I.Lea & R.Skrenta
  35. X *  Created   : 01-04-91
  36. X *  Updated   : 20-06-92
  37. X *  Notes     :
  38. X *  Copyright : (c) Copyright 1991-92 by Iain Lea & Rich Skrenta
  39. X *              You may  freely  copy or  redistribute  this software,
  40. X *              so  long as there is no profit made from its use, sale
  41. X *              trade or  reproduction.  You may not change this copy-
  42. X *              right notice, and it must be included in any copy made
  43. X */
  44. X
  45. X#include    "tin.h"
  46. X
  47. X#define MARK_OFFSET    8
  48. X
  49. Xextern char cvers[LEN];
  50. Xextern int cur_groupnum;
  51. Xextern int last_resp;        /* page.c */
  52. Xextern int this_resp;        /* page.c */
  53. Xextern int note_page;        /* page.c */
  54. X
  55. Xchar *glob_group;
  56. Xint index_point;
  57. Xint first_subj_on_screen;
  58. Xint last_subj_on_screen;
  59. Xstatic int len_from;
  60. Xstatic int len_subj;
  61. Xstatic char *spaces = "XXXX";
  62. X
  63. Xstatic int bld_sline();
  64. Xstatic int draw_sline();
  65. X
  66. X#ifndef ART_ADJUST    /* what we do here is bizarre */
  67. X#define ART_ADJUST(n)    (show_only_unread \
  68. X                ? ((n) > 1 ? (n) : 0) \
  69. X                : ((n) > 0 ? (n) - 1 : 0))
  70. X#endif
  71. X
  72. X#define INDEX2SNUM(i)    ((i) % NOTESLINES)
  73. X#define SNUM2LNUM(i)    (INDEX_TOP + (i))
  74. X#define INDEX2LNUM(i)    (SNUM2LNUM(INDEX2SNUM(i)))
  75. X
  76. Xvoid group_page (group)
  77. X    char *group;
  78. X{
  79. X#ifndef INDEX_DAEMON
  80. X
  81. X    char group_path[LEN];
  82. X     char buf[128];
  83. X     char pat[128];
  84. X    char *p;
  85. X    int ch;
  86. X    int dummy = 0;
  87. X    int flag, i, n;
  88. X    int kill_state;
  89. X    int old_top = 0;
  90. X    int posted;
  91. X    int sav_groupnum;
  92. X    int scroll_lines;
  93. X    long old_artnum = 0L;
  94. X    int xflag = 0;
  95. X     struct art_stat_t sbuf;
  96. X
  97. X    active[my_group[cur_groupnum]].attribute.read = TRUE;
  98. X
  99. X    glob_group = group;
  100. X    sav_groupnum = cur_groupnum;
  101. X    
  102. X    strcpy (group_path, group);            /* turn comp.unix.amiga into */
  103. X    for (p = group_path; *p; p++)        /* comp/unix/amiga */
  104. X        if (*p == '.')
  105. X            *p = '/';
  106. X
  107. X    last_resp = -1;
  108. X    this_resp = -1;
  109. X    index_group (group, group_path);    /* update index file */
  110. X    
  111. X    if (space_mode) {
  112. X        for (i = 0; i < top_base; i++) {
  113. X            if (new_responses (i)) {
  114. X                break;
  115. X            }
  116. X        }
  117. X        if (i < top_base) {
  118. X            index_point = i;
  119. X        } else {
  120. X            index_point = top_base - 1;
  121. X        }
  122. X    } else {
  123. X        index_point = top_base - 1;
  124. X    }
  125. X    if (index_point < 0) {
  126. X        index_point = 0;
  127. X    }
  128. X    
  129. X    clear_note_area ();
  130. X
  131. X    show_group_page (group);
  132. X
  133. X    while (TRUE) {
  134. X        ch = ReadCh ();
  135. X
  136. X        if (ch > '0' && ch <= '9') {    /* 0 goes to basenote */
  137. X            prompt_subject_num (ch, group);
  138. X            continue;
  139. X        } 
  140. X        switch (ch) {
  141. X            case ESC:    /* common arrow keys */
  142. X                switch (get_arrow_key ()) {
  143. X                case KEYMAP_UP:
  144. X                    goto group_up;
  145. X
  146. X                case KEYMAP_DOWN:
  147. X                    goto group_down;
  148. X
  149. X                case KEYMAP_PAGE_UP:
  150. X                    goto group_page_up;
  151. X
  152. X                case KEYMAP_PAGE_DOWN:
  153. X                    goto group_page_down;
  154. X
  155. X                case KEYMAP_HOME:
  156. X                    if (! top_base) {
  157. X                        break;
  158. X                    }            
  159. X                    if (index_point != 0) {
  160. X                        if (0 < first_subj_on_screen) {
  161. X#ifndef USE_CLEARSCREEN
  162. X                            erase_subject_arrow ();
  163. X#endif                    
  164. X                            index_point = 0;
  165. X                            show_group_page (group);
  166. X                        } else {
  167. X                            erase_subject_arrow ();
  168. X                            index_point = 0;
  169. X                            draw_subject_arrow ();
  170. X                        }
  171. X                    }
  172. X                    break;
  173. X                    
  174. X                case KEYMAP_END:
  175. X                    goto end_of_list;
  176. X                }
  177. X                break;
  178. X
  179. X#ifndef NO_SHELL_ESCAPE
  180. X            case '!':
  181. X                shell_escape ();
  182. X                show_group_page (group);
  183. X                break;
  184. X#endif
  185. X
  186. X            case '$':    /* show last page of articles */
  187. Xend_of_list:
  188. X                if (! top_base) {
  189. X                    break;
  190. X                }            
  191. X                if (index_point != top_base - 1) {
  192. X                    if (top_base - 1 > last_subj_on_screen) {
  193. X#ifndef USE_CLEARSCREEN
  194. X                        erase_subject_arrow ();
  195. X#endif                    
  196. X                        index_point = top_base - 1;
  197. X                        show_group_page (group);
  198. X                    } else {
  199. X                        erase_subject_arrow ();
  200. X                        index_point = top_base - 1;
  201. X                        draw_subject_arrow ();
  202. X                    }
  203. X                }
  204. X                break;
  205. X                
  206. X            case '-':    /* go to last viewed article */
  207. X                if (this_resp < 0) {
  208. X                    info_message (txt_no_last_message);
  209. X                    break;
  210. X                }
  211. X                index_point = show_page (this_resp, &dummy, group, group_path);
  212. X                if (index_point < 0) {
  213. X                    space_mode = FALSE;
  214. X                    goto group_done;
  215. X                }
  216. X                clear_note_area ();
  217. X                show_group_page (group);
  218. X                break;
  219. X
  220. X            case '|':    /* pipe article/thread/tagged arts to command */
  221. X                if (index_point >= 0) {
  222. X                    feed_articles (FEED_PIPE, GROUP_LEVEL, "Pipe",
  223. X                        (int) base[index_point], group_path);
  224. X                }
  225. X                break;
  226. X
  227. X            case '/':    /* forward/backward search */
  228. X            case '?':
  229. X                i = (ch == '/');
  230. X                search_subject (i, group);
  231. X                break;
  232. X
  233. X            case '\r':
  234. X            case '\n':    /* read current basenote */
  235. X                if (index_point < 0) {
  236. X                    info_message(txt_no_arts);
  237. X                    break;
  238. X                }
  239. X                i = (int) base[index_point];
  240. X                index_point = show_page (i, &dummy, group, group_path);
  241. X                if (index_point < 0) {
  242. X                    space_mode = FALSE;
  243. X                    goto group_done;
  244. X                }
  245. X                clear_note_area ();
  246. X                show_group_page (group);
  247. X                break;
  248. X
  249. X            case '\t':
  250. X                 space_mode = TRUE;
  251. X
  252. X                if (index_point < 0
  253. X                || (n=next_unread((int) base[index_point]))<0) {
  254. X                    for (i = cur_groupnum+1 ; i < group_top ; i++)
  255. X                        if (unread[i] > 0)
  256. X                            break;
  257. X                    if (i >= group_top)
  258. X                        goto group_done;
  259. X
  260. X                    cur_groupnum = i;
  261. X                    index_point = -3;
  262. X                    goto group_done;
  263. X                }
  264. X                index_point = show_page(n, &dummy, group, group_path);
  265. X                if (index_point < 0)
  266. X                    goto group_done;
  267. X                clear_note_area ();
  268. X                show_group_page(group);
  269. X                break;
  270. X    
  271. X            case ' ':        /* page down */
  272. X            case ctrl('D'):
  273. X            case ctrl('F'):        /* vi style */
  274. Xgroup_page_down:
  275. X                if (! top_base) {
  276. X                    break;
  277. X                }
  278. X                if (index_point == top_base - 1) {
  279. X#ifdef NO_LOOP_AROUND
  280. X                    break;
  281. X#else
  282. X                    if (0 < first_subj_on_screen) {
  283. X#    ifndef USE_CLEARSCREEN
  284. X                        erase_subject_arrow ();
  285. X#    endif                    
  286. X                        index_point = 0;
  287. X                        show_group_page (group);
  288. X                    } else {
  289. X                        erase_subject_arrow ();
  290. X                        index_point = 0;
  291. X                        draw_subject_arrow ();
  292. X                    }
  293. X                    break;
  294. X#endif                    
  295. X                }
  296. X                erase_subject_arrow ();
  297. X                scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
  298. X                index_point = ((index_point + scroll_lines) / scroll_lines) * scroll_lines;
  299. X                if (index_point >= top_base) {
  300. X                    index_point = (top_base / scroll_lines) * scroll_lines;
  301. X                    if (index_point < top_base - 1) {
  302. X                        index_point = top_base - 1;
  303. X                    }
  304. X                }
  305. X                if (index_point < first_subj_on_screen
  306. X                || index_point >= last_subj_on_screen)
  307. X                    show_group_page (group);
  308. X                else
  309. X                    draw_subject_arrow ();
  310. X                break;
  311. X
  312. X            case ctrl('K'):        /* kill article */
  313. X                 if (index_point < 0) {
  314. X                     info_message (txt_no_arts);
  315. X                    break;
  316. X                }
  317. X                old_top = top;
  318. X                n = (int) base[index_point];
  319. X                old_artnum = arts[n].artnum;
  320. X                if (kill_art_menu (group, (int) base[index_point])) {
  321. X                    kill_any_articles (group);
  322. X                    make_threads (FALSE);
  323. X                    find_base (show_only_unread);
  324. X                    index_point = find_new_pos (old_top, old_artnum, index_point);
  325. X                }
  326. X                show_group_page (group);
  327. X                break;
  328. X
  329. X            case ctrl('L'):        /* redraw screen */
  330. X            case ctrl('R'):
  331. X            case ctrl('W'):
  332. X#ifndef USE_CLEARSCREEN
  333. X                ClearScreen ();
  334. X#endif
  335. X                show_group_page (group);
  336. X                break;
  337. X
  338. X            case ctrl('N'):
  339. X            case 'j':        /* line down */
  340. Xgroup_down:
  341. X                if (! top_base) {
  342. X                    break;
  343. X                }
  344. X                if (index_point + 1 >= top_base) {
  345. X#ifdef NO_LOOP_AROUND
  346. X                    break;
  347. X#else
  348. X                    if (0 < first_subj_on_screen) {
  349. X                        index_point = 0;
  350. X                        show_group_page (group);
  351. X                    } else {
  352. X                        erase_subject_arrow ();
  353. X                        index_point = 0;
  354. X                        draw_subject_arrow ();
  355. X                    }
  356. X                    break;
  357. X#endif                    
  358. X                }
  359. X                if (index_point + 1 >= last_subj_on_screen) {
  360. X#ifndef USE_CLEARSCREEN
  361. X                    erase_subject_arrow();
  362. X#endif                    
  363. X                    index_point++;
  364. X                    show_group_page(group);
  365. X                } else {
  366. X                    erase_subject_arrow();
  367. X                    index_point++;
  368. X                    draw_subject_arrow();
  369. X                }
  370. X                break;
  371. X
  372. X            case ctrl('P'):
  373. X            case 'k':        /* line up */
  374. Xgroup_up:
  375. X                if (! top_base) {
  376. X                    break;
  377. X                }    
  378. X                if (index_point == 0) {
  379. X#ifdef NO_LOOP_AROUND
  380. X                    break;
  381. X#else
  382. X                    if (top_base > last_subj_on_screen) {
  383. X                        index_point = top_base - 1;
  384. X                        show_group_page (group);
  385. X                    } else {
  386. X                        erase_subject_arrow ();
  387. X                        index_point = top_base - 1;
  388. X                        draw_subject_arrow ();
  389. X                    }
  390. X                    break;
  391. X#endif                    
  392. X                }
  393. X                if (index_point <= first_subj_on_screen) {
  394. X                    index_point--;
  395. X                    show_group_page(group);
  396. X                } else {
  397. X                    erase_subject_arrow();
  398. X                    index_point--;
  399. X                    draw_subject_arrow();
  400. X                }
  401. X                break;
  402. X
  403. X            case 'b':        /* page up */
  404. X            case ctrl('U'):
  405. X            case ctrl('B'):        /* vi style */
  406. Xgroup_page_up:
  407. X                if (! top_base) {
  408. X                    break;
  409. X                }
  410. X                if (index_point == 0) {
  411. X#ifdef NO_LOOP_AROUND
  412. X                    break;
  413. X#else
  414. X                    if (top_base > last_subj_on_screen) {
  415. X                        index_point = top_base - 1;
  416. X                        show_group_page (group);
  417. X                    } else {
  418. X                        erase_subject_arrow ();
  419. X                        index_point = top_base - 1;
  420. X                        draw_subject_arrow ();
  421. X                    }
  422. X                    break;
  423. X#endif                    
  424. X                }
  425. X#ifndef USE_CLEARSCREEN
  426. X                clear_message ();
  427. X#endif
  428. X                erase_subject_arrow ();
  429. X                scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
  430. X                if ((n = index_point % scroll_lines) > 0) {
  431. X                    index_point = index_point - n;
  432. X                } else {
  433. X                    index_point = ((index_point - scroll_lines) / scroll_lines) * scroll_lines;
  434. X                }
  435. X                if (index_point < 0) {
  436. X                    index_point = 0;
  437. X                }
  438. X                if (index_point < first_subj_on_screen
  439. X                || index_point >= last_subj_on_screen)
  440. X                    show_group_page (group);
  441. X                else
  442. X                    draw_subject_arrow ();
  443. X                break;
  444. X
  445. X            case 'a':    /* author search forward */
  446. X            case 'A':    /* author search backward */
  447. X                if (index_point < 0) {
  448. X                    info_message (txt_no_arts);
  449. X                    break;
  450. X                }
  451. X
  452. X                i = (ch == 'a');
  453. X
  454. X                n = search_author(show_only_unread, (int) base[index_point], i);
  455. X                if (n < 0)
  456. X                    break;
  457. X
  458. X                index_point = show_page (n, &dummy, group, group_path);
  459. X                if (index_point < 0) {
  460. X                    space_mode = FALSE;
  461. X                    goto group_done;
  462. X                }
  463. X                clear_note_area ();
  464. X                show_group_page (group);
  465. X                break;
  466. X
  467. X            case 'B':    /* bug/gripe/comment mailed to author */
  468. X                mail_bug_report ();
  469. X#ifndef USE_CLEARSCREEN
  470. X                ClearScreen ();
  471. X#endif
  472. X                show_group_page (group);
  473. X                break;
  474. X                
  475. X            case 'c':    /* catchup - mark all articles as read */
  476. X                if (!confirm_action || prompt_yn (LINES, txt_mark_all_read, 'y')) {
  477. X                    for (n = 0; n < top; n++) {
  478. X                        arts[n].unread = ART_READ;
  479. X                    }
  480. X                    if (cur_groupnum + 1 < group_top) {
  481. X                        cur_groupnum++;
  482. X                    }
  483. X                    goto group_done;
  484. X                }
  485. X                break;
  486. X
  487. X            case 'd':    /* toggle display of subject & subj/author */
  488. X                toggle_subject_from ();
  489. X                show_group_page (group);
  490. X                break;
  491. X
  492. X            case 'g':    /* choose a new group by name */
  493. X                n = choose_new_group ();
  494. X                if (n >= 0 && n != cur_groupnum) {
  495. X                    cur_groupnum = n;
  496. X                    index_point = -3;
  497. X                    goto group_done;
  498. X                }
  499. X                break;
  500. X
  501. X            case 'h':    /* help */
  502. X                show_info_page (HELP_INFO, help_group, txt_index_page_com);
  503. X                show_group_page (group);
  504. X                break;
  505. X
  506. X            case 'I':    /* toggle inverse video */
  507. X                toggle_inverse_video ();
  508. X                show_group_page (group);
  509. X                break;
  510. X
  511. X            case 'K':    /* mark rest of thread as read */
  512. X                if (index_point < 0) {
  513. X                    info_message (txt_no_next_unread_art);
  514. X                    break;
  515. X                }
  516. X                for (i = (int) base[index_point]; i >= 0; i = arts[i].thread) {
  517. X                    arts[i].unread = ART_READ;
  518. X                }    
  519. X                bld_sline(index_point);
  520. X                draw_sline(index_point, FALSE);
  521. X                if (index_point + 1 < top_base)
  522. X                    goto group_down;
  523. X                draw_subject_arrow();
  524. X                info_message(txt_end_of_arts);
  525. X                break;
  526. X
  527. X            case 'l':    /* list articles within current thread */
  528. X                if (index_point < 0) {
  529. X                    info_message (txt_no_arts);
  530. X                    break;
  531. X                }
  532. X                 space_mode = TRUE;
  533. X                n = show_thread ((int) base[index_point], group, group_path);
  534. X                if (n == -2) {
  535. X                    index_point = n;
  536. X                    space_mode = FALSE;
  537. X                    goto group_done;
  538. X                } else {
  539. X                    if (index_point < 0) {
  540. X                        space_mode = FALSE;
  541. X                        goto group_done;
  542. X                    }
  543. X                    clear_note_area ();
  544. X                    show_group_page (group);
  545. X                }
  546. X                break;    
  547. X
  548. X            case 'm':    /* mail article to somebody */
  549. X                if (index_point >= 0) {
  550. X                    feed_articles (FEED_MAIL, GROUP_LEVEL, "Mail",
  551. X                        (int) base[index_point], group_path);
  552. X                }
  553. X                break;
  554. X
  555. X            case 'M':    /* options menu */
  556. X                if (top_base > 0) {
  557. X                    old_top = top;
  558. X                    n = (int) base[index_point];
  559. X                    old_artnum = arts[n].artnum;
  560. X                }
  561. X                n = sort_art_type;
  562. X                kill_state = change_rcfile (group, TRUE);
  563. X                if (kill_state == NO_KILLING && n != sort_art_type) {
  564. X                    make_threads (TRUE);
  565. X                    find_base (show_only_unread);
  566. X                }
  567. X                index_point = find_new_pos (old_top, old_artnum, index_point);
  568. X                show_group_page (group);
  569. X                break;
  570. X
  571. X            case 'n':    /* next group */
  572. X                clear_message();
  573. X                if (cur_groupnum + 1 >= group_top)
  574. X                    info_message(txt_no_more_groups);
  575. X                else {
  576. X                    cur_groupnum++;
  577. X                    index_point = -3;
  578. X                    space_mode = pos_first_unread;
  579. X                    goto group_done;
  580. X                }
  581. X                break;
  582. X
  583. X            case 'N':    /* go to next unread article */
  584. X                if (index_point < 0) {
  585. X                    info_message(txt_no_next_unread_art);
  586. X                    break;
  587. X                }
  588. X
  589. X                n = next_unread ((int) base[index_point]);
  590. X                if (n == -1)
  591. X                    info_message (txt_no_next_unread_art);
  592. X                else {
  593. X                    index_point = show_page (n, &dummy, group, group_path);
  594. X                    if (index_point < 0) {
  595. X                        space_mode = pos_first_unread;
  596. X                        goto group_done;
  597. X                    }
  598. X                    clear_note_area ();
  599. X                    show_group_page (group);
  600. X                }
  601. X                break;
  602. X
  603. X            case 'o':    /* output art/thread/tagged arts to printer */
  604. X                if (index_point >= 0) {
  605. X                    feed_articles (FEED_PRINT, GROUP_LEVEL, "Print",
  606. X                        (int) base[index_point], group_path);
  607. X                }
  608. X                break;
  609. X
  610. X            case 'p':    /* previous group */
  611. X                clear_message();
  612. X                if (cur_groupnum <= 0)
  613. X                    info_message(txt_no_prev_group);
  614. X                else {
  615. X                    cur_groupnum--;
  616. X                    index_point = -3;
  617. X                    space_mode = pos_first_unread;
  618. X                    goto group_done;
  619. X                }
  620. X                break;
  621. X
  622. X            case 'P':    /* go to previous unread article */
  623. X                if (index_point < 0) {
  624. X                    info_message(txt_no_prev_unread_art);
  625. X                    break;
  626. X                }
  627. X                n = prev_response ((int) base[index_point]);
  628. X                n = prev_unread (n);
  629. X                if (n == -1)
  630. X                    info_message(txt_no_prev_unread_art);
  631. X                else {
  632. X                    index_point = show_page (n, &dummy, group, group_path);
  633. X                    if (index_point < 0) {
  634. X                        space_mode = pos_first_unread;
  635. X                        goto group_done;
  636. X                    }
  637. X                    clear_note_area ();
  638. X                    show_group_page (group);
  639. X                }
  640. X                break;
  641. X
  642. X            case 'q':    /* return to group selection page */
  643. X            case 'i':
  644. X                goto group_done;
  645. X
  646. X            case 'Q':        /* quit */
  647. X                index_point = -2;
  648. X                space_mode = FALSE;
  649. X                goto group_done;
  650. X
  651. X             case 'r':        
  652. X                 /* 
  653. X                  * If in show_only_unread mode or there  are
  654. X                  * unread articles we know this thread  will
  655. X                  * exist after toggle. Otherwise we find the
  656. X                  * next closest 
  657. X                  */
  658. X                 i = -1;
  659. X                 if (index_point >= 0) {
  660. X                     if (show_only_unread || new_responses(index_point))
  661. X                         i = base[index_point];
  662. X                     else if ((n = prev_unread(base[index_point])) >= 0)
  663. X                         i = n;
  664. X                     else if ((n = next_unread(base[index_point])) >= 0)
  665. X                         i = n;
  666. X                 }
  667. X                 show_only_unread = (show_only_unread ? FALSE : TRUE);
  668. X                 auto_select_articles (group);
  669. X                 find_base (show_only_unread);
  670. X                 if (i >= 0 && (n = which_thread(i)) >= 0)
  671. X                     index_point = n;
  672. X                 else if (top_base > 0)
  673. X                     index_point = top_base - 1;
  674. X                 show_group_page (group);
  675. X                 break;
  676. X
  677. X            case 's':    /* save regex pattern to file/s */
  678. X                if (index_point >= 0) {
  679. X                    feed_articles (FEED_SAVE, GROUP_LEVEL, "Save",
  680. X                        (int) base[index_point], group_path);
  681. X                }
  682. X                break;
  683. X            
  684. X            case 'T':    /* tag/untag art for mailing/piping/printing/saving */
  685. X                 if (index_point < 0)
  686. X                    break;
  687. X                n = (int) base[index_point];
  688. X                if (arts[n].tagged)
  689. X                    arts[n].tagged = 0;
  690. X                else
  691. X                    arts[n].tagged = ++num_of_tagged_arts;
  692. X                info_message (arts[n].tagged == 0 
  693. X                          ? txt_untagged_art 
  694. X                          : txt_tagged_art);
  695. X                bld_sline(index_point);
  696. X                draw_sline(index_point, FALSE);
  697. X                if (index_point + 1 < top_base)
  698. X                    goto group_down;
  699. X                draw_subject_arrow();
  700. X                break;
  701. X
  702. X            case 'u':    /* unthread/thread articles */
  703. X                 if (index_point >= 0) {
  704. X                    thread_arts = !thread_arts;
  705. X                    make_threads (TRUE);
  706. X                    find_base (show_only_unread);
  707. X                    show_group_page (group);
  708. X                }
  709. X                break;
  710. X
  711. X            case 'U':    /* untag all articles */
  712. X                 if (index_point >= 0) {
  713. X                    untag_all_articles ();
  714. X                    update_group_page (group);
  715. X                }
  716. X                break;
  717. X
  718. X            case 'v':
  719. X                info_message (cvers);
  720. X                break;
  721. X
  722. X            case 'w':    /* post a basenote */
  723. X                if (! can_post) {
  724. X                    info_message (txt_cannot_post);
  725. X                    break;
  726. X                }
  727. X                if (post_base (group, &posted)) {
  728. X                    show_group_page (group);
  729. X                }
  730. X                break;
  731. X
  732. X            case 'W':    /* display messages posted by user */
  733. X                if (user_posted_messages ()) {
  734. X                    show_group_page (group);
  735. X                }
  736. X                break;
  737. X
  738. X            case 'x':    /* crosspost current article */
  739. X                if (index_point >= 0) {
  740. X                    feed_articles (FEED_XPOST, GROUP_LEVEL, "Crosspost",
  741. X                        (int) base[index_point], group_path);
  742. X                }
  743. X                break;
  744. X
  745. X            case 'z':    /* mark article as unread */
  746. X            case 'Z':    /* mark thread as unread */
  747. X                 if (index_point < 0) {
  748. X                     info_message (txt_no_arts);
  749. X                    break;
  750. X                }
  751. X                n = 0;
  752. X                for (i = (int) base[index_point] ; i != -1 ; i = arts[i].thread) {
  753. X                    arts[i].unread = ART_UNREAD;
  754. X                    ++n;
  755. X                    if (ch == 'z')
  756. X                        break;
  757. X                }
  758. X                assert (n > 0);
  759. X                bld_sline(index_point);
  760. X                draw_sline(index_point, FALSE);
  761. X                draw_subject_arrow();
  762. X                if (ch == 'z')
  763. X                    info_message (txt_art_marked_as_unread);
  764. X                else
  765. X                    info_message (txt_thread_marked_as_unread);
  766. X                break;
  767. X
  768. X            case '*':    /* mark thread as selected */
  769. X            case '.':    /* toggle thread */
  770. X                 if (index_point < 0) {
  771. X                     info_message (txt_no_arts);
  772. X                    break;
  773. X                }
  774. X                
  775. X                flag = 1;
  776. X                if (ch == '.') {
  777. X                    stat_thread(index_point, &sbuf);
  778. X                    if (sbuf.hot_unread == sbuf.unread)
  779. X                        flag = 0;
  780. X                }
  781. X                n = 0;
  782. X                for (i = (int) base[index_point] ; i != -1 ; i = arts[i].thread) {
  783. X                    arts[i].hot = flag;
  784. X                    ++n;
  785. X                }
  786. X                assert (n > 0);
  787. X                bld_sline(index_point);
  788. X                draw_sline(index_point, FALSE);
  789. X#if 0
  790. X                info_message ( flag
  791. X                          ? txt_thread_marked_as_selected
  792. X                          : txt_thread_marked_as_deselected);
  793. X#endif
  794. X                if (index_point + 1 < top_base)
  795. X                    goto group_down;
  796. X                draw_subject_arrow ();
  797. X                break;
  798. X
  799. X            case '@':    /* reverse selections */
  800. X                for (i=0; i<top; ++i)
  801. X                    arts[i].hot = (arts[i].hot ? 0 : 1);
  802. X                update_group_page (group);
  803. X                break;
  804. X
  805. X              case '~':    /* undo selections */
  806. X                 for (i=0; i<top; ++i) {
  807. X                      arts[i].hot = 0;
  808. X                     arts[i].zombie = 0;
  809. X                 }
  810. X                 xflag = 0;
  811. X                  update_group_page(group);
  812. X                  break;
  813. X  
  814. X             case '=':    /* select matching patterns */
  815. X                 sprintf (msg, txt_select_pattern, default_select_pattern);
  816. X                 if (! prompt_string (msg, buf)) {
  817. X                     break;
  818. X                 }    
  819. X                 if (strcmp (buf, "") == 0) {
  820. X                     if (default_select_pattern[0] == '\0') {
  821. X                         info_message ("No previous expression");
  822. X                         break;
  823. X                     }
  824. X                     strcpy (pat, default_select_pattern);
  825. X                 } else if (strcmp (buf, "*") == 0) { /* all */
  826. X                     strcpy (pat, buf);
  827. X                     strcpy (default_select_pattern, pat);
  828. X                 } else {
  829. X                     make_lower (buf, buf);
  830. X                     strcpy (default_select_pattern, buf);
  831. X                     sprintf (pat, "*%s*", default_select_pattern);
  832. X                 }
  833. X                 flag = 0;
  834. X                 for (n=0; n < top_base; ++n) {
  835. X                     char sub[LEN];
  836. X                     make_lower (arts[base[n]].subject, sub);
  837. X                     if (wildmat (sub, pat) == 0)
  838. X                         continue;
  839. X                     for (i = (int) base[n] ; i != -1 ; i = arts[i].thread) {
  840. X                         arts[i].hot = 1;
  841. X                     }
  842. X                     bld_sline(n);
  843. X                     ++flag;
  844. X                 }
  845. X                 if (flag)
  846. X                     update_group_page (group);
  847. X                 break;
  848. X
  849. X            case ';':    /* make all unread hot if 1 is hot */
  850. X                for (n=0; n<top_base; ++n) {
  851. X                    stat_thread(n, &sbuf);
  852. X                    if (sbuf.hot_unread == 0
  853. X                        || sbuf.hot_unread == sbuf.unread)
  854. X                        continue;
  855. X                    for (i = (int) base[n] ; i != -1 ; i = arts[i].thread) {
  856. X                        arts[i].hot = 1;
  857. X                    }
  858. X                }
  859. X                /* no screen update needed */
  860. X                break;
  861. X
  862. X            case 'X':    /* mark read all unselected arts */
  863. X                if (xflag)
  864. X                    goto X_undo;
  865. X
  866. X                for (i=0; i<top; ++i) {
  867. X                    if (arts[i].unread != ART_UNREAD)
  868. X                        continue;
  869. X                    if (arts[i].hot != 1) {
  870. X                        arts[i].unread = ART_READ;
  871. X                        arts[i].zombie = 1;
  872. X                    }
  873. X                }
  874. X
  875. X                if (show_only_unread) {
  876. X                    find_base (show_only_unread);
  877. X                }
  878. X
  879. X                xflag = 1;
  880. X                index_point = 0;
  881. X                 show_group_page (group);
  882. X
  883. X                break;
  884. X
  885. X            case '+':    /* perform auto-selection on group */
  886. X                if (auto_select_articles (group) == 0)
  887. X                    break;
  888. X                update_group_page (group);
  889. X                break;
  890. X
  891. X            X_undo:
  892. X                for (i=0; i<top; ++i) {
  893. X                    if (arts[i].unread == ART_READ
  894. X                        && arts[i].zombie == 1) {
  895. X                        arts[i].unread = ART_UNREAD;
  896. X                        arts[i].zombie = 0;
  897. X                    }
  898. X                }
  899. X
  900. X                if (show_only_unread) {
  901. X                    find_base (show_only_unread);
  902. X                }
  903. X
  904. X                xflag = 0;
  905. X                index_point = 0;    /* do we want this ? */
  906. X                 show_group_page (group);
  907. X
  908. X                break;
  909. X                
  910. X
  911. X
  912. X            default:
  913. X                info_message (txt_bad_command);
  914. X        }
  915. X    }
  916. X
  917. Xgroup_done:
  918. X    fix_new_highest (sav_groupnum);
  919. X    update_newsrc (group, my_group[sav_groupnum], FALSE);
  920. X
  921. X    if (index_point == -2) {
  922. X        write_rcfile ();
  923. X        tin_done (0);
  924. X    }    
  925. X    clear_note_area ();
  926. X
  927. X#endif /* INDEX_DAEMON */
  928. X}
  929. X
  930. X
  931. X/*
  932. X *  Correct highest[] for the group selection page display since
  933. X *  new articles may have been read or marked unread
  934. X */
  935. X
  936. Xvoid fix_new_highest (groupnum)
  937. X    int groupnum;
  938. X{
  939. X    register int i;
  940. X    int sum = 0;
  941. X
  942. X    for (i = 0; i < top; i++) {
  943. X        if (arts[i].unread) {
  944. X            sum++;
  945. X        }
  946. X    }
  947. X    
  948. X    unread[groupnum] = sum;
  949. X}
  950. X
  951. X
  952. Xvoid show_group_page (group)
  953. X    char *group;
  954. X{
  955. X#ifndef INDEX_DAEMON
  956. X
  957. X    char buf[LEN];
  958. X    int i, art_cnt = 0;
  959. X
  960. X    set_signals_group ();
  961. X    
  962. X#ifdef USE_CLEARSCREEN
  963. X    ClearScreen ();
  964. X#else
  965. X    MoveCursor (0, 0);
  966. X    CleartoEOLN ();
  967. X#endif
  968. X
  969. X    if (show_only_unread) {
  970. X        for (i = 0 ; i < top_base ; i++) {
  971. X            art_cnt += new_responses (i);
  972. X        }    
  973. X    } else {
  974. X        for (i = 0 ; i < top ; i++) {
  975. X            if (! IGNORE_ART(i)) {
  976. X                ++art_cnt;
  977. X            }
  978. X        }        
  979. X    }
  980. X
  981. X    if (active[my_group[cur_groupnum]].attribute.thread && thread_arts) {
  982. X        sprintf (buf, "%s (%dT %dA %dK %dH)", group, top_base, art_cnt,
  983. X            num_of_killed_arts, num_of_hot_arts);
  984. X    } else {
  985. X        sprintf (buf, "%s (%dU %dK %dH)", group, art_cnt, 
  986. X            num_of_killed_arts, num_of_hot_arts);
  987. X    }
  988. X    show_title (buf);
  989. X
  990. X#ifndef USE_CLEARSCREEN
  991. X    MoveCursor (1, 0);
  992. X    CleartoEOLN ();
  993. X#endif
  994. X
  995. X    MoveCursor (INDEX_TOP, 0);
  996. X
  997. X    if (index_point >= top_base) {
  998. X        index_point = top_base - 1;
  999. X    }
  1000. X
  1001. X    if (NOTESLINES <= 0) {
  1002. X        first_subj_on_screen = 0;
  1003. X    } else {
  1004. X        first_subj_on_screen = (index_point / NOTESLINES) * NOTESLINES;
  1005. X        if (first_subj_on_screen < 0) {
  1006. X            first_subj_on_screen = 0;
  1007. X        }
  1008. X    }
  1009. X
  1010. X    last_subj_on_screen = first_subj_on_screen + NOTESLINES;
  1011. X
  1012. X    if (last_subj_on_screen >= top_base) {
  1013. X        last_subj_on_screen = top_base;
  1014. X        first_subj_on_screen = (top_base / NOTESLINES) * NOTESLINES;
  1015. X
  1016. X        if (first_subj_on_screen == last_subj_on_screen ||
  1017. X            first_subj_on_screen < 0) {
  1018. X            if (first_subj_on_screen < 0) {
  1019. X                first_subj_on_screen = 0;
  1020. X            } else {
  1021. X                first_subj_on_screen = last_subj_on_screen - NOTESLINES;
  1022. X            }
  1023. X        }
  1024. X    }
  1025. X
  1026. X    if (top_base == 0) {
  1027. X        first_subj_on_screen = 0;
  1028. X        last_subj_on_screen = 0;
  1029. X    }
  1030. X
  1031. X    if (draw_arrow_mark) {
  1032. X        CleartoEOS ();
  1033. X    }
  1034. X
  1035. X    for (i = first_subj_on_screen; i < last_subj_on_screen; ++i) {
  1036. X        bld_sline(i);
  1037. X        draw_sline(i, TRUE);
  1038. X    }
  1039. X
  1040. X#ifndef USE_CLEARSCREEN
  1041. X    CleartoEOS ();
  1042. X#endif
  1043. X
  1044. X    if (top_base <= 0) {
  1045. X        info_message(txt_no_arts);
  1046. X        return;
  1047. X    } else if (last_subj_on_screen == top_base) {
  1048. X        info_message(txt_end_of_arts);
  1049. X    }
  1050. X
  1051. X    draw_subject_arrow();
  1052. X
  1053. X#endif /* INDEX_DAEMON */
  1054. X}
  1055. X
  1056. X
  1057. Xvoid update_group_page(group)
  1058. Xchar *group;
  1059. X{
  1060. X#ifndef INDEX_DAEMON
  1061. X    register int i;
  1062. X
  1063. X    for (i = first_subj_on_screen; i < last_subj_on_screen; ++i) {
  1064. X        bld_sline(i);
  1065. X        draw_sline(i, FALSE);
  1066. X    }
  1067. X
  1068. X    if (top_base <= 0)
  1069. X        return;
  1070. X
  1071. X    draw_subject_arrow();
  1072. X#endif /* INDEX_DAEMON */
  1073. X}
  1074. X
  1075. X
  1076. Xvoid draw_subject_arrow()
  1077. X{
  1078. X    MoveCursor (INDEX2LNUM(index_point), 0);
  1079. X
  1080. X    if (draw_arrow_mark) {
  1081. X        fputs ("->", stdout);
  1082. X        fflush (stdout);
  1083. X    } else {
  1084. X        StartInverse();
  1085. X        draw_sline(index_point, TRUE);
  1086. X        EndInverse();
  1087. X    }
  1088. X    MoveCursor (LINES, 0);
  1089. X}
  1090. X
  1091. Xvoid erase_subject_arrow()
  1092. X{
  1093. X    MoveCursor (INDEX2LNUM(index_point), 0);
  1094. X
  1095. X    if (draw_arrow_mark) {
  1096. X        fputs ("  ", stdout);
  1097. X    } else {
  1098. X        draw_sline(index_point, TRUE);
  1099. X    }
  1100. X    fflush (stdout);
  1101. X}
  1102. X
  1103. X
  1104. Xvoid prompt_subject_num (ch, group)
  1105. X    int ch;
  1106. X    char *group;
  1107. X{
  1108. X    int num;
  1109. X
  1110. X    if (! top_base) {
  1111. X        return;
  1112. X    }
  1113. X
  1114. X    clear_message ();
  1115. X
  1116. X    if ((num = prompt_num (ch, txt_read_art)) == -1) {
  1117. X        clear_message ();
  1118. X        return;
  1119. X    }
  1120. X    num--;        /* index from 0 (internal) vs. 1 (user) */
  1121. X
  1122. X    if (num < 0) {
  1123. X        num = 0;
  1124. X    }
  1125. X    if (num >= top_base) {
  1126. X        num = top_base - 1;
  1127. X    }
  1128. X
  1129. X    if (num >= first_subj_on_screen
  1130. X    &&  num < last_subj_on_screen) {
  1131. X        erase_subject_arrow();
  1132. X        index_point = num;
  1133. X        draw_subject_arrow();
  1134. X    } else {
  1135. X#ifndef USE_CLEARSCREEN
  1136. X        erase_subject_arrow();
  1137. X#endif        
  1138. X        index_point = num;
  1139. X        show_group_page(group);
  1140. X    }
  1141. X}
  1142. X
  1143. X
  1144. Xvoid clear_note_area ()
  1145. X{
  1146. X#ifndef USE_CLEARSCREEN
  1147. X    MoveCursor (INDEX_TOP, 0);
  1148. X    CleartoEOS ();
  1149. X#endif
  1150. X}
  1151. X
  1152. X/*
  1153. X * Find new index position after a kill or unkill. Because
  1154. X * kill can work on author it is impossible to know which,
  1155. X * if any, articles will be left afterwards. So we make a
  1156. X * "best attempt" to find a new index point.
  1157. X */
  1158. X
  1159. Xint find_new_pos (old_top, old_artnum, cur_pos)
  1160. X    int old_top;
  1161. X    long old_artnum;
  1162. X    int cur_pos;
  1163. X{
  1164. X    int pos;
  1165. X    
  1166. X     if (top == old_top) {
  1167. X         return (cur_pos);
  1168. X     }    
  1169. X  
  1170. X     if ((pos = valid_artnum (old_artnum)) >= 0) {
  1171. X         if ((pos = which_thread (pos)) >= 0){
  1172. X             return pos;
  1173. X         }
  1174. X     }        
  1175. X     
  1176. X     if (cur_pos < top_base) {
  1177. X         return cur_pos;
  1178. X     } else {
  1179. X         return (top_base - 1);
  1180. X     }    
  1181. X}
  1182. X
  1183. X
  1184. Xvoid mark_screen (level, screen_row, screen_col, value)
  1185. X    int level;
  1186. X    int screen_row;
  1187. X    int screen_col;
  1188. X    char *value;
  1189. X{
  1190. X    int i, len;
  1191. X
  1192. X    len = strlen (value);
  1193. X    
  1194. X    if (draw_arrow_mark) {
  1195. X        MoveCursor(INDEX_TOP + screen_row, screen_col);
  1196. X        fputs (value, stdout);
  1197. X        MoveCursor (LINES, 0);
  1198. X        fflush (stdout);
  1199. X    } else {
  1200. X        for (i=0 ; i < len ; i++) {
  1201. X            screen[screen_row].col[screen_col+i] = value[i];
  1202. X        }
  1203. X        if (level == SELECT_LEVEL) {
  1204. X            draw_group_arrow();
  1205. X        } else {
  1206. X            draw_subject_arrow();
  1207. X        }    
  1208. X    }
  1209. X}
  1210. X
  1211. X
  1212. Xvoid set_subj_from_size (num_cols)
  1213. X    int num_cols;
  1214. X{
  1215. X    int size = 0;
  1216. X    
  1217. X    if (show_author == SHOW_FROM_BOTH) {
  1218. X        max_subj = (num_cols / 2) - 2;
  1219. X    } else {
  1220. X        max_subj = (num_cols / 2) + 5;
  1221. X    }
  1222. X    max_from = (num_cols - max_subj) - 17;
  1223. X
  1224. X    if (show_author != SHOW_FROM_BOTH) {
  1225. X        if (max_from > 25) {
  1226. X            size = max_from - 25;
  1227. X            max_from = 25;
  1228. X            max_subj = max_subj + size;
  1229. X        }
  1230. X    }
  1231. X
  1232. X    if (show_author != SHOW_FROM_NONE) {
  1233. X        len_from = max_from - BLANK_GROUP_COLS;
  1234. X        len_subj = max_subj;
  1235. X        spaces = "   ";
  1236. X    } else {
  1237. X        len_from = 0;
  1238. X        len_subj = (max_subj+max_from+3) - BLANK_GROUP_COLS;
  1239. X        spaces = "";
  1240. X    }
  1241. X}
  1242. X
  1243. X
  1244. Xvoid toggle_subject_from ()
  1245. X{
  1246. X    if (default_show_author != SHOW_FROM_NONE) {
  1247. X        if (show_author != SHOW_FROM_NONE) {
  1248. X            show_author = SHOW_FROM_NONE;
  1249. X        } else {
  1250. X            show_author = default_show_author;
  1251. X        }
  1252. X    } else {
  1253. X        if (show_author + 1 > SHOW_FROM_BOTH) {
  1254. X            show_author = SHOW_FROM_NONE;
  1255. X        } else {
  1256. X            show_author++;
  1257. X        }
  1258. X    }
  1259. X    set_subj_from_size (COLS);
  1260. X}
  1261. X
  1262. X/*
  1263. X * Build subject line given an index into base[]. 
  1264. X *
  1265. X * WARNING: the routine is tightly coupled with draw_sline() in the sense
  1266. X * that draw_sline() expects bld_sline() to place the article mark
  1267. X * (READ_ART_MARK, HOT_ART_MARK, etc) at MARK_OFFSET in the screen[].col.
  1268. X * So, if you change the format used in this routine, be sure to check
  1269. X * that the value of MARK_OFFSET is still correct. 
  1270. X * Yes, this is somewhat kludgy.
  1271. X */
  1272. Xstatic int bld_sline (i)
  1273. X    int i;
  1274. X{
  1275. X#ifndef INDEX_DAEMON
  1276. X    int respnum;
  1277. X    int n, j;
  1278. X    char from[LEN];
  1279. X    char new_resps[8];
  1280. X    char art_cnt[8];
  1281. X     struct art_stat_t sbuf;
  1282. X
  1283. X    from[0] = '\0';
  1284. X    respnum = (int) base[i];
  1285. X    
  1286. X    stat_thread(i, &sbuf);
  1287. X    if (show_only_unread)
  1288. X        n = sbuf.unread + sbuf.seen;
  1289. X    else
  1290. X        n = sbuf.total;
  1291. X    
  1292. X    n = ART_ADJUST(n);
  1293. X    
  1294. X    if (arts[respnum].tagged) {
  1295. X        sprintf (new_resps, "%3d", arts[respnum].tagged);
  1296. X    } else {
  1297. X        sprintf (new_resps, "  %c", sbuf.art_mark);
  1298. X    }
  1299. X
  1300. X    if (n) {
  1301. X        sprintf (art_cnt, "%-3d", n); 
  1302. X    } else {
  1303. X        strcpy (art_cnt, "   ");
  1304. X    }
  1305. X    
  1306. X    if (show_author != SHOW_FROM_NONE) {
  1307. X        get_author (FALSE, respnum, from);
  1308. X    }    
  1309. X    
  1310. X    j = INDEX2SNUM(i);
  1311. X    sprintf (screen[j].col, "  %4d%3s %s%-*.*s%s%-*.*s",
  1312. X         i+1, new_resps, art_cnt, len_subj, len_subj, 
  1313. X         arts[respnum].subject, spaces, len_from, len_from, from);
  1314. X    
  1315. X#endif /* INDEX_DAEMON */
  1316. X    return(0);
  1317. X}
  1318. X
  1319. X/*
  1320. X * Draw subject line given an index into base[].
  1321. X *
  1322. X * WARNING: this routine is tightly coupled with bld_sline(); see the warning
  1323. X * associated with that routine for details. (C++ would be handy here.)
  1324. X *
  1325. X * NOTE: the 2nd argument is used to control whether the full line is 
  1326. X * redrawn or just the the parts of it that can be changed by a
  1327. X * command; i.e., the unread art count and the art mark. This will result
  1328. X * in a slightly more efficient update, though at the price of increased 
  1329. X * code complexity and readability.
  1330. X */
  1331. X
  1332. Xstatic int draw_sline (i, full)
  1333. X    int i;
  1334. X    int full;    /* unused at moment */
  1335. X{
  1336. X#ifndef INDEX_DAEMON
  1337. X    int j, tlen, x;
  1338. X    int k = MARK_OFFSET;
  1339. X    char *s;
  1340. X
  1341. X    j = INDEX2SNUM(i);
  1342. X
  1343. X    if (full) {
  1344. X        tlen = strlen (screen[j].col);
  1345. X        s = screen[j].col;
  1346. X        x = 0;
  1347. X    } else {
  1348. X        tlen  = 7;
  1349. X        s = &screen[j].col[6];
  1350. X        x = 6;
  1351. X    }
  1352. X
  1353. X    MoveCursor (INDEX2LNUM(i), x);
  1354. X    fwrite (s, 1, tlen, stdout);
  1355. X
  1356. X    /* it is somewhat less efficient to go back and redo that art mark
  1357. X     * if hot, but it is quite readable as to what is happening 
  1358. X     */
  1359. X    if (screen[j].col[k] == HOT_ART_MARK) {
  1360. X        MoveCursor (INDEX2LNUM(i), k);
  1361. X        ToggleInverse ();
  1362. X        fputc (screen[j].col[k], stdout);
  1363. X        ToggleInverse ();
  1364. X    }
  1365. X
  1366. X    MoveCursor(INDEX2LNUM(i)+1, 0);
  1367. X    
  1368. X#endif /* INDEX_DAEMON */
  1369. X    return(0);
  1370. X}
  1371. END_OF_FILE
  1372.   if test 29136 -ne `wc -c <'group.c'`; then
  1373.     echo shar: \"'group.c'\" unpacked with wrong size!
  1374.   fi
  1375.   # end of 'group.c'
  1376. fi
  1377. if test -f 'patchlev.h' -a "${1}" != "-c" ; then 
  1378.   echo shar: Will not clobber existing file \"'patchlev.h'\"
  1379. else
  1380.   echo shar: Extracting \"'patchlev.h'\" \(585 characters\)
  1381.   sed "s/^X//" >'patchlev.h' <<'END_OF_FILE'
  1382. X/*
  1383. X *  Project   : tin - a threaded Netnews reader
  1384. X *  Module    : patchlev.h
  1385. X *  Author    : I.Lea
  1386. X *  Created   : 01-04-91
  1387. X *  Updated   : 22-06-92
  1388. X *  Notes     :
  1389. X *  Copyright : (c) Copyright 1991-92 by Iain Lea
  1390. X *              You may  freely  copy or  redistribute  this software,
  1391. X *              so  long as there is no profit made from its use, sale
  1392. X *              trade or  reproduction.  You may not change this copy-
  1393. X *              right notice, and it must be included in any copy made
  1394. X */
  1395. X
  1396. X#define VERSION        "1.1"        /* Beta versions are "1.n Beta" */
  1397. X#define PATCHLEVEL    4
  1398. END_OF_FILE
  1399.   if test 585 -ne `wc -c <'patchlev.h'`; then
  1400.     echo shar: \"'patchlev.h'\" unpacked with wrong size!
  1401.   fi
  1402.   # end of 'patchlev.h'
  1403. fi
  1404. if test -f 'thread.c' -a "${1}" != "-c" ; then 
  1405.   echo shar: Will not clobber existing file \"'thread.c'\"
  1406. else
  1407.   echo shar: Extracting \"'thread.c'\" \(20862 characters\)
  1408.   sed "s/^X//" >'thread.c' <<'END_OF_FILE'
  1409. X/*
  1410. X *  Project   : tin - a threaded Netnews reader
  1411. X *  Module    : thread.c
  1412. X *  Author    : I.Lea
  1413. X *  Created   : 01-04-91
  1414. X *  Updated   : 20-06-92
  1415. X *  Notes     :
  1416. X *  Copyright : (c) Copyright 1991-92 by Iain Lea
  1417. X *              You may  freely  copy or  redistribute  this software,
  1418. X *              so  long as there is no profit made from its use, sale
  1419. X *              trade or  reproduction.  You may not change this copy-
  1420. X *              right notice, and it must be included in any copy made
  1421. X */
  1422. X
  1423. X#include    "tin.h"
  1424. X
  1425. Xextern int index_point;
  1426. Xint threaded_on_subject;
  1427. Xstatic int top_thread = 0;
  1428. Xstatic int thread_index_point = 0;
  1429. Xstatic int thread_basenote = 0;
  1430. Xstatic int thread_respnum = 0;
  1431. Xstatic int first_thread_on_screen = 0;
  1432. Xstatic int last_thread_on_screen = 0;
  1433. X
  1434. X#define INDEX2TNUM(i)    ((i) % NOTESLINES)
  1435. X#define TNUM2LNUM(i)    (INDEX_TOP + (i))
  1436. X#define INDEX2LNUM(i)    (TNUM2LNUM(INDEX2TNUM(i)))
  1437. X
  1438. X#define MARK_OFFSET 8
  1439. X
  1440. X
  1441. Xstatic int bld_tline (l, i)
  1442. X    int l;
  1443. X    int i;
  1444. X{
  1445. X#ifndef INDEX_DAEMON
  1446. X    int j;
  1447. X    char mark;
  1448. X    char new_resps[8];
  1449. X    char from[LEN];
  1450. X
  1451. X    int len_from;
  1452. X    int len_subj = 0;
  1453. X    int off_subj = 0;
  1454. X    int off_both = 0;
  1455. X    char *spaces = "XXX";
  1456. X
  1457. X    if (! draw_arrow_mark) {
  1458. X        off_subj = 2;
  1459. X        off_both = 5;
  1460. X    }    
  1461. X
  1462. X    if (threaded_on_subject) {
  1463. X        len_from = max_subj+max_from+off_both;
  1464. X        spaces = "";
  1465. X    } else {
  1466. X        if (show_author != SHOW_FROM_NONE) {
  1467. X            len_from = max_from;
  1468. X            len_subj = max_subj+off_subj;
  1469. X            spaces = "   ";
  1470. X        } else {
  1471. X            len_from = 0;
  1472. X            len_subj = max_from+max_subj+off_subj;
  1473. X            spaces = "";
  1474. X        }
  1475. X    }    
  1476. X
  1477. X    j = INDEX2TNUM(l);
  1478. X
  1479. X    if (arts[i].tagged) {
  1480. X        sprintf (new_resps, "%3d", arts[i].tagged);
  1481. X    } else {
  1482. X        if (arts[i].unread == ART_UNREAD) {
  1483. X            mark = (arts[i].hot ? HOT_ART_MARK : UNREAD_ART_MARK);
  1484. X        } else if (arts[i].unread == ART_WILL_RETURN) {
  1485. X            mark =  RETURN_ART_MARK;
  1486. X        } else {
  1487. X            mark = READ_ART_MARK;
  1488. X        }
  1489. X        sprintf (new_resps, "  %c", mark);
  1490. X    }
  1491. X    
  1492. X    from[0] = '\0';
  1493. X    if (threaded_on_subject || show_author != SHOW_FROM_NONE)
  1494. X        get_author (TRUE, i, from);
  1495. X
  1496. X    sprintf (screen[j].col, "  %4d%3s  %-*.*s%s%-*.*s",
  1497. X         l, new_resps, len_subj, len_subj, arts[i].subject, 
  1498. X         spaces, len_from, len_from, from);
  1499. X
  1500. X#endif
  1501. X    return(0);
  1502. X}
  1503. X
  1504. X
  1505. Xstatic int draw_tline (i, full)
  1506. X    int i;
  1507. X    int full;
  1508. X{
  1509. X#ifndef INDEX_DAEMON
  1510. X    int j, tlen, x;
  1511. X    int k = MARK_OFFSET;
  1512. X    char *s;
  1513. X
  1514. X    j = INDEX2TNUM(i);
  1515. X
  1516. X    if (full) {
  1517. X        tlen = strlen(screen[j].col);
  1518. X        s = screen[j].col;
  1519. X        x = 0;
  1520. X    } else {
  1521. X        tlen  = 3;
  1522. X        s = &screen[j].col[6];
  1523. X        x = 6;
  1524. X    }
  1525. X
  1526. X    MoveCursor(INDEX2LNUM(i), x);
  1527. X    fwrite (s, 1, tlen, stdout);
  1528. X
  1529. X    /* it is somewhat less efficient to go back and redo that art mark
  1530. X     * if hot, but it is quite readable as to what is happening 
  1531. X     */
  1532. X    if (screen[j].col[k] == HOT_ART_MARK) {
  1533. X        MoveCursor (INDEX2LNUM(i), k);
  1534. X        ToggleInverse ();
  1535. X        fputc (screen[j].col[k], stdout);
  1536. X        ToggleInverse ();
  1537. X    }
  1538. X
  1539. X    MoveCursor(INDEX2LNUM(i)+1, 0);
  1540. X#endif
  1541. X    return(0);
  1542. X}
  1543. X/*
  1544. X * show current thread. If threaded on Subject: show
  1545. X *   <respnum> <name>    <respnum> <name>
  1546. X * If threaded on Archive-name: show
  1547. X *   <respnum> <subject> <name>
  1548. X */
  1549. Xint show_thread (respnum, group, group_path)
  1550. X    int respnum;
  1551. X    char *group;
  1552. X    char *group_path;
  1553. X{
  1554. X    int ret_code = TRUE;
  1555. X#ifndef INDEX_DAEMON
  1556. X    int ch;
  1557. X    int i, index, n;
  1558. X    int scroll_lines;
  1559. X    int flag;
  1560. X
  1561. X    thread_respnum = respnum;
  1562. X    thread_basenote = which_thread (thread_respnum);
  1563. X    top_thread = num_of_responses (thread_basenote) + 1;
  1564. X
  1565. X    if (top_thread <= 0) {
  1566. X        info_message (txt_no_resps_in_thread);
  1567. X        return FALSE;
  1568. X    }
  1569. X
  1570. X    if (arts[thread_respnum].archive != (char *) 0) {
  1571. X        threaded_on_subject = FALSE;
  1572. X    } else {
  1573. X        threaded_on_subject = TRUE;
  1574. X    }
  1575. X
  1576. X    thread_index_point = top_thread;
  1577. X    if (space_mode) {
  1578. X        if (i = new_responses (thread_basenote)) {
  1579. X            for (n=0, i = base[thread_basenote]; i >= 0 ;
  1580. X                 i = arts[i].thread, n++) {
  1581. X                if (arts[i].unread == ART_UNREAD) {
  1582. X                    thread_index_point = n;
  1583. X                    break;
  1584. X                }
  1585. X            }
  1586. X        }
  1587. X    }
  1588. X
  1589. X    if (thread_index_point < 0) {
  1590. X        thread_index_point = 0;
  1591. X    }
  1592. X
  1593. X    show_thread_page ();
  1594. X
  1595. X    while (TRUE) {
  1596. X        ch = (char) ReadCh();
  1597. X
  1598. X        if (ch >= '0' && ch <= '9') {    /* 0 goes to basenote */
  1599. X            prompt_thread_num (ch);
  1600. X            continue;
  1601. X        }
  1602. X        switch (ch) {
  1603. X            case ESC:    /* common arrow keys */
  1604. X                switch (get_arrow_key ()) {
  1605. X                    case KEYMAP_UP:
  1606. X                        goto thread_up;
  1607. X
  1608. X                    case KEYMAP_DOWN:
  1609. X                        goto thread_down;
  1610. X
  1611. X                    case KEYMAP_PAGE_UP:
  1612. X                        goto thread_page_up;
  1613. X
  1614. X                    case KEYMAP_PAGE_DOWN:
  1615. X                        goto thread_page_down;
  1616. X
  1617. X                    case KEYMAP_HOME:
  1618. X                        if (thread_index_point != 0) {
  1619. X                            if (0 < first_thread_on_screen) {
  1620. X#ifndef USE_CLEARSCREEN
  1621. X                                erase_thread_arrow ();
  1622. X#endif
  1623. X                                thread_index_point = 0;
  1624. X                                show_thread_page ();
  1625. X                            } else {
  1626. X                                erase_thread_arrow ();
  1627. X                                thread_index_point = 0;
  1628. X                                draw_thread_arrow ();
  1629. X                            }
  1630. X                        }
  1631. X                        break;
  1632. X                    
  1633. X                    case KEYMAP_END:
  1634. X                        goto end_of_thread;
  1635. X                }
  1636. X                break;
  1637. X
  1638. X            case '$':    /* show last page of threads */
  1639. Xend_of_thread:            
  1640. X                if (thread_index_point < top_thread - 1) {
  1641. X                    if (top_thread > last_thread_on_screen) {
  1642. X#ifndef USE_CLEARSCREEN
  1643. X                        erase_thread_arrow ();
  1644. X#endif                    
  1645. X                        thread_index_point = top_thread - 1;
  1646. X                        show_thread_page ();
  1647. X                    } else {
  1648. X                        erase_thread_arrow ();
  1649. X                        thread_index_point = top_thread - 1;
  1650. X                        draw_thread_arrow ();
  1651. X                    }
  1652. X                }
  1653. X                break;
  1654. X                
  1655. X            case '\r':
  1656. X            case '\n':    /* read current article within thread */
  1657. X                n = choose_response (thread_basenote, thread_index_point);
  1658. X                n = show_page (n, &thread_index_point, group, group_path);
  1659. X                if (n == thread_basenote) {
  1660. X                    show_thread_page ();
  1661. X                } else {
  1662. X                    index_point = n;    
  1663. X                    goto thread_done;
  1664. X                }
  1665. X                break;
  1666. X
  1667. X            case '\t':
  1668. X                 space_mode = TRUE;
  1669. X                if (thread_index_point == 0) {
  1670. X                    n = thread_respnum;
  1671. X                } else {
  1672. X                    n = choose_response (thread_basenote, thread_index_point);
  1673. X                }
  1674. X                index = thread_index_point;
  1675. X                for (i = n ; i != -1 ; i = arts[i].thread) {
  1676. X                    if (arts[i].unread == ART_UNREAD) {
  1677. X                        n = show_page (i, &thread_index_point, group, group_path);
  1678. X                        break;
  1679. X                    }
  1680. X                    index++;
  1681. X                }
  1682. X                if (n == thread_basenote) {
  1683. X                    show_thread_page ();
  1684. X                } else {
  1685. X                    index_point = n;    
  1686. X                    goto thread_done;
  1687. X                }
  1688. X                break;
  1689. X    
  1690. X            case ' ':        /* page down */
  1691. X            case ctrl('D'):
  1692. X            case ctrl('F'):        /* vi style */
  1693. Xthread_page_down:
  1694. X                if (thread_index_point + 1 == top_thread) {
  1695. X#ifdef NO_LOOP_AROUND
  1696. X                    break;
  1697. X#else
  1698. X                    if (0 < first_thread_on_screen) {
  1699. X#    ifndef USE_CLEARSCREEN
  1700. X                        erase_thread_arrow ();
  1701. X#    endif                    
  1702. X                        thread_index_point = 0;
  1703. X                        show_thread_page ();
  1704. X                    } else {
  1705. X                        erase_thread_arrow ();
  1706. X                        thread_index_point = 0;
  1707. X                        draw_thread_arrow ();
  1708. X                    }
  1709. X                    break;
  1710. X#endif                    
  1711. X                }
  1712. X                erase_thread_arrow ();
  1713. X                scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
  1714. X                thread_index_point = ((thread_index_point + scroll_lines) /
  1715. X                            scroll_lines) * scroll_lines;
  1716. X                if (thread_index_point >= top_thread) {
  1717. X                    thread_index_point = (top_thread / scroll_lines) * scroll_lines;
  1718. X                    if (thread_index_point < top_thread - 1) {
  1719. X                        thread_index_point = top_thread - 1;
  1720. X                    }
  1721. X                }
  1722. X                if (thread_index_point < first_thread_on_screen ||
  1723. X                    thread_index_point >= last_thread_on_screen) {
  1724. X                    show_thread_page ();
  1725. X                } else {
  1726. X                    draw_thread_arrow ();
  1727. X                }
  1728. X                break;
  1729. X
  1730. X            case ctrl('L'):        /* redraw screen */
  1731. X            case ctrl('R'):
  1732. X            case ctrl('W'):
  1733. X#ifndef USE_CLEARSCREEN
  1734. X                ClearScreen ();
  1735. X#endif
  1736. X                show_thread_page ();
  1737. X                break;
  1738. X
  1739. X            case ctrl('N'):
  1740. X            case 'j':        /* line down */
  1741. Xthread_down:
  1742. X                if (thread_index_point + 1 >= top_thread) {
  1743. X#ifdef NO_LOOP_AROUND
  1744. X                    break;
  1745. X#else
  1746. X                    if (0 < first_thread_on_screen) {
  1747. X                        thread_index_point = 0;
  1748. X                        show_thread_page ();
  1749. X                    } else {
  1750. X                        erase_thread_arrow ();
  1751. X                        thread_index_point = 0;
  1752. X                        draw_thread_arrow ();
  1753. X                    }
  1754. X                    break;
  1755. X#endif                    
  1756. X                }
  1757. X                if (thread_index_point + 1 >= last_thread_on_screen) {
  1758. X#ifndef USE_CLEARSCREEN
  1759. X                    erase_thread_arrow ();
  1760. X#endif                    
  1761. X                    thread_index_point++;
  1762. X                    show_thread_page ();
  1763. X                } else {
  1764. X                    erase_thread_arrow ();
  1765. X                    thread_index_point++;
  1766. X                    draw_thread_arrow ();
  1767. X                }
  1768. X                break;
  1769. X
  1770. X            case ctrl('P'):
  1771. X            case 'k':        /* line up */
  1772. Xthread_up:
  1773. X                if (thread_index_point == 0) {
  1774. X#ifdef NO_LOOP_AROUND
  1775. X                    break;
  1776. X#else
  1777. X                    if (top_thread > last_thread_on_screen) {
  1778. X                        thread_index_point = top_thread - 1;
  1779. X                        show_thread_page ();
  1780. X                    } else {
  1781. X                        erase_thread_arrow ();
  1782. X                        thread_index_point = top_thread - 1;
  1783. X                        draw_thread_arrow ();
  1784. X                    }
  1785. X                    break;
  1786. X#endif
  1787. X                }
  1788. X                if (thread_index_point <= first_thread_on_screen) {
  1789. X                    thread_index_point--;
  1790. X                    show_thread_page ();
  1791. X                } else {
  1792. X                    erase_thread_arrow ();
  1793. X                    thread_index_point--;
  1794. X                    draw_thread_arrow ();
  1795. X                }
  1796. X                break;
  1797. X
  1798. X            case 'b':        /* page up */
  1799. X            case ctrl('U'):
  1800. X            case ctrl('B'):        /* vi style */
  1801. Xthread_page_up:
  1802. X                if (thread_index_point == 0) {
  1803. X#ifdef NO_LOOP_AROUND
  1804. X                    break;
  1805. X#else
  1806. X                    if (top_thread > last_thread_on_screen) {
  1807. X                        thread_index_point = top_thread - 1;
  1808. X                        show_thread_page ();
  1809. X                    } else {
  1810. X                        erase_thread_arrow ();
  1811. X                        thread_index_point = top_thread - 1;
  1812. X                        draw_thread_arrow ();
  1813. X                    }
  1814. X                    break;
  1815. X#endif                    
  1816. X                }
  1817. X#ifndef USE_CLEARSCREEN
  1818. X                clear_message ();
  1819. X#endif
  1820. X                erase_thread_arrow ();
  1821. X                scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
  1822. X                if ((n = thread_index_point % scroll_lines) > 0) {
  1823. X                    thread_index_point = thread_index_point - n;
  1824. X                } else {
  1825. X                    thread_index_point = ((thread_index_point - scroll_lines) / scroll_lines) * scroll_lines;
  1826. X                }
  1827. X                if (thread_index_point < 0) {
  1828. X                    thread_index_point = 0;
  1829. X                }
  1830. X                if (thread_index_point < first_thread_on_screen
  1831. X                || thread_index_point >= last_thread_on_screen)
  1832. X                    show_thread_page ();
  1833. X                else
  1834. X                    draw_thread_arrow ();
  1835. X                break;
  1836. X
  1837. X            case 'B':    /* bug/gripe/comment mailed to author */
  1838. X                mail_bug_report ();
  1839. X#ifndef USE_CLEARSCREEN
  1840. X                ClearScreen ();
  1841. X#endif
  1842. X                show_thread_page ();
  1843. X                break;
  1844. X
  1845. X            case 'c':    /* catchup thread but ask for confirmation */
  1846. X            case 'K':    /* mark thread as read immediately */
  1847. X                if (ch == 'c') {
  1848. X                    if (confirm_action && !prompt_yn (LINES, txt_mark_thread_read, 'y')) {
  1849. X                        break;
  1850. X                    }
  1851. X                }
  1852. X                for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) {
  1853. X                    arts[i].unread = ART_READ;
  1854. X                }
  1855. X                goto thread_done;
  1856. X                break;
  1857. X
  1858. X            case 'd':    /* toggle display of subject & subj/author */
  1859. X                if (! threaded_on_subject) {
  1860. X                    toggle_subject_from ();
  1861. X                    show_thread_page ();
  1862. X                }    
  1863. X                break;
  1864. X                
  1865. X            case 'h':    /* help */
  1866. X                show_info_page (HELP_INFO, help_thread, txt_thread_com);
  1867. X                show_thread_page ();
  1868. X                break;
  1869. X
  1870. X            case 'I':    /* toggle inverse video */
  1871. X                toggle_inverse_video ();
  1872. X                show_thread_page ();
  1873. X                break;
  1874. X
  1875. X            case 'q':    /* return to previous level */
  1876. X                goto thread_done;
  1877. X
  1878. X            case 'Q':    /* quit */
  1879. X                ret_code = -2;
  1880. X                goto thread_done;
  1881. X
  1882. X             case 'T':    /* tag/untag art for mailing/piping/printing/saving */
  1883. X                n = choose_response (thread_basenote, thread_index_point);
  1884. X
  1885. X                 if (n < 0)
  1886. X                     break;
  1887. X                 if (arts[n].tagged) {
  1888. X                     arts[n].tagged = 0;
  1889. X                     info_message (txt_untagged_art);
  1890. X                 } else {
  1891. X                     arts[n].tagged = ++num_of_tagged_arts;
  1892. X                    info_message (txt_tagged_art);
  1893. X                 }
  1894. X                bld_tline (thread_index_point, n);
  1895. X                draw_tline (thread_index_point, FALSE);
  1896. X                if (thread_index_point + 1 < top_thread)
  1897. X                    goto thread_down;
  1898. X                draw_thread_arrow ();
  1899. X                break;
  1900. X
  1901. X            case 'v':    /* version */
  1902. X                info_message (cvers);
  1903. X                break;
  1904. X
  1905. X            case 'z':    /* mark article as unread */
  1906. X            case 'Z':    /* mark thread as unread */
  1907. X                n = choose_response (thread_basenote, thread_index_point);
  1908. X                if (ch == 'z') {
  1909. X                    arts[n].unread = ART_UNREAD;
  1910. X                } else {
  1911. X                    for (i = (int) base[thread_basenote] ; i != -1; i = arts[i].thread) {
  1912. X                        arts[i].unread = ART_UNREAD;
  1913. X                    }
  1914. X                }
  1915. X                bld_tline (thread_index_point, n);
  1916. X                draw_tline (thread_index_point, FALSE);
  1917. X                info_message (ch == 'z' 
  1918. X                          ? txt_art_marked_as_unread
  1919. X                          : txt_thread_marked_as_unread);
  1920. X                draw_thread_arrow ();
  1921. X                break;
  1922. X                
  1923. X            case '*':    /* mark article as selected */
  1924. X            case '.':    /* toggle article as selected */
  1925. X                n = choose_response (thread_basenote, thread_index_point);
  1926. X
  1927. X                if (n < 0)
  1928. X                    break;
  1929. X                if (ch == '.' && arts[n].hot == 1)
  1930. X                    flag = 0;
  1931. X                else
  1932. X                    flag = 1;
  1933. X                arts[n].hot = flag;
  1934. X/*                update_thread_page (); */
  1935. X                bld_tline (thread_index_point, n);
  1936. X                draw_tline (thread_index_point, FALSE);
  1937. X                if (thread_index_point + 1 < top_thread)
  1938. X                    goto thread_down;
  1939. X                draw_thread_arrow ();
  1940. X#if 0
  1941. X                info_message (flag 
  1942. X                          ? txt_art_marked_as_selected
  1943. X                          : txt_art_marked_as_deselected)
  1944. X#endif
  1945. X                break;
  1946. X
  1947. X            case '@':    /* reverse selections */
  1948. X                for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) {
  1949. X                    arts[i].hot = (arts[i].hot ? 0 : 1);
  1950. X                }
  1951. X                update_thread_page ();
  1952. X                break;
  1953. X
  1954. X            case '~':    /* undo selections */
  1955. X                for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) {
  1956. X                    arts[i].hot = 0;
  1957. X                }
  1958. X                update_thread_page ();
  1959. X                break;
  1960. X
  1961. X            default:
  1962. X                info_message (txt_bad_command);
  1963. X        }
  1964. X    }
  1965. X
  1966. Xthread_done:
  1967. X    clear_note_area ();
  1968. X
  1969. X#endif /* INDEX_DAEMON */
  1970. X
  1971. X    return (ret_code);
  1972. X}
  1973. X
  1974. X
  1975. Xvoid show_thread_page ()
  1976. X{
  1977. X#ifndef INDEX_DAEMON
  1978. X
  1979. X    extern int index_point;
  1980. X    char new_resps[8];
  1981. X    char from[LEN];
  1982. X    int i, j;
  1983. X    int len_from;
  1984. X    int len_subj = 0;
  1985. X    int off_subj = 0;
  1986. X    int off_both = 0;
  1987. X    static int index = 0;
  1988. X
  1989. X    set_signals_thread ();
  1990. X    
  1991. X    ClearScreen ();
  1992. X
  1993. X    if (threaded_on_subject) {
  1994. X        sprintf (msg, "Thread (%.*s)", COLS-23, arts[thread_respnum].subject);
  1995. X    } else {
  1996. X        sprintf (msg, "List Thread (%d of %d)", index_point+1, top_base);
  1997. X    }
  1998. X    show_title (msg);
  1999. X
  2000. X    MoveCursor (INDEX_TOP, 0);
  2001. X    if (thread_index_point > top_thread - 1) {
  2002. X        thread_index_point = top_thread - 1;
  2003. X    }
  2004. X
  2005. X    if (NOTESLINES <= 0) {
  2006. X        first_thread_on_screen = 0;
  2007. X    } else {
  2008. X        first_thread_on_screen = (thread_index_point / NOTESLINES) * NOTESLINES;
  2009. X        if (first_thread_on_screen < 0) {
  2010. X            first_thread_on_screen = 0;
  2011. X        }
  2012. X    }
  2013. X
  2014. X    last_thread_on_screen = first_thread_on_screen + NOTESLINES;
  2015. X
  2016. X    if (last_thread_on_screen >= top_thread) {
  2017. X        last_thread_on_screen = top_thread;
  2018. X        first_thread_on_screen = (top_thread / NOTESLINES) * NOTESLINES;
  2019. X
  2020. X        if (first_thread_on_screen == last_thread_on_screen ||
  2021. X            first_thread_on_screen < 0) {
  2022. X            if (first_thread_on_screen < 0) {
  2023. X                first_thread_on_screen = 0;
  2024. X            } else {
  2025. X                first_thread_on_screen = last_thread_on_screen - NOTESLINES;
  2026. X            }
  2027. X        }
  2028. X    }
  2029. X
  2030. X    if (top_thread == 0) {
  2031. X        first_thread_on_screen = 0;
  2032. X        last_thread_on_screen = 0;
  2033. X    }
  2034. X
  2035. X    index = choose_response (thread_basenote, first_thread_on_screen);
  2036. X    assert(first_thread_on_screen != 0 || index == thread_respnum);
  2037. X
  2038. X    if (! draw_arrow_mark) {
  2039. X        off_subj = 2;
  2040. X        off_both = 5;
  2041. X    }    
  2042. X
  2043. X    if (threaded_on_subject) {
  2044. X        len_from = max_subj+max_from+off_both;
  2045. X    } else {
  2046. X        if (show_author != SHOW_FROM_NONE) {
  2047. X            len_from = max_from;
  2048. X            len_subj = max_subj+off_subj;
  2049. X        } else {
  2050. X            len_from = 0;
  2051. X            len_subj = max_from+max_subj+off_subj;
  2052. X        }
  2053. X    }    
  2054. X
  2055. X    for (j=0, i = first_thread_on_screen; j < NOTESLINES && i < last_thread_on_screen; i++, j++) {
  2056. X        bld_tline (i, index);
  2057. X        draw_tline (i, TRUE);
  2058. X        if ((index = next_response (index)) == -1) {
  2059. X            break;
  2060. X        }    
  2061. X    }
  2062. X
  2063. X#ifndef USE_CLEARSCREEN
  2064. X    CleartoEOS ();
  2065. X#endif
  2066. X
  2067. X    if (last_thread_on_screen == top_thread) {
  2068. X        info_message (txt_end_of_thread);
  2069. X    }
  2070. X
  2071. X    draw_thread_arrow ();
  2072. X
  2073. X#endif /* INDEX_DAEMON */
  2074. X}
  2075. X
  2076. X
  2077. Xvoid update_thread_page()
  2078. X{
  2079. X#ifndef INDEX_DAEMON
  2080. X    register int i, j, index;
  2081. X
  2082. X    index = choose_response (thread_basenote, first_thread_on_screen);
  2083. X    assert(first_thread_on_screen != 0 || index == thread_respnum);
  2084. X
  2085. X    for (j=0, i = first_thread_on_screen; j < NOTESLINES && i < last_thread_on_screen; ++i, ++j) {
  2086. X        bld_tline (i, index);
  2087. X        draw_tline (i, FALSE);
  2088. X        if ((index = next_response (index)) == -1) {
  2089. X            break;
  2090. X        }    
  2091. X    }
  2092. X
  2093. X    draw_thread_arrow();
  2094. X#endif /* INDEX_DAEMON */
  2095. X}
  2096. X
  2097. X
  2098. Xvoid draw_thread_arrow ()
  2099. X{
  2100. X    MoveCursor (INDEX2LNUM(thread_index_point), 0);
  2101. X
  2102. X    if (draw_arrow_mark) {
  2103. X        fputs ("->", stdout);
  2104. X        fflush (stdout);
  2105. X    } else {
  2106. X        StartInverse();
  2107. X        draw_tline(thread_index_point, TRUE);
  2108. X        EndInverse();
  2109. X    }
  2110. X    MoveCursor (LINES, 0);
  2111. X}
  2112. X
  2113. X
  2114. Xvoid erase_thread_arrow ()
  2115. X{
  2116. X    MoveCursor (INDEX2LNUM(thread_index_point), 0);
  2117. X
  2118. X    if (draw_arrow_mark) {
  2119. X        fputs ("  ", stdout);
  2120. X    } else {
  2121. X        draw_tline(thread_index_point, TRUE);
  2122. X    }
  2123. X    fflush (stdout);
  2124. X}
  2125. X
  2126. X
  2127. Xint prompt_thread_num (ch)
  2128. X    char ch;
  2129. X{
  2130. X    int num;
  2131. X
  2132. X    clear_message ();
  2133. X
  2134. X    if ((num = prompt_num (ch, txt_read_art)) == -1) {
  2135. X        clear_message ();
  2136. X        return FALSE;
  2137. X    }
  2138. X
  2139. X    if (num >= top_thread)
  2140. X        num = top_thread - 1;
  2141. X
  2142. X    if (num >= first_thread_on_screen
  2143. X    &&  num < last_thread_on_screen) {
  2144. X        erase_thread_arrow ();
  2145. X        thread_index_point = num;
  2146. X        draw_thread_arrow ();
  2147. X    } else {
  2148. X#ifndef USE_CLEARSCREEN
  2149. X        erase_thread_arrow ();
  2150. X#endif        
  2151. X        thread_index_point = num;
  2152. X        show_thread_page ();
  2153. X    }
  2154. X    return TRUE;
  2155. X}
  2156. X
  2157. X/*
  2158. X *  Return the number of unread articles there are within a thread
  2159. X */
  2160. X
  2161. Xint new_responses (thread)
  2162. X    int thread;
  2163. X{
  2164. X    int i;
  2165. X    int sum = 0;
  2166. X
  2167. X    for (i = (int) base[thread]; i >= 0; i = arts[i].thread) {
  2168. X        if (arts[i].unread) {
  2169. X            sum++;
  2170. X        }
  2171. X    }
  2172. X    
  2173. X    return sum;
  2174. X}
  2175. X
  2176. X/*
  2177. X *  Which base note (an index into base[]) does a respnum
  2178. X *  (an index into arts[]) corresponsd to?
  2179. X *
  2180. X *  In other words, base[] points to an entry in arts[] which is
  2181. X *  the head of a thread, linked with arts[].thread.  For any q: arts[q],
  2182. X *  find i such that base[i]->arts[n]->arts[o]->...->arts[q]
  2183. X *
  2184. X *  Note that which_thread() can return -1 if in show_read_only mode and
  2185. X *  the article of interest has been read as well as all other articles in
  2186. X *  the thread,  thus resulting in no base[] entry for it.
  2187. X */
  2188. X
  2189. Xint which_thread (n)
  2190. X    int n;
  2191. X{
  2192. X    register int i, j;
  2193. X
  2194. X    for (i = 0; i < top_base; i++) {
  2195. X        for (j = (int) base[i] ; j >= 0 ; j = arts[j].thread) {
  2196. X            if (j == n) {
  2197. X                return i;
  2198. X            }
  2199. X        }
  2200. X    }
  2201. X
  2202. X    sprintf (msg, "%d", n);
  2203. X    error_message (txt_cannot_find_base_art, msg);
  2204. X    return -1;
  2205. X}
  2206. X
  2207. X/*
  2208. X *  Find how deep in a thread a response is.  Start counting at zero
  2209. X */
  2210. X
  2211. Xint which_response (n)
  2212. X    int n;
  2213. X{
  2214. X    int i, j;
  2215. X    int num = 0;
  2216. X
  2217. X    i = which_thread (n);
  2218. X    assert(i >= 0);
  2219. X
  2220. X    for (j = (int) base[i]; j != -1; j = arts[j].thread)
  2221. X        if (j == n)
  2222. X            break;
  2223. X        else
  2224. X            num++;
  2225. X
  2226. X    return num;
  2227. X}
  2228. X
  2229. X/*
  2230. X *  Given an index into base[], find the number of responses for
  2231. X *  that basenote
  2232. X */
  2233. X
  2234. Xint num_of_responses (n)
  2235. X    int n;
  2236. X{
  2237. X    int i;
  2238. X    int oldi = -3;
  2239. X    int sum = 0;
  2240. X
  2241. X    assert (n < top_base);
  2242. X
  2243. X    for (i = (int) base[n]; i != -1; i = arts[i].thread) {
  2244. X        assert (i != -2);
  2245. X        assert (i != oldi);
  2246. X        oldi = i;
  2247. X        sum++;
  2248. X    }
  2249. X
  2250. X    return sum - 1;
  2251. X}
  2252. X
  2253. X/*
  2254. X * Given an index into base[], return relevant statistics
  2255. X */
  2256. X
  2257. Xint stat_thread (n, sbuf)
  2258. X    int n;
  2259. X    struct art_stat_t *sbuf;
  2260. X{
  2261. X    int i;
  2262. X
  2263. X    sbuf->total  = 0;
  2264. X    sbuf->unread = 0;
  2265. X    sbuf->seen   = 0;
  2266. X    sbuf->hot_total = 0;
  2267. X    sbuf->hot_unread= 0;
  2268. X    sbuf->hot_seen  = 0;
  2269. X
  2270. X    for (i = (int) base[n]; i != -1; i = arts[i].thread) {
  2271. X        ++sbuf->total;
  2272. X        if (arts[i].unread == ART_UNREAD)
  2273. X            ++sbuf->unread;
  2274. X        else if (arts[i].unread == ART_WILL_RETURN)
  2275. X            ++sbuf->seen;
  2276. X
  2277. X        if (arts[i].hot) {
  2278. X            ++sbuf->hot_total;
  2279. X            if (arts[i].unread == ART_UNREAD)
  2280. X                ++sbuf->hot_unread;
  2281. X            else if (arts[i].unread == ART_WILL_RETURN)
  2282. X                ++sbuf->hot_seen;
  2283. X        }
  2284. X
  2285. X#if 0
  2286. X        if (arts[i].killed) {
  2287. X            ++sbuf->killed;
  2288. X        }
  2289. X#endif
  2290. X    }
  2291. X
  2292. X
  2293. X    if (sbuf->hot_unread)
  2294. X        sbuf->art_mark = HOT_ART_MARK;
  2295. X    else if (sbuf->unread)
  2296. X        sbuf->art_mark = UNREAD_ART_MARK;
  2297. X    else if (sbuf->seen)
  2298. X        sbuf->art_mark = RETURN_ART_MARK;
  2299. X    else
  2300. X        sbuf->art_mark = READ_ART_MARK;
  2301. X
  2302. X    return(sbuf->total);
  2303. X}
  2304. X
  2305. X
  2306. X/*
  2307. X *  Find the next response.  Go to the next basenote if there
  2308. X *  are no more responses in this thread
  2309. X */
  2310. X
  2311. Xint next_response (n)
  2312. X    int n;
  2313. X{
  2314. X    int i;
  2315. X
  2316. X    if (arts[n].thread >= 0)
  2317. X        return arts[n].thread;
  2318. X
  2319. X    i = which_thread (n) + 1;
  2320. X
  2321. X    if (i >= top_base)
  2322. X        return -1;
  2323. X
  2324. X    return (int) base[i];
  2325. X}
  2326. X
  2327. X/*
  2328. X *  Given a respnum (index into arts[]), find the respnum of the
  2329. X *  next basenote
  2330. X */
  2331. X
  2332. Xint next_thread (n)
  2333. X    int n;
  2334. X{
  2335. X    int i;
  2336. X
  2337. X    i = which_thread (n) + 1;
  2338. X    if (i >= top_base)
  2339. X        return -1;
  2340. X
  2341. X    return (int) base[i];
  2342. X}
  2343. X
  2344. X/*
  2345. X *  Find the previous response.  Go to the last response in the previous
  2346. X *  thread if we go past the beginning of this thread.
  2347. X */
  2348. X
  2349. Xint prev_response (n)
  2350. X    int n;
  2351. X{
  2352. X    int resp;
  2353. X    int i;
  2354. X
  2355. X    resp = which_response (n);
  2356. X
  2357. X    if (resp > 0)
  2358. X        return choose_response (which_thread (n), resp-1);
  2359. X
  2360. X    i = which_thread (n) - 1;
  2361. X
  2362. X    if (i < 0)
  2363. X        return -1;
  2364. X
  2365. X    return choose_response (i, num_of_responses (i));
  2366. X}
  2367. X
  2368. X/*
  2369. X *  return response number n from thread i
  2370. X */
  2371. X
  2372. Xint choose_response (i, n)
  2373. X    int i;
  2374. X    int n;
  2375. X{
  2376. X    int j;
  2377. X
  2378. X    j = (int) base[i];
  2379. X
  2380. X    while (n-- > 0 && arts[j].thread >= 0) {
  2381. X        j = arts[j].thread;
  2382. X    }
  2383. X
  2384. X    return j;
  2385. X}
  2386. X
  2387. X/*
  2388. X *  Find the next unread response in this group 
  2389. X */
  2390. X
  2391. Xint next_unread (n)
  2392. X    int n;
  2393. X{
  2394. X    while (n >= 0) {
  2395. X        if (arts[n].unread == ART_UNREAD) {
  2396. X            return n;
  2397. X        }
  2398. X        n = next_response (n);
  2399. X    }
  2400. X
  2401. X    return -1;
  2402. X}
  2403. X
  2404. X
  2405. X/*
  2406. X *  Find the previous unread response in this thread
  2407. X */
  2408. X
  2409. Xint prev_unread (n)
  2410. X    int n;
  2411. X{
  2412. X    while (n >= 0) {
  2413. X        if (arts[n].unread == ART_UNREAD) {
  2414. X            return n;
  2415. X        }
  2416. X        n = prev_response (n);
  2417. X    }
  2418. X
  2419. X    return -1;
  2420. X}
  2421. END_OF_FILE
  2422.   if test 20862 -ne `wc -c <'thread.c'`; then
  2423.     echo shar: \"'thread.c'\" unpacked with wrong size!
  2424.   fi
  2425.   # end of 'thread.c'
  2426. fi
  2427. echo shar: End of archive 5 \(of 15\).
  2428. cp /dev/null ark5isdone
  2429. MISSING=""
  2430. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do
  2431.     if test ! -f ark${I}isdone ; then
  2432.     MISSING="${MISSING} ${I}"
  2433.     fi
  2434. done
  2435. if test "${MISSING}" = "" ; then
  2436.     echo You have unpacked all 15 archives.
  2437.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2438. else
  2439.     echo You still must unpack the following archives:
  2440.     echo "        " ${MISSING}
  2441. fi
  2442. exit 0
  2443. exit 0 # Just in case...
  2444.