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

  1. Subject:  v19i063:  NN, a Usenet news reader, Part02/15
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: storm@texas.dk (Kim F. Storm)
  7. Posting-number: Volume 19, Issue 63
  8. Archive-name: nn/part02
  9.  
  10. #!/bin/sh
  11. # this is part 2 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file admin.c continued
  14. #
  15. CurArch=2
  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 admin.c"
  26. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> admin.c
  27. X    printf("You must restart nnadmin to access the new group(s)\n");
  28. X    ngp = master.number_of_groups;
  29. X    }
  30. X    
  31. X    ngp = 0;
  32. X    
  33. X    Loop_Groups_Header(gh) 
  34. X    if (update_group(gh) < 0) ngp++;
  35. X
  36. X    if (ngp) printf("There are %d blocked groups\n", ngp);
  37. X}
  38. X
  39. X    
  40. Xstatic master_admin()
  41. X{
  42. X    register char c;
  43. X    int cur_group, value;
  44. X    register group_header *gh;
  45. X
  46. X    for (;;) {
  47. X    switch (c = get_cmd(
  48. X"\nG)roup A)ll E)mpty N)on-empty F)iles O)ptions S)tat T)race K)ill",
  49. X"MASTER")) {
  50. X
  51. X     case 'G':
  52. X        cur_group = get_entry("Group number",
  53. X                  0L, (long)(master.number_of_groups - 1));
  54. X        if (cur_group >= 0)
  55. X        dump_m_entry(&active_groups[cur_group]);
  56. X        break;
  57. X
  58. X     case 'A':
  59. X     case 'E':
  60. X     case 'N':
  61. X        cur_group = -1; 
  62. X        s_keyboard = 0;
  63. X        
  64. X        while (++cur_group < master.number_of_groups) {
  65. X        if (s_keyboard || s_hangup) break;
  66. X        
  67. X        gh = &active_groups[cur_group];
  68. X        if (c == 'N' && gh->last_l_article == 0) continue;
  69. X        if (c == 'E' && gh->last_l_article != 0) continue;
  70. X        dump_m_entry(gh);
  71. X        }
  72. X        break;
  73. X
  74. X     case 'F':
  75. X        find_files(-1);
  76. X        break;
  77. X
  78. X     case 'O':
  79. X        c = get_cmd("r)epeat_delay  e)xpire_level", "OPTION");
  80. X        if (c != 'r' && c != 'e') break;
  81. X        value = get_entry("Option value", 1, 10000);
  82. X        if (value < 0) break;
  83. X        send_master(c, (long)value, 0L);
  84. X        break;
  85. X        
  86. X     case 'S':
  87. X        master_status();
  88. X        break;
  89. X
  90. X     case 'T':
  91. X        send_master('T', 0L, 0L);
  92. X        break;
  93. X        
  94. X     case 'K':
  95. X        if (admin_confirm("Stop nn Master"))
  96. X        kill_master_1(SIGHUP);
  97. X        break;
  98. X        
  99. X     default:
  100. X        return;
  101. X    }
  102. X    }
  103. X}
  104. X
  105. X
  106. Xstatic log_admin()
  107. X{
  108. X    char command[FILENAME + 100], c;
  109. X
  110. X    if (pre_input && *pre_input == NUL) {
  111. X    c = SP;
  112. X    goto log_tail;
  113. X    }
  114. X    
  115. X loop:
  116. X
  117. X    c = get_cmd(
  118. X"\nE)rrors R)eports C)ollect e(X)pire A)dmin G)roup (1-9)tail (*)all (@)clean",
  119. X"LOG");
  120. X    
  121. X    if (c < SP || c == 'Q') return;
  122. X    if (c == '@') { 
  123. X    if (admin_confirm("Truncation")) {
  124. X        sprintf(command, 
  125. X"cd %s && mv Log Log.old && ./log_entry A Truncated && chmod 666 Log",
  126. X            lib_directory);
  127. X        system(command);
  128. X    }
  129. X    return;
  130. X    }
  131. X    
  132. X    if (c == 'G') {
  133. X    char *groupname;
  134. X    
  135. X    raw();
  136. X    printf("Group: ");
  137. X    fl;
  138. X    groupname = get_s(NONE, NONE, NONE, group_completion);
  139. X    no_raw();
  140. X
  141. X    putchar(NL); putchar(CR);
  142. X    
  143. X    if (groupname == NULL) return;
  144. X
  145. X    sprintf(command, "grep '%s' %s/Log | %s",
  146. X        groupname, lib_directory, pager);
  147. X    system(command);
  148. X    
  149. X    goto loop;
  150. X    }
  151. X
  152. X log_tail:    
  153. X    if (c == '$' || c == SP || isdigit(c)) {
  154. X    int n;
  155. X    
  156. X    n = isdigit(c) ? 10 * (c - '0') : 10;
  157. X    sprintf(command, "tail -%d %s/Log", n, lib_directory);
  158. X    system(command);
  159. X    goto loop;
  160. X    }
  161. X    
  162. X    if (c == '*') {
  163. X    c = '.';
  164. X    }
  165. X
  166. X    sprintf(command, "grep '^%c:' %s/Log | %s", c, lib_directory, pager);
  167. X    system(command);
  168. X
  169. X    goto loop;
  170. X}
  171. X
  172. X    
  173. Xstatic group_admin()
  174. X{
  175. X    char *groupname;
  176. X    register group_header *gh;
  177. X    
  178. X new_group:
  179. X
  180. X    raw();
  181. X    printf("Group: ");
  182. X    fl;
  183. X    groupname = get_s(NONE, NONE, NONE, group_completion);
  184. X    no_raw();
  185. X
  186. X    putchar(NL); putchar(CR);
  187. X    
  188. X    if (groupname == NULL) return;
  189. X    
  190. X    gh = lookup(groupname);
  191. X    if (gh == NULL) {
  192. X    printf("No group named %s\n", groupname);
  193. X    goto new_group;
  194. X    }
  195. X    for (;;) {
  196. X    switch (get_cmd(
  197. X"\nD)ata H)eader F)iles S)et_flag C)lear_flag E)xpire R)ecollect G)roup",
  198. X"GROUP")) {
  199. X     case 'D':
  200. X        dump_group(gh, 0);
  201. X        break;
  202. X        
  203. X     case 'H':
  204. X        dump_m_entry(gh);
  205. X        break;
  206. X
  207. X     case 'F':
  208. X        find_files(gh->group_num);
  209. X        break;
  210. X        
  211. X     case 'S':
  212. X        flag_admin(gh, "Set", 1);
  213. X        break;
  214. X        
  215. X     case 'C':
  216. X        flag_admin(gh, "Clear", 0);
  217. X        break;
  218. X        
  219. X     case 'R':
  220. X        if (admin_confirm("Recolletion of Group"))
  221. X        send_master('R', (long)gh->group_num, 0L);
  222. X        break;
  223. X
  224. X     case 'E':
  225. X        if (admin_confirm("Expire Group"))
  226. X        send_master('X', (long)gh->group_num, 0L);
  227. X        break;
  228. X        
  229. X     case 'G':
  230. X        goto new_group;
  231. X        
  232. X     default:
  233. X        return;
  234. X    }
  235. X    }
  236. X}
  237. X
  238. X
  239. Xstatic flag_admin(gh, mode_str, set_flag)
  240. Xgroup_header *gh;
  241. Xchar *mode_str;
  242. Xint set_flag;
  243. X{
  244. X    char buffer[50];
  245. X    int new_flag = 0;
  246. X
  247. X    putchar(NL);
  248. X    
  249. X    dump_g_flag(gh);
  250. X
  251. X    sprintf(buffer, "%s FLAG", mode_str);
  252. X
  253. X    switch (get_cmd(
  254. X"\nA)lways_digest N)ever_digest M)oderated C)ontrol no_(D)ir",
  255. Xbuffer)) {
  256. X
  257. X     default:
  258. X    return;
  259. X    
  260. X     case 'M':
  261. X    new_flag = G_MODERATED;
  262. X    break;
  263. X    
  264. X     case 'C':
  265. X    new_flag = G_CONTROL;
  266. X    break;
  267. X    
  268. X     case 'D':
  269. X    new_flag = G_NO_DIRECTORY;
  270. X    break;
  271. X    
  272. X     case 'A':
  273. X    new_flag = G_ALWAYS_DIGEST;
  274. X    break;
  275. X    
  276. X     case 'N':
  277. X    new_flag = G_NEVER_DIGEST;
  278. X    break;
  279. X    }
  280. X    
  281. X    if (new_flag & (G_CONTROL | G_NO_DIRECTORY))
  282. X    if (!admin_confirm("Flag Change"))
  283. X        new_flag = 0;
  284. X    
  285. X    if (new_flag) {
  286. X    if (set_flag) {
  287. X        if (gh->group_flag & new_flag)
  288. X        new_flag = 0;
  289. X        else {
  290. X        send_master('S', (long)gh->group_num, (long)new_flag);
  291. X        gh->group_flag |= new_flag;
  292. X        }
  293. X    } else {
  294. X        if ((gh->group_flag & new_flag) == 0)
  295. X        new_flag = 0;
  296. X        else {
  297. X        send_master('C', (long)gh->group_num, (long)new_flag);
  298. X        gh->group_flag &= ~new_flag;
  299. X        }
  300. X    }        
  301. X    }
  302. X    
  303. X    if (new_flag == 0)
  304. X    printf("NO CHANGE\n");
  305. X    else
  306. X    dump_g_flag(gh);
  307. X}
  308. X
  309. X    
  310. Xstatic find_files(group)
  311. Xgroup_number group;
  312. X{
  313. X    char command[512];
  314. X    
  315. X    if (group < 0)
  316. X    sprintf(command, "ls -l %s/DATA | %s", db_directory, pager);
  317. X    else
  318. X    sprintf(command, "ls -l %s/DATA/%d.*", db_directory, group);
  319. X    system(command);
  320. X}
  321. X
  322. X
  323. Xstatic master_status()
  324. X{
  325. X    int cur_group;
  326. X    long articles, disk_use;
  327. X    register group_header *gh;
  328. X
  329. X    printf("\nMaster:\n");
  330. X    printf("   last_scan:     %s\n", date_time(master.last_scan));
  331. X    printf("   no of groups:  %d\n", master.number_of_groups);
  332. X    printf("   next write:    %ld\n", master.next_group_write_offset);
  333. X
  334. X    articles = disk_use = 0;
  335. X    
  336. X    for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
  337. X    gh = &active_groups[cur_group];
  338. X    
  339. X#define DISK_BLOCKS(bytes) (((bytes) + 1023) / 1024)
  340. X
  341. X    disk_use += DISK_BLOCKS(gh->index_write_offset);
  342. X    disk_use += DISK_BLOCKS(gh->data_write_offset);
  343. X
  344. X    articles += gh->last_l_article - gh->first_l_article + 1;
  345. X    }
  346. X    
  347. X    printf("\n   Articles:   %ld\n", articles);
  348. X    printf(  "   Disk usage: %ld k\n", disk_use);
  349. X}
  350. X
  351. Xstatic show_config()
  352. X{
  353. X    extern char *temp_file;
  354. X    extern char news_active[], news_directory[];
  355. X#ifdef NNTP
  356. X    extern char nntp_server[];
  357. X#endif
  358. X    
  359. X    printf("\nConfiguration:\n\n");
  360. X    printf("BIN:  %s\nLIB:  %s\nDB:   %s\nNEWS: %s\n",
  361. X       BIN_DIRECTORY,
  362. X       lib_directory, 
  363. X       db_directory, 
  364. X       news_directory);
  365. X    
  366. X    printf("ACTIVE: %s\n", news_active);
  367. X    
  368. X#ifdef NNTP
  369. X    if (use_nntp)
  370. X    printf("NNTP ACTIVE. server=%s\n", nntp_server);
  371. X    else
  372. X    printf("NNTP NOT ACTIVE\n");
  373. X#endif
  374. X
  375. X#ifdef NETWORK_DATABASE
  376. X    printf("Database is machine independent (network format).\n");
  377. X#ifdef NETWORK_BYTE_ORDER
  378. X    printf("Local system assumes to use network byte order\n");
  379. X#endif
  380. X#else
  381. X    printf("Database format is machine dependent (byte order and alignment)\n");
  382. X#endif
  383. X
  384. X#ifdef STATISTICS
  385. X    printf("Recording usage statistics\n");
  386. X#else
  387. X    printf("No usage statistics are recorded\n");
  388. X#endif
  389. X
  390. X    printf("Default pager: %s\n", PAGER);
  391. X    printf("Default printer: %s\n", PRINTER);
  392. X    
  393. X    printf("Mail delivery program: %s\n", REC_MAIL);
  394. X#ifdef APPEND_SIGNATURE
  395. X    printf("Query for appending .signature ENABLED\n");
  396. X#else
  397. X    printf("Query for appending .signature DISABLED\n");
  398. X#endif    
  399. X
  400. X    printf("Max pathname length is %d bytes\n", FILENAME-1);
  401. X}
  402. X       
  403. Xstatic dump_m_entry(gh)
  404. Xregister group_header *gh;
  405. X{
  406. X    update_group(gh);
  407. X    
  408. X    printf("\n%s\t%d\n", gh->group_name, gh->group_num);
  409. X    printf("first/last art: %06ld %06d\n",
  410. X       gh->first_l_article, gh->last_l_article);
  411. X    printf("   active info: %06ld %06d\n",
  412. X       gh->first_article, gh->last_article);
  413. X    printf("Offsets: index->%ld, data->%ld\n",
  414. X       gh->index_write_offset,
  415. X       gh->data_write_offset);
  416. X    if (gh->group_flag)
  417. X    dump_g_flag(gh);
  418. X}
  419. X
  420. Xstatic dump_g_flag(gh)
  421. Xregister group_header *gh;
  422. X{
  423. X    printf("Flags: ");
  424. X    if (gh->group_flag & G_BLOCKED)     printf(" BLOCKED");
  425. X    if (gh->group_flag & G_EXPIRE)     printf(" EXPIRE");
  426. X    if (gh->group_flag & G_MODERATED)     printf(" MODERATED");
  427. X    if (gh->group_flag & G_CONTROL)     printf(" CONTROL");
  428. X    if (gh->group_flag & G_NO_DIRECTORY) printf(" NO_DIRECTORY");
  429. X    if (gh->group_flag & G_ALWAYS_DIGEST) printf(" ALWAYS_DIGEST");
  430. X    if (gh->group_flag & G_NEVER_DIGEST) printf(" NEVER_DIGEST");
  431. X    printf("\n");
  432. X}
  433. X
  434. X
  435. Xstatic dump_group(gh, validate)
  436. Xgroup_header *gh;
  437. Xint validate;
  438. X{
  439. X    FILE            *data, *ix;
  440. X    data_header            hdr;
  441. X    off_t              data_offset, real_offset, next_offset;
  442. X    cross_post_number        cross_post;
  443. X    article_number        first_article, next_article, this_art;
  444. X    int                n, was_digest;
  445. X    char            buffer[512];
  446. X    
  447. X    if (init_group(gh) <= 0)
  448. X    printf("cannot access group %s\n", gh->group_name);
  449. X
  450. X    update_group(gh);
  451. X    
  452. X    if (validate)
  453. X    first_article = gh->first_l_article;
  454. X    else
  455. X    first_article = get_entry("First article",
  456. X                  (long)gh->first_l_article,
  457. X                  (long)gh->last_l_article);
  458. X
  459. X    if (first_article < 0) first_article = gh->first_l_article;
  460. X
  461. X    ix = open_data_file(gh, 'x', OPEN_READ);
  462. X    if (ix == NULL) {
  463. X    if (verbose) printf("NO INDEX FILE\n");
  464. X    return 0;
  465. X    }
  466. X    next_offset = get_index_offset(gh, first_article);
  467. X    fseek(ix, next_offset, 0);
  468. X    
  469. X    if (!db_read_offset(ix, &real_offset)) {
  470. X    if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)first_article);
  471. X    fclose(ix);
  472. X    return 0;
  473. X    }
  474. X
  475. X    data_offset = real_offset;
  476. X    fseek(ix, next_offset, 0);
  477. X    
  478. X    data = open_data_file(gh, 'd', OPEN_READ);
  479. X    if (data == NULL) {
  480. X    if (verbose) printf("NO DATA FILE\n");
  481. X    fclose(ix);
  482. X    return 0;
  483. X    }
  484. X
  485. X    
  486. X    next_article = first_article;
  487. X    s_keyboard = 0;
  488. X    was_digest = 0;
  489. X    
  490. X    while (next_article <= gh->last_l_article) {
  491. X    if (s_hangup || s_keyboard) goto out;
  492. X
  493. X    if (!db_read_offset(ix, &real_offset)) {
  494. X        if (verbose)
  495. X        printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article);
  496. X        goto err;
  497. X    }
  498. X
  499. X    if (data_offset != real_offset)
  500. X        goto ix_data_err;
  501. X    
  502. X    fseek(data, data_offset, 0);
  503. X
  504. X     in_digest:
  505. X
  506. X    next_offset = data_offset;
  507. X    if (!db_read_art(data, &hdr, &data_offset)) {
  508. X        if (real_offset == gh->data_write_offset || was_digest)
  509. X        break;
  510. X        
  511. X        if (verbose) printf("No article header for article # %ld\n",
  512. X                (long)next_article);
  513. X        goto err;
  514. X    }
  515. X    
  516. X    if (hdr.dh_number) {
  517. X        if (was_digest) {
  518. X        next_article++;
  519. X        if (!db_read_offset(ix, &real_offset)) 
  520. X            goto ix_eof_err;
  521. X        if (real_offset != next_offset)
  522. X            goto ix_data_err;
  523. X        }
  524. X        
  525. X        this_art = hdr.dh_number < 0 ? -hdr.dh_number : hdr.dh_number;
  526. X    
  527. X        if (this_art != next_article) {
  528. X        if (this_art < next_article) {
  529. X            if (verbose)
  530. X            printf("Article # %ld out of sequence\n", 
  531. X                   (long)this_art);
  532. X            goto err;
  533. X        }
  534. X/*        
  535. X        if (this_art == (next_article + 1)) 
  536. X            printf("Article # %ld missing (OK)\n",
  537. X               (long)next_article);
  538. X        else
  539. X            printf("Articles # %ld -> %ld missing (OK)\n",
  540. X               (long)next_article, (long)this_art);
  541. X*/        
  542. X        while (this_art > next_article) {
  543. X            if (!db_read_offset(ix, &next_offset))
  544. X            goto ix_eof_err;
  545. X            if (next_offset != real_offset) 
  546. X            goto ix_data_err;
  547. X            next_article++;
  548. X        }
  549. X        }
  550. X    }
  551. X
  552. X    if (!validate)
  553. X        printf("\noffset = %ld, article # = %ld\n",
  554. X           (long)real_offset, (long)(hdr.dh_number));
  555. X       
  556. X    if (hdr.dh_lpos == (off_t)0) {    /* article not accessible */
  557. X        if (verbose)
  558. X        printf("# %ld: NO ARTICLE (ok)\n", (long)next_article);
  559. X        continue;
  560. X    }
  561. X    
  562. X    data_offset += hdr.dh_cross_postings * sizeof(cross_post_number)
  563. X        + hdr.dh_sender_length 
  564. X        + hdr.dh_subject_length;
  565. X
  566. X    if (hdr.dh_cross_postings) {
  567. X        if (!validate)
  568. X        printf("xpost(%d):", hdr.dh_cross_postings);
  569. X    
  570. X        n = hdr.dh_cross_postings;
  571. X        while (--n >= 0) {
  572. X        if (fread((char *)&cross_post, sizeof(cross_post_number), 1, data)!= 1)
  573. X            goto data_error;
  574. X#ifdef NETWORK_DATABASE
  575. X#ifndef NETWORK_BYTE_ORDER
  576. X        cross_post = ntohl(cross_post);
  577. X#endif
  578. X#endif        
  579. X        if (validate) {
  580. X            if (cross_post >= master.number_of_groups) {
  581. X            if (verbose)
  582. X                printf("xpost group out of range: %ld (article # %ld)\n",
  583. X                   (long)cross_post, (long)next_article);
  584. X            goto err;
  585. X            }
  586. X        } else
  587. X            printf(" %d", cross_post);
  588. X        } 
  589. X        if (!validate) printf("\n");
  590. X    }    
  591. X
  592. X
  593. X    if (!validate) 
  594. X        if (IS_DIGEST_HEADER(hdr))
  595. X        printf("digest header ");
  596. X        else
  597. X        if (IS_SUB_DIGEST(hdr))
  598. X        printf("digest article ");
  599. X        else
  600. X        printf("normal article ");
  601. X    
  602. X    if (!validate)
  603. X        printf("ts=%lu hp=%ld, fp=+%d, lp=%ld, rep=%d, lines=%d\n",
  604. X           (long unsigned)hdr.dh_date,
  605. X           (long)hdr.dh_hpos, (int)hdr.dh_fpos, (long)hdr.dh_lpos,
  606. X           hdr.dh_replies, hdr.dh_lines);
  607. X    
  608. X    if (hdr.dh_sender_length) {
  609. X        if (fread(buffer, sizeof(char), (int)hdr.dh_sender_length, data)
  610. X        != hdr.dh_sender_length) goto data_error;
  611. X        buffer[hdr.dh_sender_length] = NUL;
  612. X        if (!validate)
  613. X        printf("Sender(%d): %s\n", hdr.dh_sender_length, buffer);
  614. X    } else
  615. X        if (!validate)
  616. X        printf("No sender\n");
  617. X    
  618. X    if (hdr.dh_subject_length) {
  619. X        if (fread(buffer, sizeof(char), (int)hdr.dh_subject_length, data)
  620. X        !=  hdr.dh_subject_length) goto data_error;
  621. X        buffer[hdr.dh_subject_length] = NUL;
  622. X        if (!validate)
  623. X        printf("Subj(%d): %s\n", hdr.dh_subject_length, buffer);
  624. X    } else
  625. X        if (!validate)
  626. X        printf("No subject\n");
  627. X
  628. X    if (IS_DIGEST_HEADER(hdr) || IS_SUB_DIGEST(hdr)) {
  629. X        was_digest = 1;
  630. X        goto in_digest;
  631. X    }
  632. X        
  633. X    was_digest = 0;
  634. X    next_article++;
  635. X
  636. X    }
  637. X
  638. Xout:
  639. X
  640. X    if (s_keyboard == 0 && data_offset != gh->data_write_offset) {
  641. X    if (verbose)
  642. X        printf("\n*** ERROR: Next data offset %ld wrong. real offset=%ld\n",
  643. X           gh->data_write_offset, data_offset);
  644. X    goto err;
  645. X    }
  646. X    
  647. X    fclose(data);
  648. X    fclose(ix);
  649. X    return 1;
  650. X
  651. X    
  652. X    
  653. X data_error:
  654. X    if (verbose) printf("\n*** END OF FILE on DATA FILE\n\n");
  655. X    goto err;
  656. X
  657. X ix_eof_err:
  658. X    if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article);
  659. X    goto err;
  660. X    
  661. X ix_data_err:
  662. X    if (verbose) {
  663. X    printf("\n*** OFFSET ERROR in article # %ld offsets:\n",
  664. X           (long)next_article);
  665. X    printf("    Calcuated offset %ld differs from index %ld\n",
  666. X           (long)data_offset, (long)real_offset);
  667. X    }
  668. X    goto err;
  669. X    
  670. X err:
  671. X            
  672. X    fclose(data);
  673. X    fclose(ix);
  674. X    return 0;
  675. X}
  676. X
  677. X
  678. Xsend_master(command, arg1, arg2)
  679. Xchar command;
  680. Xlong arg1, arg2;
  681. X{
  682. X    FILE *gate;
  683. X
  684. X    gate = open_file(relative(lib_directory, "GATE"), OPEN_APPEND);
  685. X    
  686. X    if (gate == NULL) {
  687. X    printf("Cannot send to master\n");
  688. X    return;
  689. X    }
  690. X    
  691. X    fprintf(gate, "%c;%ld;%ld;%s %s;\n",
  692. X        command, arg1, arg2, user_name(), date_time((time_t)0));
  693. X    
  694. X    fclose(gate);
  695. X
  696. X    log_entry('A', "SEND %c %ld %ld", command, arg1, arg2);
  697. X}
  698. X
  699. X
  700. X    
  701. X    
  702. X/* fake this for visit_active_file() */
  703. X
  704. X/*ARGSUSED*/
  705. Xgroup_header *add_new_group(name)
  706. Xchar *name;
  707. X{
  708. X    return NULL;
  709. X}
  710. X
  711. X
  712. X/*
  713. X *    make consistency check on all groups
  714. X */
  715. X
  716. X
  717. Xstatic file_error(gh, d_or_x, write_offset)
  718. Xgroup_header *gh;
  719. Xchar d_or_x;
  720. Xoff_t write_offset;
  721. X{
  722. X    FILE *f;
  723. X    
  724. X    f = open_data_file(gh, d_or_x, OPEN_READ);
  725. X
  726. X    if (f == NULL) {
  727. X    if (verbose)
  728. X        printf("FILE '%c' NOT FOUND\n", d_or_x);
  729. X    } else {
  730. X    fseek(f, (off_t)0, 2);
  731. X    if (ftell(f) >= write_offset) {
  732. X        fclose(f);
  733. X        return 0;
  734. X    }
  735. X
  736. X    if (verbose)
  737. X        printf("FILE '%c' IS SHORTER THAN NEXT WRITE OFFSET\n", d_or_x);
  738. X    fclose(f);
  739. X    }
  740. X    
  741. X    return 1;
  742. X}
  743. X    
  744. X
  745. Xvalidate_groups()
  746. X{
  747. X    register group_header     *gh;
  748. X    group_number         num;
  749. X    import  char *pname;
  750. X    
  751. X    if (strcmp(pname, "nnadmin")) {
  752. X    printf("You can only run VALIDATION from nnadmin\n");
  753. X    return;
  754. X    }
  755. X    
  756. X    s_keyboard = 0;
  757. X
  758. X    Loop_Groups_Number(num) {
  759. X
  760. X    if (s_hangup || s_keyboard) break;
  761. X    
  762. X    gh = &active_groups[num];
  763. X    if (init_group(gh) <= 0) continue; /* no directory */
  764. X
  765. X    if (verbose) { printf("\r%s: ", gh->group_name); clrline(); }
  766. X    
  767. X    if (gh->group_flag & G_BLOCKED) {
  768. X        if (verbose) printf("BLOCKED\n");
  769. X        continue;
  770. X    }
  771. X    
  772. X    /*
  773. X     *    Check for major inconcistencies.
  774. X     *    Sometimes, news expire will reset article numbers
  775. X     *    to start from 0 again
  776. X     */
  777. X
  778. X    if (gh->last_l_article == 0) {
  779. X        continue;
  780. X    }
  781. X    
  782. X    if (gh->first_article > gh->last_l_article ||
  783. X        gh->last_l_article  > gh->last_article || 
  784. X        gh->first_l_article > gh->first_article) {
  785. X
  786. X      if (verbose)
  787. X        printf("RENUMBERING OF ARTICLES (active=%ld..%ld master=%ld..%ld)",
  788. X           gh->first_article, gh->last_article,
  789. X           gh->first_l_article, gh->last_l_article);
  790. X        goto ask_collect;
  791. X    }
  792. X
  793. X    /*
  794. X     *    Check existence and sizes of data files
  795. X     */
  796. X
  797. X    if (file_error(gh, 'x', gh->index_write_offset))
  798. X        goto ask_collect;
  799. X    
  800. X    if (file_error(gh, 'd', gh->data_write_offset))
  801. X        goto ask_collect;
  802. X
  803. X    if (!dump_group(gh, 1))
  804. X        goto ask_collect;
  805. X
  806. X    if (!verbose) continue;
  807. X    
  808. X    if (gh->first_article > gh->first_l_article)
  809. X        printf("unexpired articles: %ld\n",
  810. X           (long)(gh->first_article - gh->first_l_article));
  811. X    else
  812. X        printf("OK\r");
  813. X    continue;
  814. X    
  815. X     ask_collect:
  816. X    log_entry('V', "Database inconsistency in group %s", gh->group_name);
  817. X    
  818. X    if (admin_confirm("\nRepair group"))
  819. X        send_master('R', (long)gh->group_num, 0L);
  820. X    }
  821. X}
  822. X
  823. X
  824. X
  825. Xkill_master_1(sig)
  826. Xint sig;
  827. X{
  828. X    if (kill_master(sig)) {
  829. X    if (verbose) printf("sent signal %d to master\n", sig);
  830. X    } else
  831. X    if (errno == ESRCH) {
  832. X    if (verbose) printf("master is not running\n");
  833. X    } else
  834. X    printf("cannot signal master (errno=%d)\n", errno);
  835. X}
  836. NO_NEWS_IS_GOOD_NEWS
  837. echo "File admin.c is complete"
  838. chmod 0644 admin.c || echo "restore of admin.c fails"
  839. set `wc -c admin.c`;Sum=$1
  840. if test "$Sum" != "20076"
  841. then echo original size 20076, current size $Sum;fi
  842. echo "x - extracting answer.c (Text)"
  843. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > answer.c &&
  844. X#include "config.h"
  845. X#include "news.h"
  846. X#include "term.h"
  847. X#include "keymap.h"
  848. X
  849. Xextern char *temp_file;
  850. X
  851. Xchar *news_record    = NULL;
  852. Xchar *mail_record    = NULL;
  853. X
  854. X
  855. X#define INCL_MARK_SIZE    10
  856. X
  857. Xexport char included_mark[INCL_MARK_SIZE + 1] = ">";
  858. X
  859. X
  860. Xstatic int ed_line;
  861. X
  862. X
  863. Xanswer(ah, command, incl)
  864. Xarticle_header *ah;
  865. Xint command;
  866. Xint incl;    /* <0: ask, 0: don't include, >0: include article */
  867. X{
  868. X    register FILE *t, *art;
  869. X    char *pgm, *first_action, *record_file;
  870. X    int edit_message;
  871. X    char *str;
  872. X    news_header_buffer nhbuf, dhbuf;
  873. X    
  874. X    first_action = "edit";
  875. X    edit_message = 1;
  876. X    
  877. X    if (incl < 0) {
  878. X    prompt("Include original article? ");
  879. X    if ((incl = yes(0)) < 0) return 0;
  880. X    }
  881. X    
  882. X    art = NULL;
  883. X    if (ah && ah->a_group) init_group(ah->a_group);
  884. X    
  885. X    if (incl || command != K_MAIL_OR_FORWARD) {
  886. X    int open_modes;
  887. X    
  888. X    open_modes = FILL_NEWS_HEADER | GET_ALL_FIELDS | SKIP_HEADER;
  889. X    if (ah->flag & A_DIGEST) open_modes |= FILL_DIGEST_HEADER;
  890. X    
  891. X    art = open_news_article(ah, open_modes, nhbuf, dhbuf);
  892. X    if (art == NULL) {
  893. X        msg("Can't find original article");
  894. X        return 0;
  895. X    }
  896. X    
  897. X    if (ah->flag & A_DIGEST) {
  898. X        if (digest.dg_from)
  899. X        news.ng_path = news.ng_from = digest.dg_from;
  900. X        if (digest.dg_subj)
  901. X        news.ng_subj = digest.dg_subj;
  902. X    }
  903. X    } else
  904. X    ah = NULL;
  905. X    
  906. X    /* build header */
  907. X    
  908. X    if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
  909. X    msg("Can't create %s", temp_file);
  910. X    return 0;
  911. X    }
  912. X
  913. X    ed_line = 0;
  914. X    record_file = mail_record;
  915. X    
  916. X    if (command == K_REPLY) {
  917. X    pgm = "reply";
  918. X    
  919. X    if (reply_to(t, news.ng_reply) ||
  920. X        reply_to(t, news.ng_from) ||
  921. X        reply_to(t, news.ng_path)) goto alt0;
  922. X    if (to_line(t, news.ng_reply)) goto alt1;
  923. X    if (to_line(t, news.ng_from)) goto alt2;
  924. X    if (to_line(t, news.ng_path)) goto alt3;
  925. X    goto err;
  926. X
  927. X     alt0:
  928. X    alt_to_line(t, news.ng_reply);
  929. X     alt1:
  930. X    alt_to_line(t, news.ng_from);
  931. X     alt2:
  932. X    alt_to_line(t, news.ng_path);
  933. X     alt3:
  934. X    
  935. X    if (news.ng_subj)
  936. X        subj_line(t, ah->replies, ah->subject, (char *)NULL);
  937. X    else
  938. X        subj_line(t, 0, current_group->group_name, "Your Article in");
  939. X    
  940. X    ng_line(t);
  941. X    ref_line(t);
  942. X
  943. X    end_header(t);
  944. X
  945. X    if (incl) {
  946. X        fprintf(t, "In %s you write:\n", current_group->group_name);
  947. X        ed_line++;
  948. X    }
  949. X    }
  950. X
  951. X    if (command == K_FOLLOW_UP) {
  952. X    pgm = "follow";
  953. X    record_file = news_record;
  954. X    
  955. X    ng_line(t);
  956. X
  957. X    if (news.ng_subj)
  958. X        subj_line(t, ah->replies, ah->subject, (char *)NULL);
  959. X    else
  960. X        if (!subj_line(t, 0, news.ng_from, "Babble from"))
  961. X        if (!subj_line(t, 0, news.ng_ident, "Article")) {
  962. X            prompt("Subject: ");
  963. X            str = get_s(NONE, NONE, NONE, NO_COMPLETION);
  964. X            if (str == NULL) goto err;
  965. X            subj_line(t, -1, str, (char *)NULL);
  966. X        }
  967. X
  968. X    if (news.ng_keyw) {
  969. X        fprintf(t, "Keywords: %s\n", news.ng_keyw);
  970. X        ed_line++;
  971. X    }
  972. X    
  973. X    if (news.ng_dist) {
  974. X        fprintf(t, "Distribution: %s\n", news.ng_dist);
  975. X        ed_line++;
  976. X    }
  977. X
  978. X    ref_line(t);
  979. X
  980. X    end_header(t);
  981. X
  982. X    if (incl) {
  983. X        if (news.ng_from) {
  984. X        fprintf(t, "%s writes:\n", news.ng_from);
  985. X        ed_line++;
  986. X        } else
  987. X        if (news.ng_ident) {
  988. X        fprintf(t, "In %s %s:\n", 
  989. X            ah->flag & A_DIGEST ? "digest" : "article",
  990. X            news.ng_ident);
  991. X        ed_line++;
  992. X        }
  993. X    }
  994. X    }
  995. X    
  996. X    if (command == K_MAIL_OR_FORWARD) {
  997. X    pgm = incl ? "forward" : "mail";
  998. X    
  999. X     m3_again:
  1000. X    prompt("To: ");
  1001. X    str = get_s(user_name(), NONE, NONE, NO_COMPLETION);
  1002. X    if (str == NULL) goto close_t;
  1003. X
  1004. X    if (*str == NUL) str = user_name();
  1005. X    if (*str == '?') goto m3_again;
  1006. X
  1007. X    if (strcmp(str, user_name()) == 0)
  1008. X        record_file = NULL;    /* we will get this anyway,
  1009. X                   there is so no need to save it */ 
  1010. X    
  1011. X/*    if (reply_to(t, str)) {        alt_to_line(t, str);    } else */
  1012. X    to_line(t, str);
  1013. X
  1014. X    do {
  1015. X        prompt("Subject: ");
  1016. X        str = get_s(incl ? ah->subject : NONE, NONE, NONE, NO_COMPLETION);
  1017. X        if (str == NULL) goto close_t;
  1018. X        if (*str == NUL && incl) str = ah->subject;
  1019. X    } while (*str == NUL);
  1020. X
  1021. X    subj_line(t, -1, str, (char *)NULL);
  1022. X
  1023. X    end_header(t);
  1024. X   
  1025. X    if (incl) {
  1026. X        prompt("\1Edit\1 forwarded message? ");
  1027. X        if ((edit_message = yes(0)) < 0) goto close_t;
  1028. X        if (!edit_message) {
  1029. X        first_action = "send";
  1030. X        fseek(art, ah->hpos, 0);
  1031. X        }
  1032. X    }
  1033. X
  1034. X    }
  1035. X
  1036. X    /* empty line terminates header */
  1037. X    fputc(NL, t);
  1038. X    ed_line++;
  1039. X
  1040. X    prompt("\1WAIT\1");
  1041. X    
  1042. X    if (incl) {
  1043. X    register c, prevnl = 1;
  1044. X    
  1045. X    while ((c = getc(art)) != EOF) {
  1046. X        if (c == NL) {
  1047. X        putc(c, t);
  1048. X        if (ftell(art) >= ah->lpos) break;
  1049. X        prevnl++;
  1050. X        continue;
  1051. X        }
  1052. X        if (prevnl) {
  1053. X        if (command != K_MAIL_OR_FORWARD || ftell(art) < ah->fpos)
  1054. X            fputs(included_mark, t);
  1055. X        prevnl = 0;
  1056. X        }
  1057. X        putc(c, t);
  1058. X    }
  1059. X    } else {
  1060. X    putc(NL, t);
  1061. X    ed_line++;
  1062. X    }
  1063. X    
  1064. X    fclose(t);
  1065. X    if (art) fclose(art);
  1066. X    
  1067. X    aux_sh(pgm, first_action, record_file,
  1068. X       command == K_FOLLOW_UP ? "Article not posted" : "Mail not sent");
  1069. X    
  1070. X    return edit_message;
  1071. X    
  1072. X err:
  1073. X    msg("Can't build header for %s", 
  1074. X    command != K_FOLLOW_UP ? "letter" : "article");
  1075. X
  1076. X close_t:
  1077. X    fclose(t);
  1078. X    unlink(temp_file);
  1079. X    if (art) fclose(art);
  1080. X    
  1081. X    return 0;
  1082. X}
  1083. X
  1084. X
  1085. Xcancel(ah)
  1086. Xarticle_header *ah;
  1087. X{
  1088. X    news_header_buffer nhbuf;
  1089. X    FILE *f;
  1090. X    
  1091. X    if (ah->a_group) init_group(ah->a_group);
  1092. X    
  1093. X    if (ah->flag & A_DIGEST) {
  1094. X    fputs("\rCancel entire digest ? ", stdout); clrline();
  1095. X    if (yes(1) > 0) 
  1096. X        ah->flag &= ~A_DIGEST;
  1097. X    else {
  1098. X        msg("Can only cancel entire digests (yet?)");
  1099. X        return 2;
  1100. X    }
  1101. X    } else {
  1102. X    fputs("\rConfirm cancel: ", stdout); clrline();
  1103. X    if (yes(1) <= 0) return 0;
  1104. X    }
  1105. X    
  1106. X    f = open_news_article(ah, FILL_NEWS_HEADER|GET_ALL_FIELDS, nhbuf, (char *)NULL);
  1107. X    if (f == NULL) {
  1108. X    msg("Can't find original article");
  1109. X    return 2;
  1110. X    }
  1111. X    fclose(f);
  1112. X    
  1113. X    printf("\rCancelling article %s in group %s",
  1114. X       news.ng_ident, current_group->group_name);
  1115. X    clrline();
  1116. X    
  1117. X    ed_line = -1;
  1118. X    
  1119. X    if (aux_sh("cancel", 
  1120. X           news.ng_ident, current_group->group_name, "Not canceled"))
  1121. X    return 3;
  1122. X        
  1123. X    return 1;
  1124. X}
  1125. X
  1126. X
  1127. Xpost_menu()
  1128. X{
  1129. X    FILE *t;
  1130. X    char *str, *tail;
  1131. X    char group_name[FILENAME], subject[FILENAME], 
  1132. X         distribution[FILENAME], keywords[FILENAME];
  1133. X    extern group_completion();
  1134. X    
  1135. X    group_name[0] = NUL;
  1136. X    
  1137. X again_group:
  1138. X
  1139. X    prompt("\1POST to group\1 ");
  1140. X    
  1141. X    str = get_s(current_group ? current_group->group_name : NONE, 
  1142. X        group_name, NONE, group_completion);
  1143. X    if (str == NULL || *str == NUL) return 0;
  1144. X    strcpy(group_name, str);
  1145. X    
  1146. X    for (str = group_name; str; str = tail) {
  1147. X    tail = strchr(str, ',');
  1148. X    if (tail) *tail = NUL;
  1149. X    
  1150. X    if (lookup(str) == NULL) {
  1151. X        msg("unknown group: %s", str);
  1152. X        *str = NUL;
  1153. X        goto again_group;
  1154. X    }
  1155. X
  1156. X    if (tail) *tail++ = ',';
  1157. X    }
  1158. X
  1159. X    prompt("Subject: ");
  1160. X    str = get_s(NONE, NONE, NONE, NO_COMPLETION);
  1161. X    if (str == NULL || *str == NUL) return 0;
  1162. X    strcpy(subject, str);
  1163. X    
  1164. X    prompt("Keywords: ");
  1165. X    str = get_s(NONE, NONE, NONE, NO_COMPLETION);
  1166. X    if (str == NULL) return 0;
  1167. X    strcpy(keywords, str);
  1168. X    
  1169. X    strcpy(distribution, group_name);
  1170. X    if (str = strchr(distribution, '.')) *str = NUL;
  1171. X    
  1172. X    prompt("\1Distribution\1 (default '%s') ", distribution);
  1173. X    str = get_s(NONE, NONE, NONE, NO_COMPLETION);
  1174. X    if (str == NULL) return 0;
  1175. X    if (*str) strcpy(distribution, str);
  1176. X
  1177. X    if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
  1178. X    msg("Can't create %s", temp_file);
  1179. X    return 0;
  1180. X    }
  1181. X
  1182. X    prompt("\1WAIT\1");
  1183. X    
  1184. X    ed_line = 5;
  1185. X    fprintf(t, "Newsgroups: %s\n", group_name);
  1186. X    fprintf(t, "Distribution: %s\n", distribution);
  1187. X    fprintf(t, "Subject: %s\n", subject);
  1188. X    if (*keywords) {
  1189. X    fprintf(t, "Keywords: %s\n", keywords);
  1190. X    ed_line++;
  1191. X    }
  1192. X    fputc(NL, t);
  1193. X    fputc(NL, t);
  1194. X
  1195. X    fclose(t);
  1196. X    
  1197. X    aux_sh("post", "edit", news_record, "Article not posted");
  1198. X    
  1199. X    return 1;
  1200. X}
  1201. X
  1202. Xstatic subj_line(t, re, subj, prefix)
  1203. XFILE *t;
  1204. Xint re;
  1205. Xchar *subj, *prefix;
  1206. X{
  1207. X    if (subj == NULL) return 0;
  1208. X    
  1209. X    fputs("Subject: ", t);
  1210. X
  1211. X    if (re == 0) 
  1212. X    fputs("Re: ", t);
  1213. X    else if (re > 0)
  1214. X    fprintf(t, "Re^%d: ", re + 1);
  1215. X
  1216. X    if (prefix) {
  1217. X    fputs(prefix, t);
  1218. X    fputc(' ', t);
  1219. X    }
  1220. X    
  1221. X    fputs(subj, t);
  1222. X    fputc(NL, t);
  1223. X
  1224. X    ed_line++;
  1225. X    return 1;
  1226. X}
  1227. X
  1228. X
  1229. Xstatic ng_line(t)
  1230. XFILE *t;
  1231. X{    
  1232. X    fprintf(t, "Newsgroups: %s\n", 
  1233. X        news.ng_follow ? news.ng_follow : news.ng_groups);
  1234. X    ed_line++;
  1235. X}
  1236. X
  1237. Xstatic ref_line(t)
  1238. XFILE *t;
  1239. X{
  1240. X    if (news.ng_ref == NULL && news.ng_ident == NULL) return;
  1241. X    
  1242. X    fputs("References:", t);
  1243. X    if (news.ng_ref) fprintf(t, " %s", news.ng_ref);
  1244. X    if (news.ng_ident) fprintf(t, " %s", news.ng_ident);
  1245. X    putc(NL, t);
  1246. X    ed_line++;
  1247. X}
  1248. X
  1249. X
  1250. Xstatic to_line(t, to)
  1251. XFILE *t;
  1252. Xchar *to;
  1253. X{    
  1254. X    if (to == NULL) return 0;
  1255. X
  1256. X    fprintf(t, "To: %s\n", to);
  1257. X    ed_line++;
  1258. X    return 1;
  1259. X}
  1260. X
  1261. Xstatic alt_to_line(t, to)
  1262. XFILE *t;
  1263. Xchar *to;
  1264. X{    
  1265. X    if (to == NULL) return;
  1266. X
  1267. X    fprintf(t, "Orig-To: %s\n", to);
  1268. X    ed_line++;
  1269. X}
  1270. X
  1271. Xstatic end_header(t)
  1272. XFILE *t;
  1273. X{
  1274. X    fputc(NL, t);
  1275. X    ed_line++;
  1276. X}
  1277. X
  1278. X
  1279. Xstatic reply_to(t, address)
  1280. XFILE *t;
  1281. Xchar *address;
  1282. X{
  1283. X    char route[512];
  1284. X    
  1285. X    if (address == NULL) return 0;
  1286. X
  1287. X    if (reroute(route, address)) {
  1288. X    to_line(t, route);
  1289. X    return 1;
  1290. X    }
  1291. X    return 0;
  1292. X}
  1293. X        
  1294. X
  1295. X/*
  1296. X * invoke aux shell script with suitable arguments
  1297. X *
  1298. X * WARNING: record may be NULL, soit must be the last argument!!
  1299. X */
  1300. X
  1301. Xstatic aux_sh(prog, action, record, not_sent)
  1302. Xchar *prog, *action, *record, *not_sent;
  1303. X{
  1304. X    char *args[8];
  1305. X    char number[10];
  1306. X    register char **ap = args;
  1307. X    time_t start_t;
  1308. X    
  1309. X    *ap++ = "nnaux";
  1310. X    *ap++ = relative(lib_directory, "aux");
  1311. X    *ap++ = prog;
  1312. X
  1313. X    if (ed_line >= 0) {    /* not cancel */
  1314. X    sprintf(number, "%d", ed_line);
  1315. X    *ap++ = temp_file;
  1316. X    *ap++ = number;
  1317. X    }
  1318. X    
  1319. X    *ap++ = action;    /* article id for cancel */
  1320. X    *ap++ = record;    /* group name for cancel */
  1321. X
  1322. X    *ap++ = NULL;
  1323. X
  1324. X#ifdef STATISTICS    
  1325. X    time(&start_t);
  1326. X#endif    
  1327. X    if (execute(SHELL, args)) {
  1328. X    prompt_line = -1;
  1329. X    prompt("\1%s\1", not_sent);
  1330. X    sleep(1);
  1331. X    return 1;
  1332. X    }
  1333. X    
  1334. X#ifdef STATISTICS    
  1335. X    tick_usage((time_t *)NULL, &start_t);
  1336. X#endif
  1337. X    
  1338. X    return 0;
  1339. X}
  1340. NO_NEWS_IS_GOOD_NEWS
  1341. chmod 0644 answer.c || echo "restore of answer.c fails"
  1342. set `wc -c answer.c`;Sum=$1
  1343. if test "$Sum" != "9895"
  1344. then echo original size 9895, current size $Sum;fi
  1345. echo "x - extracting articles.c (Text)"
  1346. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > articles.c &&
  1347. X/*
  1348. X * access - get access to master data
  1349. X */
  1350. X
  1351. X#include "config.h"
  1352. X#include "db.h"
  1353. X#include "articles.h"
  1354. X#include "match.h"
  1355. X
  1356. X
  1357. X/*
  1358. X * memory management
  1359. X */
  1360. X
  1361. Xstatic thunk 
  1362. X    dummy_str_t = {
  1363. X    NULL,
  1364. X    NULL,
  1365. X    0L
  1366. X    }, 
  1367. X    dummy_art_t = {
  1368. X    NULL,
  1369. X    NULL,
  1370. X    0L
  1371. X    };
  1372. X
  1373. X
  1374. Xstatic thunk *first_str_t = &dummy_str_t;
  1375. Xstatic thunk *current_str_t = &dummy_str_t;
  1376. Xstatic thunk *first_art_t = &dummy_art_t;
  1377. Xstatic thunk *current_art_t = &dummy_art_t;
  1378. Xstatic int   cur_str_size = 0, cur_art_size = 0;
  1379. Xstatic char *next_str;
  1380. Xstatic article_header *next_art, **art_array;
  1381. X
  1382. Xstatic unsigned max_articles = 0, mem_offset = 0;
  1383. X
  1384. X/*
  1385. X * allocate one article header
  1386. X */
  1387. X
  1388. X#ifndef ART_THUNK_SIZE
  1389. X#define ART_THUNK_SIZE    127
  1390. X#endif
  1391. X
  1392. Xstatic new_thunk(t, ptr, size, chk_msg)
  1393. Xthunk *t;
  1394. Xchar *ptr;
  1395. Xlong size;
  1396. Xchar *chk_msg;
  1397. X{
  1398. X    thunk *new;
  1399. X
  1400. X    mem_check(ptr, (int)size, chk_msg);
  1401. X    
  1402. X    new = (thunk *)calloc(1, sizeof(thunk));
  1403. X    mem_check(new, sizeof(thunk), "memory thunk");
  1404. X    
  1405. X    new->next_thunk = t->next_thunk;
  1406. X    t->next_thunk = new;
  1407. X    
  1408. X    new->this_thunk = ptr;
  1409. X    new->thunk_size = size;
  1410. X}
  1411. X
  1412. X
  1413. Xarticle_header *alloc_art()
  1414. X{
  1415. X    if (cur_art_size == 0) {
  1416. X    if (current_art_t->next_thunk == NULL)
  1417. X        new_thunk(current_art_t,
  1418. X              calloc(ART_THUNK_SIZE, sizeof(article_header)),
  1419. X              (long)ART_THUNK_SIZE,
  1420. X              "article headers");
  1421. X
  1422. X    current_art_t = current_art_t->next_thunk;
  1423. X    next_art = (article_header *)current_art_t->this_thunk;
  1424. X    cur_art_size = current_art_t->thunk_size;
  1425. X    }
  1426. X
  1427. X    cur_art_size--;
  1428. X    return next_art++;
  1429. X}
  1430. X
  1431. X/*
  1432. X * allocate a string of length 'len'
  1433. X */
  1434. X
  1435. X#ifndef STR_THUNK_SIZE
  1436. X#define STR_THUNK_SIZE    ((1<<14) - 32)    /* leave room for malloc header */
  1437. X#endif
  1438. X
  1439. Xchar *alloc_str(len)
  1440. Xint len;
  1441. X{
  1442. X    char *ret;
  1443. X    
  1444. X    if (cur_str_size <= len) {    /* must be room for len+1 bytes */
  1445. X    if (current_str_t->next_thunk == NULL)
  1446. X        new_thunk(current_str_t, 
  1447. X              malloc(STR_THUNK_SIZE),
  1448. X              STR_THUNK_SIZE,
  1449. X              "string bytes");
  1450. X
  1451. X    current_str_t = current_str_t->next_thunk;
  1452. X    next_str = current_str_t->this_thunk;
  1453. X    cur_str_size = current_str_t->thunk_size;
  1454. X    }
  1455. X
  1456. X    ret = next_str;
  1457. X    cur_str_size -= len + 1;
  1458. X    next_str += len;
  1459. X    *next_str++ = NUL;    /* string is null terminated */
  1460. X    
  1461. X    return ret;
  1462. X}    
  1463. X
  1464. X/*
  1465. X * "free" the allocated memory
  1466. X */
  1467. X
  1468. Xfree_memory()
  1469. X{
  1470. X    current_str_t = first_str_t;
  1471. X    current_art_t = first_art_t;
  1472. X    cur_str_size  = 0;
  1473. X    cur_art_size  = 0;
  1474. X    n_articles      = 0;
  1475. X}    
  1476. X
  1477. X
  1478. X/*
  1479. X * mark/release memory
  1480. X */
  1481. X
  1482. X
  1483. Xmark_str(str_marker)
  1484. Xstring_marker *str_marker;
  1485. X{
  1486. X    str_marker->sm_cur_t = current_str_t;
  1487. X    str_marker->sm_size  = cur_str_size;
  1488. X    str_marker->sm_next  = next_str;
  1489. X}
  1490. X
  1491. Xrelease_str(str_marker)
  1492. Xstring_marker *str_marker;
  1493. X{
  1494. X    current_str_t = str_marker->sm_cur_t;
  1495. X    cur_str_size  = str_marker->sm_size;
  1496. X    next_str      = str_marker->sm_next;
  1497. X}
  1498. X
  1499. X
  1500. Xmark_memory(mem_marker)
  1501. Xmemory_marker *mem_marker;
  1502. X{
  1503. X    mark_str(&(mem_marker->mm_string));
  1504. X    
  1505. X    mem_marker->mm_cur_t = current_art_t;
  1506. X    mem_marker->mm_size  = cur_art_size;
  1507. X    mem_marker->mm_next  = next_art;
  1508. X    
  1509. X    mem_marker->mm_nart     = n_articles;
  1510. X    mem_offset += n_articles;
  1511. X
  1512. X    n_articles = 0;
  1513. X    articles = art_array + mem_offset;
  1514. X}
  1515. X
  1516. Xrelease_memory(mem_marker)
  1517. Xmemory_marker *mem_marker;
  1518. X{
  1519. X    release_str(&(mem_marker->mm_string));
  1520. X    
  1521. X    current_art_t = mem_marker->mm_cur_t;
  1522. X    cur_art_size  = mem_marker->mm_size;
  1523. X    next_art      = mem_marker->mm_next;
  1524. X    
  1525. X    n_articles = mem_marker->mm_nart;
  1526. X
  1527. X    mem_offset -= n_articles;
  1528. X    articles = art_array + mem_offset;
  1529. X}
  1530. X
  1531. X/*
  1532. X * merge all memory chunks into one.
  1533. X */
  1534. X
  1535. Xmerge_memory()
  1536. X{
  1537. X    n_articles += mem_offset;
  1538. X    mem_offset = 0;
  1539. X    articles = art_array;
  1540. X}
  1541. X
  1542. X
  1543. X/*
  1544. X * save article header in 'articles' array
  1545. X * 'articles' is enlarged if too small
  1546. X */
  1547. X
  1548. X#define    FIRST_ART_ARRAY_SIZE    500    /* malloc header */
  1549. X#define    NEXT_ART_ARRAY_SIZE    512
  1550. X
  1551. Xadd_article(art)
  1552. Xarticle_header *art;
  1553. X{
  1554. X    register long n;
  1555. X    
  1556. X    if ((n_articles + mem_offset) == max_articles) {
  1557. X    /* must increase size of 'articles' */
  1558. X
  1559. X    if (max_articles == 0) {
  1560. X        /* allocate initial 'articles' array */
  1561. X        max_articles = FIRST_ART_ARRAY_SIZE;
  1562. X        n = 0;
  1563. X    } else {
  1564. X        new_thunk(current_str_t, 
  1565. X              (char *)art_array, 
  1566. X              (long)(max_articles*sizeof(article_header **)),
  1567. X              "");
  1568. X        n = max_articles;
  1569. X        articles = art_array + n;
  1570. X        
  1571. X        max_articles += NEXT_ART_ARRAY_SIZE;
  1572. X    }
  1573. X    art_array = (article_header **)
  1574. X        calloc(max_articles, sizeof(article_header **));
  1575. X    mem_check(art_array, (int)max_articles, "article headers");
  1576. X    while (--n >= 0) art_array[n] = *--articles;
  1577. X    articles = art_array + mem_offset;
  1578. X    }
  1579. X    
  1580. X    articles[n_articles] = art;
  1581. X    n_articles++;
  1582. X}
  1583. X
  1584. X
  1585. Xstatic char match_subject[128] = {
  1586. X    
  1587. X/*  NUL SOH STX ETX EOT ENQ ACK BEL BS  TAB NL  VT  FF  CR  SO  SI  */
  1588. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 
  1589. X
  1590. X/*  DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM  SUB ESC FS  GS  RS  US  */
  1591. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 
  1592. X
  1593. X/*  SP  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
  1594. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 99, 00, 00, 00, 00, 
  1595. X/*                                              ^^                  */
  1596. X
  1597. X/*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
  1598. X     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 00, 00, 00, 00, 00, 00, 
  1599. X
  1600. X/*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
  1601. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  1602. X
  1603. X/*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
  1604. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00, 
  1605. X
  1606. X/*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
  1607. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  1608. X
  1609. X/*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   DEL */
  1610. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
  1611. X
  1612. X};
  1613. X
  1614. X
  1615. Xstatic article_comp(ah1, ah2)
  1616. Xarticle_header **ah1, **ah2;
  1617. X{
  1618. X    register char *a = (**ah1).subject, *b = (**ah2).subject;
  1619. X    register p;
  1620. X    
  1621. X    for (;; a++, b++) {
  1622. X    while (*a && MATCH_DROP(match_subject, *a)) a++;
  1623. X    while (*b && MATCH_DROP(match_subject, *b)) b++; 
  1624. X    if (*a == NUL) {
  1625. X        if (*b) return -1;
  1626. X        break;
  1627. X    }
  1628. X    if (*b == NUL) return 1;
  1629. X    if (p = MATCH_CMP(match_subject, *a, *b)) return p;
  1630. X    }
  1631. X/*    
  1632. X    if (p = (**ah1).replies - (**ah2).replies) return p;
  1633. X */
  1634. X    if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  1635. X    if ((**ah1).t_stamp == (**ah2).t_stamp) return 0;
  1636. X    return -1;
  1637. X}
  1638. X
  1639. X
  1640. Xstatic article_equal(ah1, ah2)        /* ah1.hdr == ah2.hdr */
  1641. Xarticle_header **ah1, **ah2;
  1642. X{
  1643. X    register char *a = (**ah1).subject, *b = (**ah2).subject;
  1644. X    
  1645. X    for (;; a++, b++) {
  1646. X    while (*a && MATCH_DROP(match_subject, *a)) a++;
  1647. X    while (*b && MATCH_DROP(match_subject, *b)) b++; 
  1648. X    if (*a == NUL) {
  1649. X        if (*b == NUL) break;
  1650. X        goto not_equal;
  1651. X    }
  1652. X    if (*b == NUL) goto not_equal;
  1653. X    if (MATCH_EQ(match_subject, *a, *b)) continue;
  1654. X    goto not_equal;
  1655. X    }
  1656. X    
  1657. X    return 1;
  1658. X    
  1659. X not_equal:
  1660. X    return 0;
  1661. X}
  1662. X
  1663. X
  1664. Xsort_articles()
  1665. X{
  1666. X    register article_header **app;
  1667. X    register long n;
  1668. X    
  1669. X    if (n_articles <= 1) return;
  1670. X
  1671. X    qsort(articles, n_articles, sizeof(article_header *), article_comp);
  1672. X    
  1673. X    for (n = n_articles - 1, app = articles + 1; --n >= 0; app++)
  1674. X    if (article_equal(app, app - 1)) (**app).flag |= A_SAME;
  1675. X}
  1676. X
  1677. X    
  1678. Xstatic offset_cmp(a, b)
  1679. Xarticle_header **a, **b;
  1680. X{
  1681. X    register i;
  1682. X    
  1683. X    if (i = (int)((*a)->a_number - (*b)->a_number))
  1684. X    return i;
  1685. X    
  1686. X    return (int)((*a)->fpos - (*b)->fpos);
  1687. X}
  1688. X
  1689. Xstatic age_cmp(ah1, ah2)
  1690. Xarticle_header **ah1, **ah2;
  1691. X{
  1692. X    if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
  1693. X    if ((**ah1).t_stamp == (**ah2).t_stamp) return 0;
  1694. X    return -1;
  1695. X}
  1696. X
  1697. X
  1698. Xunsort_articles(arrival)
  1699. X{
  1700. X    register int i;
  1701. X    
  1702. X    for (i = n_articles; --i >= 0;)
  1703. X    articles[i]->flag &= ~A_SAME;
  1704. X    
  1705. X    if (n_articles <= 1) return;
  1706. X    qsort(articles, n_articles, sizeof(article_header *), 
  1707. X      arrival ? offset_cmp : age_cmp);
  1708. X}
  1709. X
  1710. NO_NEWS_IS_GOOD_NEWS
  1711. chmod 0644 articles.c || echo "restore of articles.c fails"
  1712. set `wc -c articles.c`;Sum=$1
  1713. if test "$Sum" != "7942"
  1714. then echo original size 7942, current size $Sum;fi
  1715. echo "x - extracting articles.h (Text)"
  1716. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > articles.h &&
  1717. X/*
  1718. X * memory handling
  1719. X */
  1720. X
  1721. X/* article headers */
  1722. X
  1723. Xarticle_number    n_articles;
  1724. Xarticle_header     **articles;
  1725. X
  1726. X/* number of articles killed by last access_group call */
  1727. X
  1728. Xint         killed_articles;
  1729. X
  1730. Xtypedef struct thunk {
  1731. X    char      *this_thunk;
  1732. X    struct thunk  *next_thunk;
  1733. X    long      thunk_size;
  1734. X} thunk;
  1735. X
  1736. X
  1737. Xtypedef struct {
  1738. X    thunk *sm_cur_t;
  1739. X    int      sm_size;
  1740. X    char  *sm_next;
  1741. X} string_marker;
  1742. X
  1743. X
  1744. Xtypedef struct {
  1745. X    string_marker mm_string;
  1746. X    thunk *mm_cur_t;
  1747. X    int      mm_size;
  1748. X    article_header *mm_next;
  1749. X    long mm_nart;
  1750. X} memory_marker;
  1751. X
  1752. X
  1753. Xextern article_header *alloc_art();
  1754. Xextern char *alloc_str();
  1755. NO_NEWS_IS_GOOD_NEWS
  1756. chmod 0644 articles.h || echo "restore of articles.h fails"
  1757. set `wc -c articles.h`;Sum=$1
  1758. if test "$Sum" != "611"
  1759. then echo original size 611, current size $Sum;fi
  1760. echo "x - extracting aux.sh (Text)"
  1761. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > aux.sh &&
  1762. X
  1763. X# CONFIG is inserted above this line during INSTALL
  1764. X
  1765. Xtrap : 2 3
  1766. X
  1767. XPATH=/bin:$PATH
  1768. Xexport PATH
  1769. X
  1770. X# first argument is operation to be performed:
  1771. X# when saved, we shift it off
  1772. X
  1773. XOPERATION=$1
  1774. Xshift
  1775. X
  1776. X
  1777. X# first we handle 'cancel'
  1778. X# $1 is ident, $2 is group name.
  1779. X
  1780. Xif [ "$OPERATION" = "cancel" ] ; then
  1781. X    $INEWS -t 'cmsg cancel '"$1" -n "$2" < /dev/null > /tmp/nn$$c 2>&1
  1782. X    x=$?
  1783. X    if [ $x != 0 ]; then
  1784. X        echo ''
  1785. X        cat /tmp/nn$$c
  1786. X        sleep 2
  1787. X    fi
  1788. X    rm -f /tmp/nn$$c
  1789. X    exit $x
  1790. Xfi
  1791. X
  1792. XWORK="$1"
  1793. XED_LINE="$2"
  1794. XFIRST_ACTION="$3"
  1795. XRECORD="$4"
  1796. X
  1797. XTRACE=${WORK}T
  1798. XCOPY=""
  1799. X
  1800. Xif [ "${FIRST_ACTION}" = "edit" ] ; then
  1801. X    COPY=${WORK}C
  1802. X    cp $WORK $COPY
  1803. Xfi
  1804. X
  1805. X# loop until sent or aborted.
  1806. X
  1807. Xloop=true
  1808. Xprompt=false
  1809. Xwhile $loop ; do
  1810. X    if $prompt ; then
  1811. X        echo ''
  1812. X        awk 'END{printf "Action: (a)bort (e)dit (o)k (s)end: "}' < /dev/null
  1813. X        read act
  1814. X    else
  1815. X        act="${FIRST_ACTION}"
  1816. X        prompt=true
  1817. X    fi
  1818. X
  1819. X    case $act in
  1820. X    a*) 
  1821. X        rm -f $WORK $COPY
  1822. X        exit 22
  1823. X        ;;
  1824. X    e*)
  1825. X        # call editor to enter at line $2 of work file
  1826. X
  1827. X        case `basename "${EDITOR-vi}"` in
  1828. X        vi|emacs )
  1829. X            # Berkeley vi display editor
  1830. X            # GNU emacs disply editor
  1831. X            ${EDITOR-vi} +${ED_LINE} $WORK
  1832. X            ;;
  1833. X        ded )
  1834. X            # QMC ded display editor
  1835. X            $EDITOR -l${ED_LINE} $WORK
  1836. X            ;;
  1837. X        uemacs )
  1838. X            # micro emacs
  1839. X            $EDITOR -g${ED_LINE} $WORK
  1840. X            ;;
  1841. X        * )
  1842. X            # Unknown editor
  1843. X            $EDITOR $WORK
  1844. X            ;;
  1845. X        esac
  1846. X        ;;
  1847. X
  1848. X    o*|s*)
  1849. X        loop=false
  1850. X        ;;
  1851. X
  1852. X    esac
  1853. Xdone
  1854. X
  1855. Xif [ -n "$COPY" ] ; then
  1856. X    if [ -s $WORK ] ; then
  1857. X        if cmp -s $WORK $COPY ; then
  1858. X            rm -f $WORK $COPY
  1859. X            exit 22
  1860. X        fi
  1861. X    else
  1862. X        rm -f $WORK $COPY
  1863. X        exit 22
  1864. X    fi
  1865. X        
  1866. X    rm -f $COPY
  1867. Xfi
  1868. X
  1869. Xcase "$OPERATION" in
  1870. Xreply|forward|mail)
  1871. X    if $APPENDSIG ; then
  1872. X        if [ -f $HOME/.signature ] ; then
  1873. X        awk 'END{printf "Append .signature? (y) : "}' < /dev/null
  1874. X        read ans
  1875. X        case $ans in
  1876. X        ''|y*|Y*)
  1877. X            echo "--" >> $WORK
  1878. X            cat $HOME/.signature >> $WORK
  1879. X            ;;
  1880. X        esac
  1881. X        fi
  1882. X    fi
  1883. X    ;;
  1884. Xfollow|post)
  1885. X    echo "Be patient! Your new article will not show up immediately."
  1886. X    sleep 2
  1887. X    ;;
  1888. Xesac
  1889. X
  1890. X{
  1891. X    trap 'echo SIGNAL' 1 2 3
  1892. X
  1893. X    case "$OPERATION" in
  1894. X
  1895. X    reply|forward|mail)
  1896. X        grep -v "^Orig-To: " $WORK | $RECMAIL
  1897. X        x=$?
  1898. X        ;;
  1899. X
  1900. X    follow|post)
  1901. X        grep -v "^Orig-To: " $WORK | $INEWS -h
  1902. X        x=$?
  1903. X        # wait for inews to finish
  1904. X        sleep 60
  1905. X        ;;
  1906. X
  1907. X    *)
  1908. X        echo "Invalid operation: $OPERATION -- help"
  1909. X        OPERATION="nn response operation"
  1910. X        x=1
  1911. X        cat > /dev/null
  1912. X        ;;
  1913. X
  1914. X    esac > $TRACE 2>&1
  1915. X
  1916. X
  1917. X    if [ 0"$x" -ne 0 -o -s $TRACE ] ; then
  1918. X        if [ -s $HOME/dead.letter ] ; then
  1919. X            cat $HOME/dead.letter >> $HOME/dead.letters
  1920. X            echo '' >> $HOME/dead.letters
  1921. X        fi
  1922. X        grep -v "^Orig-To: " $WORK > $HOME/dead.letter
  1923. X        {
  1924. X        echo "To: ${LOGNAME-$USER}"
  1925. X        echo "Subject: $OPERATION failed"
  1926. X        echo ''
  1927. X        cat $TRACE
  1928. X        echo ''
  1929. X        echo 'Your response has been saved in ~/dead.letter'
  1930. X        echo ''
  1931. X        echo 'Your article/letter follows:'
  1932. X        cat $WORK
  1933. X        } | $RECMAIL
  1934. X        
  1935. X    elif [ -n "${RECORD}" ] ; then
  1936. X        {
  1937. X        # keep a copy of message in $RECORD (in mail format)
  1938. X        set `date`
  1939. X        if [ $3 -gt 9 ] ; then
  1940. X            echo From ${LOGNAME-$USER} $1 $2 $3 $4 $6 $7
  1941. X        else
  1942. X            echo From ${LOGNAME-$USER} $1 $2 ' '$3 $4 $6 $7
  1943. X        fi
  1944. X        echo "From: ${LOGNAME-$USER}"
  1945. X        grep -v '^Orig-To: ' $WORK
  1946. X        echo '' 
  1947. X        } >> "$RECORD"
  1948. X    fi
  1949. X
  1950. X    rm -f $WORK $TRACE
  1951. X
  1952. X} > /dev/null 2>&1 &
  1953. X
  1954. Xexit 0
  1955. NO_NEWS_IS_GOOD_NEWS
  1956. chmod 0644 aux.sh || echo "restore of aux.sh fails"
  1957. set `wc -c aux.sh`;Sum=$1
  1958. if test "$Sum" != "3107"
  1959. then echo original size 3107, current size $Sum;fi
  1960. echo "x - extracting back_act.sh (Text)"
  1961. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > back_act.sh &&
  1962. X# prefix is inserted above by make
  1963. X
  1964. X#
  1965. X#    back_act will maintain a set of `old' active files
  1966. X#    in the DB directory where they can be used by nngoback
  1967. X#    to backtrack the rc file a number of days.
  1968. X#
  1969. X#    It should be invoked by cron every day at midnight.
  1970. X#    It should run as user `news'!
  1971. X#
  1972. X
  1973. Xcd $DB || exit 1
  1974. X
  1975. Xp=15
  1976. Xl=""
  1977. Xfor i in 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
  1978. Xdo
  1979. X    if [ -f active.$i ]
  1980. X    then
  1981. X        mv active.$i active.$p
  1982. X        l=$p
  1983. X    elif [ -n "$l" ]
  1984. X    then
  1985. X        ln active.$l active.$p
  1986. X    fi
  1987. X    p=$i
  1988. Xdone
  1989. X
  1990. Xcp $ACTIVE active.0
  1991. Xchmod 644 active.0
  1992. NO_NEWS_IS_GOOD_NEWS
  1993. chmod 0644 back_act.sh || echo "restore of back_act.sh fails"
  1994. set `wc -c back_act.sh`;Sum=$1
  1995. if test "$Sum" != "522"
  1996. then echo original size 522, current size $Sum;fi
  1997. echo "x - extracting collect.c (Text)"
  1998. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > collect.c &&
  1999. X#include "config.h"
  2000. X#include "db.h"
  2001. X#include "news.h"
  2002. X
  2003. Ximport int trace;
  2004. X
  2005. X
  2006. Xstatic FILE *ix, *data;
  2007. X
  2008. X/*
  2009. X *    Collect unread articles in current group
  2010. X *
  2011. X *    On entry, init_group has been called to setup the proper environment
  2012. X */
  2013. X
  2014. Xcollect_group(gh)
  2015. Xregister group_header *gh;
  2016. X{
  2017. X    int article_count, temp;
  2018. X    article_number start_collect;
  2019. X    
  2020. X    if (gh->last_l_article == 0) {
  2021. X    gh->first_l_article = gh->first_article;
  2022. X    gh->last_l_article = gh->first_l_article - 1;
  2023. X    }
  2024. X
  2025. X    if (gh->last_l_article >= gh->last_article) return 0;
  2026. X    
  2027. X    if (gh->index_write_offset) {
  2028. X    ix = open_data_file(gh, 'x', OPEN_UPDATE|MUST_EXIST);
  2029. X    fseek(ix, gh->index_write_offset, 0);
  2030. X    } else
  2031. X        ix = open_data_file(gh, 'x', OPEN_CREATE|MUST_EXIST);
  2032. X
  2033. X    if (gh->data_write_offset) {
  2034. X    data = open_data_file(gh, 'd', OPEN_UPDATE|MUST_EXIST);
  2035. X    fseek(data, gh->data_write_offset, 0);
  2036. X    } else
  2037. X    data = open_data_file(gh, 'd', OPEN_CREATE|MUST_EXIST);
  2038. X
  2039. X    article_count = 0;
  2040. X    start_collect = gh->last_l_article+1;
  2041. X    
  2042. X    while (gh->last_l_article < gh->last_article) {
  2043. X    if (s_hangup) break;
  2044. X    gh->last_l_article++;
  2045. X    gh->data_write_offset = ftell(data);
  2046. X#ifdef NNTP
  2047. X    gh->index_write_offset = ftell(ix);
  2048. X#endif
  2049. X    if (!db_write_offset(ix, &(gh->data_write_offset)))
  2050. X        write_error();
  2051. X
  2052. X    temp = collect_article(gh, gh->last_l_article);
  2053. X#ifdef NNTP
  2054. X    if (temp < 0) {
  2055. X        /* connection failed, current article is not collected */
  2056. X        gh->last_l_article--;
  2057. X        article_count = -1;
  2058. X        goto out;
  2059. X    }
  2060. X#endif    
  2061. X    article_count += temp;
  2062. X    }
  2063. X
  2064. X    if (trace && start_collect <= gh->last_l_article) {
  2065. X    log_entry('T', "Col %s (%d to %d) %d", 
  2066. X          gh->group_name, 
  2067. X          start_collect, gh->last_l_article,
  2068. X          article_count);
  2069. X    fl;
  2070. X    }
  2071. X    
  2072. X    gh->data_write_offset = ftell(data);
  2073. X    gh->index_write_offset = ftell(ix);
  2074. X
  2075. X out:
  2076. X    fclose(data);
  2077. X    fclose(ix);
  2078. X    
  2079. X    return article_count;
  2080. X}
  2081. X
  2082. X
  2083. Xstatic data_header hdr;
  2084. X
  2085. X
  2086. Xstatic collect_article(gh, art_num)
  2087. Xregister group_header *gh;
  2088. Xarticle_number art_num;
  2089. X{
  2090. X    FILE *art_file;
  2091. X    news_header_buffer nhbuf, dgbuf;
  2092. X    article_header art_hdr;
  2093. X    int mode, count;
  2094. X    cross_post_number cross_post_table[256], *cp_ptr;
  2095. X    count = 0;
  2096. X    
  2097. X    hdr.dh_number = art_num;
  2098. X    
  2099. X    /* get article header */
  2100. X
  2101. X    art_hdr.a_number = art_num;
  2102. X    art_hdr.hpos = (off_t)0;
  2103. X    art_hdr.lpos = (off_t)0;
  2104. X
  2105. X    mode = FILL_NEWS_HEADER | FILL_OFFSETS | SKIP_HEADER; 
  2106. X    if ((gh->group_flag & (G_CONTROL | G_NEVER_DIGEST | G_ALWAYS_DIGEST)) == 0)
  2107. X    mode |= DIGEST_CHECK;
  2108. X    
  2109. X    if ((art_file = open_news_article(&art_hdr, mode, nhbuf, (char *)NULL)) == NULL) {
  2110. X
  2111. X#ifdef NNTP
  2112. X    import nntp_failed;
  2113. X    
  2114. X    if (nntp_failed) {
  2115. X        /* 
  2116. X         * connection to nntp_server is broken 
  2117. X         * stop collection of articles immediately
  2118. X         */
  2119. X        return -1;
  2120. X    }
  2121. X#endif    
  2122. X    /* 
  2123. X     * it is not really necessary to save anything in the data file
  2124. X     * we simply use the index file to get the *first* available article
  2125. X     */
  2126. X    return 1;    /* but we have still collected one article */
  2127. X    }
  2128. X
  2129. X    /* map cross-postings into a list of group numbers */
  2130. X
  2131. X    hdr.dh_cross_postings = 0;
  2132. X    
  2133. X    if (gh->group_flag & G_CONTROL) {
  2134. X    /* we cannot trust the Newsgroups: line in the control group */
  2135. X    /* so we simply ignore it (i.e. use "Newsgroups: control") */
  2136. X    goto dont_digest;
  2137. X    }
  2138. X
  2139. X    if (news.ng_groups) {
  2140. X    char *curg, *nextg;
  2141. X    group_header *gh1;
  2142. X    
  2143. X    for (nextg = news.ng_groups, cp_ptr = cross_post_table; *nextg; ) {
  2144. X        curg = nextg;
  2145. X        
  2146. X        if (nextg = strchr(curg, ','))
  2147. X        *nextg++ = NUL;
  2148. X        else
  2149. X        nextg = "";
  2150. X        
  2151. X        if (strcmp(gh->group_name, curg) == 0) break;
  2152. X
  2153. X        if ((gh1 = lookup(curg)) == NULL) continue;
  2154. X
  2155. X        *cp_ptr++ = gh1->group_num;
  2156. X        hdr.dh_cross_postings++;
  2157. X    }
  2158. X    }
  2159. X    
  2160. X    if (gh->group_flag & G_NEVER_DIGEST)
  2161. X    goto dont_digest;
  2162. X
  2163. X    /* split digest */
  2164. X
  2165. X    
  2166. X    if ((gh->group_flag & G_ALWAYS_DIGEST) || (news.ng_flag & N_DIGEST)) {
  2167. X    int any = 0, cont = 1;
  2168. X    
  2169. X    skip_digest_body(art_file);
  2170. X        
  2171. X    while (cont && (cont = get_digest_article(art_file, dgbuf)) >= 0) {
  2172. X
  2173. X        if (any == 0) {
  2174. X        /* write DIGEST_HEADER */
  2175. X        build_hdr(2, -art_num, cross_post_table);
  2176. X        count++;
  2177. X
  2178. X        hdr.dh_cross_postings = 0;    /* no cross post in sub */
  2179. X        any++;
  2180. X        }
  2181. X        /* write SUB_DIGEST */
  2182. X        build_hdr(1, (article_number)0, (group_number *)NULL);
  2183. X        count++;
  2184. X    }
  2185. X
  2186. X    if (any) goto finish;
  2187. X    }
  2188. X    
  2189. X    /* not a digest */
  2190. X
  2191. X dont_digest:
  2192. X
  2193. X    build_hdr(0, art_num, cross_post_table);    /* normal article */
  2194. X    count++;
  2195. X    
  2196. Xfinish:    
  2197. X    
  2198. X    fclose(art_file);
  2199. X
  2200. X    return count;
  2201. X}
  2202. X
  2203. X
  2204. Xstatic build_hdr(use_digest, art_num, cross_post_table)
  2205. Xint use_digest;
  2206. Xarticle_number art_num;
  2207. Xcross_post_number *cross_post_table;
  2208. X{
  2209. X    register char *name, *subj;
  2210. X    char name_buf[NAME_LENGTH+1], subj_buf[256];
  2211. X    int re;
  2212. X    
  2213. X    if (use_digest & 1) {
  2214. X    
  2215. X    name = digest.dg_from;
  2216. X    subj = digest.dg_subj;
  2217. X
  2218. X    hdr.dh_lines = digest.dg_lines;
  2219. X
  2220. X    hdr.dh_hpos = digest.dg_hpos;
  2221. X    hdr.dh_fpos = (int16)(digest.dg_fpos - hdr.dh_hpos);
  2222. X    hdr.dh_lpos = digest.dg_lpos;
  2223. X    
  2224. X    pack_date(&(hdr.dh_date),
  2225. X          digest.dg_date ? digest.dg_date : news.ng_date);
  2226. X    } else {
  2227. X    
  2228. X    if (!news.ng_from) news.ng_from = news.ng_reply;
  2229. X    
  2230. X    name = news.ng_from;
  2231. X    subj = news.ng_subj;
  2232. X    
  2233. X    hdr.dh_lines = news.ng_lines;
  2234. X    
  2235. X    hdr.dh_hpos = 0;
  2236. X    hdr.dh_fpos = (int16)(news.ng_fpos);
  2237. X    hdr.dh_lpos = news.ng_lpos;
  2238. X
  2239. X    pack_date(&(hdr.dh_date), news.ng_date);
  2240. X    }
  2241. X    
  2242. X    hdr.dh_number = art_num;    
  2243. X
  2244. X    /* pack name and write on .nn2 */
  2245. X
  2246. X    if (name) {
  2247. X    hdr.dh_sender_length = pack_name(name_buf, name, NAME_LENGTH);
  2248. X    } else
  2249. X        hdr.dh_sender_length = 0;
  2250. X    
  2251. X    /* write subject line on .nn2 */
  2252. X
  2253. X    hdr.dh_subject_length = pack_subject(subj_buf, subj, &re, 255);
  2254. X    hdr.dh_replies = re;
  2255. X
  2256. X    if (use_digest & 2) hdr.dh_subject_length++;    /* @ */
  2257. X
  2258. X    /* WRITE hdr, cross postings, name, subject */
  2259. X
  2260. X    db_write_art(data, &hdr);
  2261. X
  2262. X    if (cross_post_table && hdr.dh_cross_postings) {
  2263. X#ifdef NETWORK_DATABASE
  2264. X#ifndef NETWORK_BYTE_ORDER
  2265. X    int i;
  2266. X    
  2267. X    for (i = 0; i < hdr.dh_cross_postings; i++)
  2268. X        cross_post_table[i] = htonl(cross_post_table[i]);
  2269. X#endif
  2270. X#endif    
  2271. X    Fwrite((char *)cross_post_table, sizeof(cross_post_number), 
  2272. X           (int)hdr.dh_cross_postings, data);
  2273. X    }
  2274. X    
  2275. X    if (hdr.dh_sender_length)
  2276. X    Fwrite(name_buf, sizeof(char), (int)hdr.dh_sender_length, data);
  2277. X
  2278. X    if (use_digest & 2) {
  2279. NO_NEWS_IS_GOOD_NEWS
  2280. echo "End of part 2"
  2281. echo "File collect.c is continued in part 3"
  2282. echo "3" > s2_seq_.tmp
  2283. exit 0
  2284. ---
  2285. Kim F. Storm        storm@texas.dk        Tel +45 429 174 00
  2286. Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
  2287.       No news is good news, but nn is better!
  2288.  
  2289.