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

  1. Subject:  v19i064:  NN, a Usenet news reader, Part03/15
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: mcvax!tidk!storm@uunet.UU.NET (Kim F. Storm)
  7. Posting-number: Volume 19, Issue 64
  8. Archive-name: nn/part03
  9.  
  10. #!/bin/sh
  11. # this is part 3 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file collect.c continued
  14. #
  15. CurArch=3
  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 collect.c"
  26. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> collect.c
  27. X    putc('@', data);
  28. X    hdr.dh_subject_length--;
  29. X    }
  30. X    
  31. X    if (hdr.dh_subject_length)
  32. X    Fwrite(subj_buf, sizeof(char), (int)hdr.dh_subject_length, data);
  33. X
  34. X    return;
  35. X}
  36. NO_NEWS_IS_GOOD_NEWS
  37. echo "File collect.c is complete"
  38. chmod 0644 collect.c || echo "restore of collect.c fails"
  39. set `wc -c collect.c`;Sum=$1
  40. if test "$Sum" != "6420"
  41. then echo original size 6420, current size $Sum;fi
  42. echo "x - extracting config.h-dist (Text)"
  43. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > config.h-dist &&
  44. X/**************************** NN CONFIGURATION ***************************
  45. X *
  46. X *    Configuration file for nn
  47. X *
  48. X *    You must edit this file to reflect your local configuration
  49. X *    and environment.
  50. X *
  51. X *    Follow the instructions given in the comments.  See the files
  52. X *    INSTALLATION, NNTP, and PROBLEMS for more details.
  53. X */
  54. X
  55. X#define    RELEASE     6
  56. X#define    VERSION     3
  57. X
  58. X
  59. X#include <stdio.h>
  60. X#include <ctype.h>
  61. X
  62. X
  63. X/*********************** NETWORK DEPENDENT DEFINITIONS **********************
  64. X *
  65. X *    Define NETWORK_DATABASE if you share the database through NFS on 
  66. X *    a network with different, non-compatible machines, e.g. SUNs and
  67. X *    VAXen, or SUN-3 and SUN-4, or if you are using different compilers
  68. X *    on the same architecture.
  69. X *
  70. X *    In a homogenous network, you can leave it undefined for higher
  71. X *    performance (no data conversion is needed).
  72. X */
  73. X
  74. X/* #define NETWORK_DATABASE    /* */
  75. X
  76. X
  77. X/********************************** NNTP *********************************
  78. X *
  79. X *     Define NNTP to enable nntp support.
  80. X *
  81. X *    With NNTP, the nnmaster still maintains a local database of 
  82. X *    all article headers for fast access (and because NNTP does not 
  83. X *    support nn - yet), while the articles are fetched from the
  84. X *    nntp server when they are read or saved.
  85. X *
  86. X *    You may still share this database through NFS locally (see the 
  87. X *    description of NETWORK_DATABASE above) if you don't want to
  88. X *    have separate nn databases on all your local systems.
  89. X *
  90. X *    Consult the file NNTP for further information on the use of NNTP.
  91. X */
  92. X
  93. X/* #define NNTP            /* */
  94. X
  95. X/* 
  96. X *     Define NNTP_SERVER to the name of a file containing the name of the
  97. X *     nntp server.
  98. X *
  99. X *     It is vital that both the nnmaster and all nn users on a machine 
  100. X *    uses the same nntp server, because the nn database is synchronized
  101. X *    with a specific news active file. 
  102. X *
  103. X *    If the file name does not start with a slash, it is relative to 
  104. X *    LIB_DIRECTORY defined below.
  105. X */
  106. X
  107. X#define NNTP_SERVER    "/usr/lib/nntp_server"
  108. X
  109. X
  110. X/***************** OPERATING SYSTEM DEPENDENT DEFINITIONS *******************
  111. X *
  112. X *      Include the appropriate s- file for your system below.
  113. X *
  114. X *    If a file does not exist for your system, you can use s-template.h
  115. X *    as a starting point for writing you own.
  116. X */
  117. X
  118. X#include "s-usg3-1.h"
  119. X
  120. X
  121. X/********************** MACHINE DEPENDENT DEFINITIONS **********************
  122. X *
  123. X *    Include the appropriate m- file for your system below.
  124. X *    
  125. X *    If a file does not exist for your system, you can use m-template.h
  126. X *    as a starting point for writing you own.
  127. X */
  128. X
  129. X#include "m-m680x0.h"
  130. X
  131. X
  132. X/******************** SITE DEPENDENT DEFINITIONS **********************
  133. X *
  134. X *    Edit the following part to suit your local system setup
  135. X */
  136. X
  137. X/*
  138. X *    Specify where programs and data should be placed
  139. X *
  140. X *    BIN_DIRECTORY   - the location of the user programs
  141. X *    LIB_DIRECTORY    - the location of auxiliary programs and files
  142. X *    DB_DIRECTORY    - the directory containing the nn database
  143. X *
  144. X *
  145. X * notice: if you share the news directory accross a network, you should
  146. X *       use something like /usr/spool/news/.nn for DB_DIRECTORY.
  147. X */
  148. X
  149. X#define BIN_DIRECTORY    "/usr/local/bin"
  150. X#define LIB_DIRECTORY    "/usr/local/lib/nn"
  151. X#define DB_DIRECTORY    "/usr/spool/nn"
  152. X
  153. X/*
  154. X *     Specify directories for the user and system manuals
  155. X *
  156. X *     Adapt this to your local standards; the manuals will be named 
  157. X *         $(MAN_DIR)/program.$(MAN_SECTION)
  158. X */
  159. X
  160. X#define USER_MAN_DIR     "/usr/man/man1"
  161. X#define USER_MAN_SECTION     "1"
  162. X
  163. X#define SYS_MAN_DIR     "/usr/man/man1"
  164. X#define SYS_MAN_SECTION     "1m"
  165. X
  166. X/*
  167. X *    Specify where to put temporary files.  Overriden by $TMPDIR.
  168. X *    Notice that nn does not create "large" temp files.
  169. X */
  170. X
  171. X#define TMP_DIRECTORY    "/tmp"
  172. X
  173. X/*
  174. X * Specify owner and group for files belonging to this package.
  175. X *
  176. X * Specifically, the nnmaster will run suid/sgid to this owner and group.
  177. X *
  178. X * The only requirements are that the ownership allows the nnmaster to
  179. X * READ the news related files and directories, and the ordinary users
  180. X * to read the database and execute the nn* programs.
  181. X *
  182. X * Normal choices are:  (news, news)  and   (your uid, your gid)
  183. X */
  184. X
  185. X#define OWNER    "news"
  186. X#define    GROUP    "news"
  187. X
  188. X/*
  189. X *    Define STATISTICS if you want to keep a record of how much
  190. X *    time the users spend on news reading.
  191. X *
  192. X *    Sessions shorter than the specified number of minutes are not
  193. X *    recorded (don't clutter up the log file).
  194. X *
  195. X *    This is entered into the file $LIB_DIRECTORY/Log with code U
  196. X */
  197. X
  198. X/* #define STATISTICS    5 /* minutes */
  199. X
  200. X/*
  201. X *    Define HAVE_ROUTING if your mailer understands domain based
  202. X *    adresses (...@...) and performs the necessary rerouting (e.g. 
  203. X *    Sendmail or Smail).
  204. X *
  205. X *    Otherwise, nn will provide a simple routing facility using
  206. X *      routing information specified in the file LIB_DIRECTORY/routes.
  207. X */
  208. X
  209. X#define HAVE_ROUTING            /* */
  210. X
  211. X/*
  212. X *    If HAVE_ROUTING is NOT defined, nn needs to know the name of
  213. X *     your host.  To obtain the host name it will use either of the
  214. X *    'uname' or 'gethostname' system calls as specified in the s- file 
  215. X *    included above.
  216. X *
  217. X *    If neither 'uname' nor 'gethostname' is available, you must
  218. X *    define HOSTNAME to be the name of your host.  Otherwise, leave
  219. X *    it undefined (it will not be used anyway).
  220. X */
  221. X
  222. X/* #define HOSTNAME    "myhost"    /* Not used if HAVE_ROUTING */
  223. X
  224. X/*
  225. X *    Specify the location of your news programs and files
  226. X */
  227. X
  228. X#define    INEWS_PATH    "/usr/lib/news/inews"
  229. X#define NEWS_ACTIVE    "/usr/lib/news/active"
  230. X#define NEWS_DIRECTORY    "/usr/spool/news"
  231. X
  232. X/*
  233. X *    Specify a mailer that accepts a letter WITH a header IN THE TEXT.
  234. X *
  235. X *     A program named 'recmail' program is normally delivered with 
  236. X *    the news system.
  237. X *    On BSD systems you can also use "/usr/lib/sendmail -t".
  238. X */
  239. X
  240. X#define REC_MAIL    "/usr/lib/news/recmail"
  241. X
  242. X/*
  243. X *    Define APPEND_SIGNATURE if you want nn to ask users to append
  244. X *    ~/.signature to mail messages (reply/forward/mail).  
  245. X *    
  246. X *    If the mailer defined in REC_MAIL automatically includes .signature
  247. X *    you should not define this (it will fool people to include it twice).
  248. X *
  249. X *    I think 'recmail' includes .signature, but 'sendmail -t' doesn't.
  250. X */
  251. X
  252. X/* #define APPEND_SIGNATURE        /* */
  253. X
  254. X/*
  255. X *    Default folder directory
  256. X */
  257. X
  258. X#define FOLDER_DIRECTORY    "~/News"
  259. X
  260. X/*
  261. X *    Max length of authors name (in "edited" format).
  262. X *    Also size of "Name" field on the article menus.
  263. X *    You may want to increase this if your terminals are wider than
  264. X *    80 columns.
  265. X */
  266. X
  267. X#define NAME_LENGTH         16
  268. X
  269. X/*
  270. X *     Define RESIZING to make nn understand dynamic window-resizing.
  271. X *     (It uses the TIOCGWINSZ ioctl found on most 4.3BSD systems)
  272. X */
  273. X
  274. X/* #define RESIZING        /* */
  275. X
  276. X
  277. X/************************ CONFIGURATION COMPLETED ************************
  278. X *
  279. X *    The rest of this file will not need any changes.
  280. X */
  281. X
  282. X#include "global.h"
  283. NO_NEWS_IS_GOOD_NEWS
  284. chmod 0644 config.h-dist || echo "restore of config.h-dist fails"
  285. set `wc -c config.h-dist`;Sum=$1
  286. if test "$Sum" != "6667"
  287. then echo original size 6667, current size $Sum;fi
  288. echo "x - extracting cvt-help.c (Text)"
  289. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > cvt-help.c &&
  290. X#include <stdio.h>
  291. X
  292. Xmain()
  293. X{
  294. X    register int c;
  295. X
  296. X    while ((c = getchar()) != EOF) {
  297. X    if (c == ';') {
  298. X        c = getchar();
  299. X        if (c == ':') {
  300. X        c = getchar();
  301. X        putchar(c & 0xf);
  302. X        continue;
  303. X        }
  304. X        putchar(';');
  305. X        putchar(c);
  306. X        continue;
  307. X    }
  308. X    if (c >= 1 && c <= 7) {
  309. X        putchar(';');
  310. X        putchar(':');
  311. X        putchar(c | 0x40);
  312. X        continue;
  313. X    }
  314. X    putchar(c);
  315. X    }
  316. X    
  317. X    exit(0);
  318. X}
  319. X
  320. X
  321. X        
  322. NO_NEWS_IS_GOOD_NEWS
  323. chmod 0644 cvt-help.c || echo "restore of cvt-help.c fails"
  324. set `wc -c cvt-help.c`;Sum=$1
  325. if test "$Sum" != "407"
  326. then echo original size 407, current size $Sum;fi
  327. echo "x - extracting data.h (Text)"
  328. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > data.h &&
  329. X/*
  330. X *    Internal representation of the master, group, and article
  331. X *    information read from the database.
  332. X *
  333. X *    For each article read from the database, an article_header
  334. X *    structure is initialized.
  335. X */
  336. X
  337. X/*
  338. X *    global master data
  339. X */
  340. X
  341. Xtypedef struct {
  342. X    time_t        last_scan; /* age of active file at last scan */
  343. X    group_number    number_of_groups;
  344. X    off_t        next_group_write_offset; /* in .groups */
  345. X    int            free_groups; /* allocated during first visit */
  346. X} master_header;
  347. X
  348. X/*
  349. X *    group information
  350. X */
  351. X
  352. Xtypedef struct group_header {
  353. X
  354. X    /* this part of the header is read from */
  355. X    /* the .master file                */
  356. X
  357. X    article_number    first_l_article;
  358. X    article_number    last_l_article;
  359. X
  360. X    off_t        index_write_offset;
  361. X    off_t        data_write_offset;
  362. X
  363. X    int            group_name_length;
  364. X
  365. X    int32        group_flag;
  366. X
  367. X#    define MF(n)    (1<<(n-1))
  368. X#    define CF(n)    (1<<(n+15))
  369. X
  370. X#    define G_MASTER_FLAGS    (MF(17)-1) /* flags that are saved on file */
  371. X
  372. X    /* master flags */
  373. X
  374. X
  375. X#    define G_MODERATED    MF(1)    /* group is moderated */
  376. X#    define G_CONTROL    MF(2)    /* group is control group */
  377. X#    define G_NO_DIRECTORY    MF(3)    /* group directory not found */
  378. X#    define G_ALWAYS_DIGEST    MF(4)    /* always decode articles as digests */
  379. X#    define G_NEVER_DIGEST    MF(5)    /* never decode articles as digests */
  380. X#    define G_EXPIRE        MF(6)    /* expire in progress or pending */
  381. X#    define G_BLOCKED    MF(7)    /* don't trust this entry */
  382. X
  383. X    /* client flags */
  384. X
  385. X#    define G_SUBSCRIPTION    CF(1)    /* from .rc */
  386. X#    define G_READ        CF(2)    /* group has been read    */
  387. X#    define G_RC_UPDATED    CF(3)    /* .rc is updated */
  388. X#    define G_DONE        CF(4)    /* finished with this group */
  389. X#    define G_NEW        CF(5)    /* new group */
  390. X#    define G_FOLDER        CF(6)    /* "group" is a folder file */
  391. X#    define G_DIRECTORY    CF(7)    /* "group" is directory */
  392. X#    define G_SELECTION    CF(8)    /* a selection exist (use it) */
  393. X#    define G_UNREAD_COUNT    CF(9)    /* is included in unread_articles */
  394. X#    define G_MAILBOX    CF(10)    /* user's mail box file */
  395. X
  396. X    /* this part is initialized during reading of the .groups file */
  397. X
  398. X    /* DO NOT CHANGE THE POSITION OF group_num AS THE FIRST FIELD */
  399. X    /* AFTER THE PART WHICH IS SAVED IN THE MASTER FILE */
  400. X
  401. X    group_number    group_num;
  402. X
  403. X    char *        group_name;
  404. X    
  405. X    /* this part is used by the master to hold active file data */
  406. X    /* and the reader to hold information from the .rc file */
  407. X
  408. X    article_number    first_article;
  409. X    article_number    last_article;
  410. X
  411. X    struct group_header *next_group;    /* group sequence */
  412. X    struct group_header    *prev_group;
  413. X
  414. X    char        *kill_list;
  415. X    char        *save_file;     /* default save file from init */
  416. X
  417. X    off_t        rc_offset;    /* offset in rc_file */
  418. X} group_header;
  419. X
  420. X
  421. X/* size of the part of the group header placed on backing storage */
  422. X
  423. X
  424. X#define SAVED_GROUP_HEADER_SIZE(group) \
  425. X    (((char *)(&((group).group_num))) - ((char *)(&(group))))
  426. X
  427. X/*
  428. X *    Internal article header information.
  429. X */
  430. X
  431. Xtypedef struct {
  432. X    union {
  433. X    article_number au_number;    /* article number in the group    */
  434. X    char *au_string;
  435. X    } au_union;
  436. X                /* indexes to header line text    */
  437. X    off_t    hpos;        /* first byte of header        */
  438. X    off_t    fpos;        /* first byte in article text    */
  439. X    off_t    lpos;        /* last pos of article        */
  440. X
  441. X    time_stamp     t_stamp;    /* encoded time_stamp        */
  442. X    
  443. X    char *    sender;        /*   sender's name        */
  444. X    char *    subject;    /*   subject (w/o Re:)        */
  445. X
  446. X    int16    replies;    /*   no of Re:            */
  447. X    int16    lines;        /*   no of lines        */
  448. X
  449. X    group_header *a_group;    /* if merged article menu    */
  450. X    
  451. X    int    flag;            /* flags:             */
  452. X
  453. X#    define AF(n) (1<<(n-1))
  454. X
  455. X#    define A_SELECT       AF(1)    /* article has been selected    */
  456. X#    define A_SAME            AF(2)    /* same subject as prev. article */
  457. X#    define A_DIGEST       AF(3)    /* digest sub article        */
  458. X#    define A_FULL_DIGEST    AF(4)    /* full digest            */
  459. X#    define A_FAKED        AF(5)    /* only 'number' is valid    */
  460. X#    define A_FOLDER        AF(6)    /* article file = "folder_path"    */
  461. X#    define A_CANCEL        AF(7)    /* folder entry cancelled    */
  462. X#    define A_SEEN        AF(8)    /* article presented on menu    */
  463. X
  464. X} article_header;
  465. X
  466. X
  467. X#define    a_number    au_union.au_number
  468. X#define a_string    au_union.au_string
  469. X
  470. NO_NEWS_IS_GOOD_NEWS
  471. chmod 0644 data.h || echo "restore of data.h fails"
  472. set `wc -c data.h`;Sum=$1
  473. if test "$Sum" != "4039"
  474. then echo original size 4039, current size $Sum;fi
  475. echo "x - extracting date_regexp.c (Text)"
  476. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > date_regexp.c &&
  477. X/*
  478. X * produce input for nngoback
  479. X *
  480. X *    generates a regular expression for egrep that will
  481. X *    match the last N days, execute egrep with this pattern
  482. X *    and output a sequence of "group-name article" pairs
  483. X */
  484. X
  485. X#include "config.h"
  486. X#include <time.h>
  487. X
  488. X#define DAYS    * 24 * 60 * 60
  489. X
  490. Xmain(argc, argv)
  491. Xint argc;
  492. Xchar **argv;
  493. X{
  494. X    time_t now, then;
  495. X    struct tm *tm, *localtime();
  496. X    int then_year, then_month, then_day;
  497. X    int first;
  498. X    
  499. X    if (argc != 2) {
  500. X    fprintf(stderr, "usage: nngoback1 <days>\n");
  501. X    exit(1);
  502. X    }
  503. X
  504. X    time(&now);    
  505. X
  506. X    then = now - (atoi(argv[1]) DAYS);
  507. X    tm = localtime(&then);
  508. X    then_year = tm->tm_year;
  509. X    then_month = tm->tm_mon;
  510. X    then_day = tm->tm_mday;
  511. X    
  512. X    tm = localtime(&now);
  513. X
  514. X    printf("\t(");
  515. X    
  516. X    first = 0;
  517. X    while (tm->tm_year > then_year) {
  518. X    printf("%s%02d", first == 0 ? "../../(" : "|", tm->tm_year);
  519. X    first = 1;
  520. X
  521. X    tm->tm_year--;
  522. X    tm->tm_mon = 11;
  523. X    tm->tm_mday = 31;
  524. X    }
  525. X    if (first == 1) putchar(')');
  526. X    
  527. X    while (tm->tm_mon > then_month) {
  528. X    printf(first == 0 ? "(" : first == 1 ? "|(" : "|");
  529. X    first = 2;
  530. X    printf("%02d", tm->tm_mon + 1);
  531. X    tm->tm_mon --;
  532. X    tm->tm_mday = 31;
  533. X    }
  534. X    if (first == 2) printf(")/../%02d", then_year);
  535. X    
  536. X    while (tm->tm_mday >= then_day) {
  537. X    if (first != 0)
  538. X        printf("|");
  539. X    if (first != 3) 
  540. X        printf("%02d/(", then_month + 1);
  541. X    first = 3;
  542. X    printf("%02d", tm->tm_mday);
  543. X    tm->tm_mday--;
  544. X    }
  545. X    if (first == 3) printf(")/%02d", then_year);
  546. X    
  547. X    printf(")\n");
  548. X    
  549. X    exit(0);
  550. X}
  551. NO_NEWS_IS_GOOD_NEWS
  552. chmod 0644 date_regexp.c || echo "restore of date_regexp.c fails"
  553. set `wc -c date_regexp.c`;Sum=$1
  554. if test "$Sum" != "1495"
  555. then echo original size 1495, current size $Sum;fi
  556. echo "x - extracting db.c (Text)"
  557. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > db.c &&
  558. X/*
  559. X * database access and update
  560. X */
  561. X
  562. X#include "config.h"
  563. X#include "db.h"
  564. X
  565. Xexport master_header master;
  566. Xexport group_header *active_groups, **sorted_groups;
  567. X
  568. X/*
  569. X * Init access to a group
  570. X */
  571. X
  572. Xexport group_header *current_group = NULL;
  573. X
  574. Xexport char    group_path_name[FILENAME];
  575. Xexport char    *group_file_name = NULL;
  576. X
  577. Xstatic    char    *group_position = NULL;
  578. X
  579. Xinit_group(gh)
  580. Xregister group_header *gh;
  581. X{
  582. X    register    char    *p, *q;
  583. X
  584. X    if (gh == NULL) return 0;
  585. X    if (gh == current_group) return 1;
  586. X    
  587. X    current_group = gh;
  588. X
  589. X    if (gh->group_flag & G_NO_DIRECTORY) return 0;
  590. X
  591. X    if (gh->group_flag & G_FOLDER) {
  592. X    group_position = NULL;
  593. X    group_file_name = NULL;
  594. X    strcpy(group_path_name, gh->group_name);
  595. X    return 1;
  596. X    }
  597. X
  598. X#ifdef NNTP
  599. X    if (use_nntp && nntp_set_group(gh) < 0)
  600. X    return 0;
  601. X#endif /* NNTP */
  602. X
  603. X    if (group_position == NULL)
  604. X    if (is_master) 
  605. X        group_position = group_path_name;
  606. X    else {
  607. X        strcpy(group_path_name, news_directory);
  608. X        group_position = group_path_name + strlen(group_path_name);
  609. X        *group_position++ = '/';
  610. X    }
  611. X    
  612. X    for (p = group_position, q = gh->group_name; *q; q++)
  613. X    *p++ = (*q == '.') ? '/' : *q;
  614. X
  615. X    if (is_master) {
  616. X
  617. X    /*
  618. X     *    The master will chdir to the group's directory to
  619. X     *    get better performance (can use relative path names).
  620. X     *
  621. X     *    We cannot do the same for the user client, because of
  622. X     *    the 'save' commands.
  623. X     */
  624. X
  625. X    *p++ = NUL;
  626. X    
  627. X#ifdef NNTP
  628. X    if (!use_nntp) {
  629. X#endif
  630. X        if (chdir(news_directory) < 0)
  631. X        sys_error(news_directory);
  632. X        
  633. X        if (chdir(group_path_name) < 0)
  634. X        return 0;
  635. X#ifdef NNTP
  636. X    }
  637. X#endif /* NNTP */
  638. X
  639. X    group_file_name = group_path_name;
  640. X
  641. X    } else {
  642. X
  643. X    /* client */
  644. X
  645. X    *p++ = '/';
  646. X    group_file_name = p;
  647. X    }
  648. X
  649. X    return 1;
  650. X}
  651. X
  652. X
  653. XFILE *open_groups(mode)
  654. X{
  655. X    return open_file(relative(db_directory, "GROUPS"), mode);
  656. X}
  657. X
  658. X    
  659. X
  660. X/*
  661. X *    Open master file; read it in if first open.
  662. X */
  663. X
  664. XFILE *master_file = NULL;
  665. X
  666. Xopen_master(mode)
  667. X{
  668. X    FILE             *g;
  669. X    int             entries, n, cur_group;
  670. X    char             *strings;
  671. X    register group_header     *gh;
  672. X    static int             first_open = 1;
  673. X    
  674. X    
  675. X    master_file = open_file(relative(db_directory, "MASTER"), mode|MUST_EXIST);
  676. X
  677. X    if (mode == OPEN_CREATE || !first_open) return;
  678. X    
  679. X    first_open = 0;
  680. X    
  681. X    if (!db_read_master(master_file, &master))
  682. X    sys_error("Incomplete MASTER");
  683. X    
  684. X    master.free_groups = master.number_of_groups / 10;
  685. X    
  686. X    entries = master.free_groups + master.number_of_groups;
  687. X    
  688. X    active_groups = (group_header *) calloc(entries, sizeof(group_header));
  689. X    mem_check(active_groups, entries, "group headers");
  690. X    
  691. X    sorted_groups = (group_header **) 
  692. X    calloc(entries, sizeof(group_header *));
  693. X    mem_check(sorted_groups, entries, "sorted group header pointers");
  694. X
  695. X    strings = malloc((int)master.next_group_write_offset);
  696. X    mem_check(strings, (int)master.next_group_write_offset, 
  697. X          "bytes for group names");
  698. X
  699. X    g = open_groups(OPEN_READ|MUST_EXIST);
  700. X
  701. X    n = fread(strings, sizeof(char), (int)master.next_group_write_offset, g);
  702. X    if (n != (int)master.next_group_write_offset)
  703. X    sys_error("Incomplete GROUPS file");
  704. X    fclose(g);
  705. X    
  706. X    for (cur_group = 0, gh = active_groups;
  707. X     cur_group < master.number_of_groups;
  708. X     cur_group++, gh++) {
  709. X
  710. X    sorted_groups[cur_group] = gh;
  711. X    
  712. X    if (!db_read_group(master_file, gh, -1))
  713. X        sys_error("Incomplete MASTER file");
  714. X    
  715. X    gh->group_num = cur_group;
  716. X    gh->group_name = strings;
  717. X    strings += gh->group_name_length;
  718. X    *strings++ = NUL;
  719. X    }    
  720. X
  721. X    sort_groups();
  722. X}
  723. X
  724. X
  725. Xclose_master()
  726. X{
  727. X    if (master_file != NULL) {
  728. X    fclose(master_file);
  729. X    master_file = NULL;
  730. X    }
  731. X}
  732. X
  733. X
  734. Xupdate_group(gh)
  735. Xgroup_header *gh;
  736. X{
  737. X    int flag;
  738. X
  739. X    flag = gh->group_flag & ~G_MASTER_FLAGS;
  740. X
  741. X    if (!db_read_group(master_file, gh, gh->group_num)) return -1;
  742. X    
  743. X    gh->group_flag |= flag;
  744. X
  745. X    if (gh->group_flag & G_BLOCKED) return -1;
  746. X    
  747. X    return 1;
  748. X}
  749. X
  750. X    
  751. Xstatic group_name_cmp(g1, g2)
  752. Xgroup_header **g1, **g2;
  753. X{
  754. X    return strcmp((*g1)->group_name, (*g2)->group_name);
  755. X}
  756. X
  757. X
  758. Xsort_groups()
  759. X{
  760. X    qsort(sorted_groups, master.number_of_groups,
  761. X      sizeof(group_header *), group_name_cmp);
  762. X}
  763. X
  764. X
  765. Xgroup_header *lookup(name)
  766. Xchar *name;
  767. X{
  768. X    register i, j, k, t;
  769. X    
  770. X    i = 0; j = master.number_of_groups - 1; 
  771. X    
  772. X    while (i <= j) {
  773. X    k = (i + j) / 2;
  774. X
  775. X    if ( (t=strcmp(name, sorted_groups[k]->group_name)) > 0) 
  776. X        i = k+1;
  777. X    else
  778. X    if (t < 0)
  779. X        j = k-1;
  780. X    else
  781. X        return sorted_groups[k];
  782. X    }
  783. X    
  784. X    return NULL;
  785. X}
  786. X
  787. X
  788. Xart_collected(gh, art_num)
  789. Xgroup_header *gh;
  790. Xarticle_number art_num;
  791. X{
  792. X    return gh->first_l_article <= art_num && gh->last_l_article >= art_num;
  793. X}
  794. X
  795. X
  796. XFILE *open_data_file(gh, d_or_x, mode)
  797. Xgroup_header *gh;
  798. Xchar d_or_x;
  799. Xint mode;
  800. X{
  801. X    char data_file[FILENAME];
  802. X    
  803. X    sprintf(data_file, "%s/DATA/%d.%c", db_directory, gh->group_num, d_or_x);
  804. X
  805. X    if (mode == -1) {
  806. X    unlink(data_file);
  807. X    return (FILE *)NULL;
  808. X    } else
  809. X    return open_file(data_file, mode);
  810. X}
  811. X
  812. X
  813. X#ifdef NETWORK_DATABASE
  814. X
  815. X#define MASTER_FIELDS    3
  816. X#define    GROUP_FIELDS    6
  817. X#define    ARTICLE_FIELDS    10
  818. X
  819. X
  820. Xtypedef int32 net_long;
  821. X
  822. X
  823. X#ifdef NETWORK_BYTE_ORDER
  824. X
  825. X#define net_to_host(buf, n)
  826. X#define host_to_net(buf, n)
  827. X
  828. X#else
  829. X
  830. Xstatic net_to_host(buf, lgt)
  831. Xregister net_long *buf;
  832. Xint lgt;
  833. X{
  834. X    while (--lgt >= 0) {
  835. X    *buf = ntohl(*buf);
  836. X    buf++;
  837. X    }
  838. X}
  839. X
  840. Xstatic host_to_net(buf, lgt)
  841. Xregister net_long *buf;
  842. Xint lgt;
  843. X{
  844. X    while (--lgt >= 0) {
  845. X    *buf = htonl(*buf);
  846. X    buf++;
  847. X    }
  848. X}
  849. X#endif /* not NETWORK_BYTE_ORDER */
  850. X#endif /* NETWORK_DATABASE */
  851. X
  852. X
  853. Xdb_read_master(f, masterp)
  854. XFILE *f;
  855. Xmaster_header *masterp;  
  856. X{
  857. X#ifdef NETWORK_DATABASE
  858. X    net_long buf[MASTER_FIELDS];
  859. X    
  860. X    if (fread(buf, sizeof(net_long), MASTER_FIELDS, f) != MASTER_FIELDS)
  861. X    return 0;
  862. X
  863. X    net_to_host(buf, MASTER_FIELDS);
  864. X    
  865. X    masterp->last_scan = buf[0];
  866. X    masterp->number_of_groups = buf[1];
  867. X    masterp->next_group_write_offset = buf[2];
  868. X#else
  869. X
  870. X    if (fread(masterp, sizeof(master_header), 1, f) != 1) return 0;
  871. X#endif
  872. X    return 1;
  873. X}
  874. X
  875. X
  876. Xdb_write_master(f, masterp)
  877. XFILE *f;
  878. Xmaster_header *masterp;  
  879. X{
  880. X#ifdef NETWORK_DATABASE
  881. X    net_long buf[MASTER_FIELDS];
  882. X    
  883. X    buf[0] = masterp->last_scan;
  884. X    buf[1] = masterp->number_of_groups;
  885. X    buf[2] = masterp->next_group_write_offset;
  886. X
  887. X    host_to_net(buf, MASTER_FIELDS);
  888. X    if (fwrite(buf, sizeof(net_long), MASTER_FIELDS, f) != MASTER_FIELDS) return 0;
  889. X#else
  890. X
  891. X    if (fwrite(masterp, sizeof(master_header), 1, f) != 1) return 0;
  892. X#endif
  893. X    return 1;
  894. X}
  895. X
  896. Xdb_read_group(f, gh, n)
  897. XFILE *f;
  898. Xregister group_header *gh;
  899. Xgroup_number n;
  900. X{
  901. X#ifdef NETWORK_DATABASE
  902. X    net_long buf[GROUP_FIELDS];
  903. X
  904. X    if (n >= 0) 
  905. X    fseek(f, MASTER_FIELDS * sizeof(net_long) + GROUP_FIELDS * sizeof(net_long) * n, 0);
  906. X
  907. X    if (fread(buf, sizeof(net_long), GROUP_FIELDS, f) != GROUP_FIELDS)
  908. X    return 0;
  909. X
  910. X    net_to_host(buf, GROUP_FIELDS);
  911. X
  912. X    gh->first_l_article = buf[0];
  913. X    gh->last_l_article = buf[1];
  914. X    gh->index_write_offset = buf[2];
  915. X    gh->data_write_offset = buf[3];
  916. X    gh->group_name_length = buf[4];
  917. X    gh->group_flag = buf[5];
  918. X#else
  919. X    
  920. X    if (n >= 0)
  921. X    fseek(f, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * n), 0);
  922. X
  923. X    if (fread(gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, f) != 1)
  924. X    return 0;
  925. X
  926. X#endif
  927. X    return 1;
  928. X}
  929. X
  930. X
  931. Xdb_write_group(f, gh, n)
  932. XFILE *f;
  933. Xregister group_header *gh;
  934. Xgroup_number n;
  935. X{
  936. X#ifdef NETWORK_DATABASE
  937. X    net_long buf[GROUP_FIELDS];
  938. X
  939. X    if (n >= 0) 
  940. X    fseek(f, MASTER_FIELDS * sizeof(net_long) + GROUP_FIELDS * sizeof(net_long) * n, 0);
  941. X
  942. X    buf[0] = gh->first_l_article;
  943. X    buf[1] = gh->last_l_article;
  944. X    buf[2] = gh->index_write_offset;
  945. X    buf[3] = gh->data_write_offset;
  946. X    buf[4] = gh->group_name_length;
  947. X    buf[5] = gh->group_flag;
  948. X
  949. X    host_to_net(buf, GROUP_FIELDS);
  950. X    if (fwrite(buf, sizeof(net_long), GROUP_FIELDS, f) != GROUP_FIELDS)
  951. X    return 0;
  952. X
  953. X#else
  954. X    if (n >= 0)
  955. X    fseek(f, (off_t)(sizeof(master_header) + SAVED_GROUP_HEADER_SIZE(*gh) * n), 0);
  956. X
  957. X    if (fwrite(gh, SAVED_GROUP_HEADER_SIZE(*gh), 1, f) != 1)
  958. X    return 0;
  959. X#endif
  960. X
  961. X    return 1;
  962. X}
  963. X
  964. X
  965. Xdb_read_art(f, dh, offset)
  966. XFILE *f;
  967. Xdata_header *dh;
  968. Xoff_t *offset;
  969. X{
  970. X#ifdef NETWORK_DATABASE
  971. X    net_long buf[ARTICLE_FIELDS];
  972. X    
  973. X    if (fread(buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  974. X    return 0;
  975. X    
  976. X    net_to_host(buf, ARTICLE_FIELDS);
  977. X
  978. X    dh->dh_number = buf[0];
  979. X    dh->dh_date = buf[1];
  980. X    dh->dh_hpos = buf[2];
  981. X    dh->dh_lpos = buf[3];
  982. X    dh->dh_fpos = buf[4];
  983. X    dh->dh_lines = buf[5];
  984. X    dh->dh_replies = buf[6];
  985. X    dh->dh_cross_postings = buf[7];
  986. X    dh->dh_subject_length = buf[8];
  987. X    dh->dh_sender_length = buf[9];
  988. X
  989. X    if (offset) *offset += ARTICLE_FIELDS * sizeof(net_long);
  990. X#else
  991. X    
  992. X    if (fread(dh, sizeof(data_header), 1, f) != 1) return 0;
  993. X    if (offset) *offset += sizeof(data_header);
  994. X#endif
  995. X    return 1;
  996. X}
  997. X
  998. Xdb_write_art(f, dh)
  999. XFILE *f;
  1000. Xdata_header *dh;
  1001. X{
  1002. X#ifdef NETWORK_DATABASE
  1003. X    net_long buf[ARTICLE_FIELDS];
  1004. X
  1005. X    buf[0] = dh->dh_number;
  1006. X    buf[1] = dh->dh_date;
  1007. X    buf[2] = dh->dh_hpos;
  1008. X    buf[3] = dh->dh_lpos;
  1009. X    buf[4] = dh->dh_fpos;
  1010. X    buf[5] = dh->dh_lines;
  1011. X    buf[6] = dh->dh_replies;
  1012. X    buf[7] = dh->dh_cross_postings;
  1013. X    buf[8] = dh->dh_subject_length;
  1014. X    buf[9] = dh->dh_sender_length;
  1015. X
  1016. X    host_to_net(buf, ARTICLE_FIELDS);
  1017. X
  1018. X    if (fwrite(buf, sizeof(net_long), ARTICLE_FIELDS, f) != ARTICLE_FIELDS)
  1019. X    return 0;
  1020. X#else
  1021. X    
  1022. X    if (fwrite(dh, sizeof(data_header), 1, f) != 1) return 0;
  1023. X
  1024. X#endif
  1025. X
  1026. X    return 1;
  1027. X}
  1028. X
  1029. X
  1030. X
  1031. Xoff_t get_index_offset(gh, art_num)
  1032. Xgroup_header *gh;
  1033. Xarticle_number art_num;
  1034. X{
  1035. X#ifdef NETWORK_DATABASE
  1036. X    return (off_t)((art_num - gh->first_l_article) * sizeof(net_long));
  1037. X#else
  1038. X    return (off_t)((art_num - gh->first_l_article) * sizeof(off_t));
  1039. X#endif
  1040. X}
  1041. X
  1042. Xoff_t get_data_offset(gh, art_num)
  1043. Xgroup_header *gh;
  1044. Xarticle_number art_num;
  1045. X{
  1046. X    FILE *index;
  1047. X    off_t data_offset;
  1048. X    
  1049. X    if (gh->first_l_article == art_num) return (off_t)0;
  1050. X    
  1051. X    index = open_data_file(gh, 'x', OPEN_READ);
  1052. X    if (index == NULL) return (off_t)(-1);
  1053. X    
  1054. X    fseek(index, get_index_offset(gh, art_num), 0);
  1055. X    if (!db_read_offset(index, &data_offset))
  1056. X    return (off_t)(-1);
  1057. X
  1058. X    fclose(index);
  1059. X    
  1060. X    return data_offset;
  1061. X}
  1062. X
  1063. X
  1064. Xdb_read_offset(f, offset)
  1065. XFILE *f;
  1066. Xoff_t *offset;
  1067. X{
  1068. X#ifdef NETWORK_DATABASE
  1069. X    net_long temp;
  1070. X
  1071. X    if (fread(&temp, sizeof(net_long), 1, f) != 1) return 0;
  1072. X
  1073. X#ifndef NETWORK_BYTE_ORDER
  1074. X    temp = ntohl(temp);
  1075. X#endif
  1076. X    *offset = temp;
  1077. X#else    
  1078. X
  1079. X    if (fread((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  1080. X#endif
  1081. X    return 1;
  1082. X}
  1083. X
  1084. Xdb_write_offset(f, offset)
  1085. XFILE *f;
  1086. Xoff_t *offset;
  1087. X{
  1088. X#ifdef NETWORK_DATABASE
  1089. X    net_long temp;
  1090. X
  1091. X    temp = *offset;
  1092. X
  1093. X#ifndef NETWORK_BYTE_ORDER
  1094. X    temp = htonl(temp);
  1095. X#endif
  1096. X    if (fwrite(&temp, sizeof(net_long), 1, f) != 1) return 0;
  1097. X
  1098. X#else    
  1099. X
  1100. X    if (fwrite((char *)offset, sizeof(off_t), 1, f) != 1) return 0;
  1101. X#endif
  1102. X    return 1;
  1103. X}
  1104. X
  1105. NO_NEWS_IS_GOOD_NEWS
  1106. chmod 0644 db.c || echo "restore of db.c fails"
  1107. set `wc -c db.c`;Sum=$1
  1108. if test "$Sum" != "10668"
  1109. then echo original size 10668, current size $Sum;fi
  1110. echo "x - extracting db.h (Text)"
  1111. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > db.h &&
  1112. X/*
  1113. X
  1114. X * DATABASE ORGANIZATION:
  1115. X *
  1116. X *    The central nn information is contained in following files:
  1117. X *        DB_DIRECTORY/MASTER
  1118. X *        DB_DIRECTORY/GROUPS
  1119. X *        DB_DIRECTORY/DATA/nnn.x
  1120. X *        DB_DIRECTORY/DATA/nnn.d
  1121. X *
  1122. X *     The MASTER file consists of a header and one entry for each news
  1123. X *    group.  The sequence of the group headers defines the group
  1124. X *    number associated with the group.
  1125. X *
  1126. X *     The GROUPS file contains the names of the news groups; the names
  1127. X *    occur in the same sequence as in the MASTER file.
  1128. X *
  1129. X *    For each news group, the DATA directory contains two files whose
  1130. X *    name is constructed from the group number 'nnn':
  1131. X *
  1132. X *        nnn.x    Index file
  1133. X *        nnn.d    Data file
  1134. X *
  1135. X *    The index file provides a a mapping from article numbers to offsets
  1136. X *    in the data file.
  1137. X *
  1138. X *    The data file contains the actual header data.  Each article is
  1139. X *    represented by a Header, an array of Cross Postings, and the
  1140. X *    strings representing the sender name and the article subject:
  1141. X *
  1142. X *        header
  1143. X *        group_number 1 [ if cross posted ]
  1144. X *        group_number 2
  1145. X *        ...
  1146. X *        sender name (null terminated) [if sender_length > 0]
  1147. X *        subject (null terminated) [if subject_length > 0]    
  1148. X *
  1149. X *    For a digest, cross posted groups are only specified for first 
  1150. X *    the entry.
  1151. X *
  1152. X *    The format of the MASTER file is specifed in the data.h
  1153. X *    file.  The format of the index and data files are specified below. 
  1154. X *
  1155. X *    Unless NETWORK_DATABASE is defined, the database will
  1156. X *    will contain machine dependent binary data.
  1157. X */
  1158. X
  1159. Xtypedef struct {
  1160. X    off_t        data_offset;
  1161. X} index_entry;
  1162. X
  1163. Xtypedef struct {
  1164. X    article_number    dh_number;
  1165. X
  1166. X    time_stamp    dh_date; /* encoded Date: filed (not a time_t value!!) */
  1167. X        
  1168. X    off_t    dh_hpos; /* absolute offset for first byte of header */
  1169. X    off_t    dh_lpos; /* absolute offset for last byte of article */
  1170. X    int16    dh_fpos; /* relative offset for first byte in article text */
  1171. X    
  1172. X    int16    dh_lines;
  1173. X    int8    dh_replies;
  1174. X    
  1175. X    int8    dh_cross_postings;
  1176. X    int8    dh_subject_length;
  1177. X    int8    dh_sender_length;
  1178. X} data_header;
  1179. X
  1180. X/*
  1181. X *     The article_number is negative for digest article header and 
  1182. X *     zero for following sub articles.
  1183. X */
  1184. X
  1185. Xarticle_number    current_digest_article;
  1186. X
  1187. X#define    IS_DIGEST_HEADER(e1) \
  1188. X    ((e1).dh_number < 0 && (current_digest_article = -((e1).dh_number)))
  1189. X#define    IS_SUB_DIGEST(e1) \
  1190. X    (((e1).dh_number) == 0)
  1191. X#define    ARTICLE_NUMBER(e1) \
  1192. X    (((e1).dh_number <= 0) ? current_digest_article : ((e1).dh_number))
  1193. X
  1194. X
  1195. X
  1196. X#ifdef NETWORK_DATABASE
  1197. Xtypedef int32 cross_post_number;
  1198. X#else
  1199. Xtypedef group_number cross_post_number;
  1200. X#endif
  1201. X
  1202. X
  1203. X/* open database files */
  1204. X
  1205. XFILE *open_groups(), *open_data_file();
  1206. X
  1207. X/* data access */
  1208. X
  1209. Xoff_t get_index_offset(), get_data_offset();
  1210. X
  1211. X
  1212. X
  1213. NO_NEWS_IS_GOOD_NEWS
  1214. chmod 0644 db.h || echo "restore of db.h fails"
  1215. set `wc -c db.h`;Sum=$1
  1216. if test "$Sum" != "2670"
  1217. then echo original size 2670, current size $Sum;fi
  1218. echo "x - extracting debug.h (Text)"
  1219. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > debug.h &&
  1220. X/*
  1221. X *    Debug flags and defines
  1222. X *
  1223. X *    Notice:  no modules are conditioned by this file in the
  1224. X *         makefile.  touch the source file to have a change
  1225. X *         in debugging setup to reflect the program behaviour
  1226. X */
  1227. X
  1228. X
  1229. X/* debugging */
  1230. X
  1231. X#define RC_TEST        1    /* rc file updates */
  1232. X#define DG_TEST        2     /* digest decoding */
  1233. X#define SEQ_TEST    4    /* sequence file decoding */
  1234. X#define SEQ_DUMP    8    /* dump sequence after read */
  1235. X
  1236. Xextern int Debug;
  1237. NO_NEWS_IS_GOOD_NEWS
  1238. chmod 0644 debug.h || echo "restore of debug.h fails"
  1239. set `wc -c debug.h`;Sum=$1
  1240. if test "$Sum" != "427"
  1241. then echo original size 427, current size $Sum;fi
  1242. echo "x - extracting digest.c (Text)"
  1243. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > digest.c &&
  1244. X/*
  1245. X * digest article handling
  1246. X */
  1247. X
  1248. X#include "config.h"
  1249. X#include "news.h"
  1250. X#include "match.h"
  1251. X#include "debug.h"
  1252. X
  1253. X#ifdef DG_TEST
  1254. X
  1255. X#define TEST(fmt, x, y) if (Debug & DG_TEST) printf(fmt, x, y)
  1256. X
  1257. X#else
  1258. X
  1259. X#define TEST(fmt, x, y)
  1260. X
  1261. X#endif
  1262. X
  1263. X
  1264. X
  1265. X/*
  1266. X * test if global 'news' header is header of a digest.
  1267. X * body points to a buffer (NUL term)
  1268. X * containing the first part of the article.
  1269. X */
  1270. X
  1271. Xstatic char match_digest[128] = {
  1272. X    
  1273. X/*  NUL SOH STX ETX EOT ENQ ACK BEL BS  TAB NL  VT  FF  CR  SO  SI  */
  1274. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 
  1275. X
  1276. X/*  DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM  SUB ESC FS  GS  RS  US  */
  1277. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 
  1278. X
  1279. X/*  SP  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /   */
  1280. X    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 
  1281. X
  1282. X/*  0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?   */
  1283. X     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 00, 00, 00, 00, 00, 00, 
  1284. X
  1285. X/*  @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O   */
  1286. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  1287. X
  1288. X/*  P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _   */
  1289. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00, 
  1290. X
  1291. X/*  `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   */
  1292. X    00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
  1293. X
  1294. X/*  p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   DEL */
  1295. X    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
  1296. X
  1297. X};
  1298. X
  1299. Xstatic char digest_pattern[] = "digest";
  1300. X
  1301. Xinit_digest_parsing()
  1302. X{
  1303. X    init_quick_match(digest_pattern);
  1304. X}
  1305. X
  1306. X
  1307. Xis_digest(body)
  1308. Xregister char *body;
  1309. X{
  1310. X    char *dpos, *quick_match();
  1311. X    register char *sp;
  1312. X    register int l;
  1313. X
  1314. X    /* articles without a subject line are not digests (per definition) */
  1315. X    if (news.ng_subj == NULL) return 0;
  1316. X    
  1317. X
  1318. X    if (dpos = quick_match(news.ng_subj, digest_pattern)) {
  1319. X    int lgt = dpos - news.ng_subj;
  1320. X    int maxl = 10;
  1321. X    
  1322. X    /* look for a line matching the subject */
  1323. X    while (*body && maxl) {
  1324. X        sp = news.ng_subj;
  1325. X        l = lgt;
  1326. X        if (*body == *sp && strncmp(body, sp, l) == 0) 
  1327. X        goto ok;
  1328. X        while (*body && *body != NL) {
  1329. X        while (*sp && MATCH_DROP(match_digest, *sp)) {
  1330. X            if (--l == 0) goto ok;
  1331. X            ++sp;
  1332. X        }
  1333. X        
  1334. X        if (MATCH_DROP(match_digest, *body)) {
  1335. X            ++body;
  1336. X            continue;
  1337. X        }
  1338. X        
  1339. X        if (*sp && MATCH_EQ(match_digest, *body, *sp)) {
  1340. X            if (--l == 0) goto ok;
  1341. X            ++sp;
  1342. X        }
  1343. X        ++body;
  1344. X        }
  1345. X        
  1346. X        if (*body) ++body, --maxl;
  1347. X    }
  1348. X    }
  1349. X    return 0;
  1350. X
  1351. X ok:
  1352. X    TEST("is_digest: %s\n", news.ng_subj, 0);
  1353. X    return 1;
  1354. X}
  1355. X
  1356. X
  1357. X/*
  1358. X * expect that f is positioned at header of an article
  1359. X */
  1360. X
  1361. Xget_digest_article(f, hdrbuf)
  1362. XFILE *f;
  1363. Xnews_header_buffer hdrbuf;
  1364. X{
  1365. X    int cont;
  1366. X    
  1367. X    digest.dg_hpos = ftell(f);
  1368. X    TEST("GET DIGEST hp=%ld\n", digest.dg_hpos, 0);
  1369. X    
  1370. X    do {
  1371. X    if (!parse_digest_header(f, 0, hdrbuf)) return -1;
  1372. X    digest.dg_fpos = ftell(f);
  1373. X    TEST("END HEADER hp=%ld fp=%ld\n", digest.dg_hpos, digest.dg_fpos);
  1374. X    } while ((cont = skip_digest_body(f)) < 0);
  1375. X
  1376. X    TEST("END BODY lp=%ld next=%ld\n", digest.dg_lpos, ftell(f));
  1377. X
  1378. X    return cont;
  1379. X}    
  1380. X    
  1381. X#define BACKUP_LINES     50    /* remember class + offset for parsed lines */
  1382. X
  1383. X#define    LN_BLANK    0x01    /* blank line */
  1384. X#define    LN_DASHED    0x02    /* dash line */
  1385. X#define    LN_HEADER    0x04    /* (possible) header line */
  1386. X#define    LN_ASTERISK    0x08    /* asterisk line (near end) */
  1387. X#define    LN_END_OF    0x10    /* End of ... line */
  1388. X#define    LN_TEXT        0x20    /* unclassified line */
  1389. X
  1390. X
  1391. X/*
  1392. X * skip until 'Subject: ' (or End of digest) line is found
  1393. X * then backup till start of header
  1394. X */
  1395. X
  1396. X/*
  1397. X * Tuning parameters:
  1398. X *
  1399. X *    MIN_HEADER_LINES:    number of known header lines that must
  1400. X *                be found in a block to identify a new
  1401. X *                header
  1402. X *
  1403. X *    MAX_BLANKS_DASH        max no of blanks on a 'dash line'
  1404. X *
  1405. X *    MIN_DASHES        min no of dashes on a 'dash line'
  1406. X *
  1407. X *    MAX_BLANKS_ASTERISKS    max no of blanks on an 'asterisk line'
  1408. X *
  1409. X *    MIN_ASTERISKS        min no of asterisks on an 'asterisk line'
  1410. X *
  1411. X *    MAX_BLANKS_END_OF    max no of blanks before "End of "
  1412. X */
  1413. X
  1414. X#define    MIN_HEADER_LINES    2
  1415. X#define    MAX_BLANKS_DASH        3
  1416. X#define    MIN_DASHES        16
  1417. X#define    MAX_BLANKS_ASTERISK    1
  1418. X#define    MIN_ASTERISKS        10
  1419. X#define    MAX_BLANKS_END_OF    1
  1420. X
  1421. Xskip_digest_body(f)
  1422. Xregister FILE *f;
  1423. X{
  1424. X    off_t  backup_p[BACKUP_LINES];
  1425. X    int       line_type[BACKUP_LINES];
  1426. X    register int backup_index, backup_count;
  1427. X    int    more_header_lines, end_or_asterisks, blanks;
  1428. X    char   line[1024];
  1429. X    register char *cp;
  1430. X    char **dg_hdr_field();
  1431. X
  1432. X#define    decrease_index()    \
  1433. X    if (--backup_index < 0) backup_index = BACKUP_LINES - 1
  1434. X
  1435. X    backup_index = -1;
  1436. X    backup_count = 0;
  1437. X    end_or_asterisks = 0;
  1438. X    
  1439. X    digest.dg_lines = 0;
  1440. X    
  1441. X    
  1442. X next_line:
  1443. X    more_header_lines = 0;
  1444. X
  1445. X next_possible_header_line:
  1446. X    digest.dg_lines++;
  1447. X
  1448. X    if (++backup_index == BACKUP_LINES) backup_index = 0;
  1449. X    if (backup_count < BACKUP_LINES) backup_count++;
  1450. X    
  1451. X    backup_p[backup_index] = ftell(f);
  1452. X    line_type[backup_index] = LN_TEXT;
  1453. X
  1454. X    if (fgets(line, 1024, f) == NULL) {
  1455. X    TEST("end_of_file, bc=%d, lines=%d\n", backup_count, digest.dg_lines);
  1456. X    
  1457. X    /* end of file => look for "****" or "End of" line */
  1458. X
  1459. X    if (end_or_asterisks)
  1460. X        while (--backup_count >= 0) {
  1461. X        --digest.dg_lines;
  1462. X        decrease_index();
  1463. X        if (line_type[backup_index] & (LN_ASTERISK | LN_END_OF)) break;
  1464. X        }
  1465. X    
  1466. X    if (digest.dg_lines == 0) return 0;
  1467. X    
  1468. X    while (--backup_count >= 0) {
  1469. X        --digest.dg_lines;
  1470. X        digest.dg_lpos = backup_p[backup_index];
  1471. X        decrease_index();
  1472. X        if ((line_type[backup_index] & 
  1473. X        (LN_ASTERISK | LN_END_OF | LN_BLANK | LN_DASHED)) == 0)
  1474. X        break;
  1475. X    }
  1476. X
  1477. X    return 0;    /* no article follows */
  1478. X    }
  1479. X
  1480. X    TEST("\n>>%-.50s ==>>", line, 0);
  1481. X    
  1482. X    for (cp = line; *cp && isascii(*cp) && isspace(*cp); cp++);
  1483. X
  1484. X    if (*cp == NUL) {
  1485. X    TEST("BLANK", 0, 0);
  1486. X    line_type[backup_index] = LN_BLANK;
  1487. X    goto next_line;
  1488. X    }
  1489. X    
  1490. X    blanks = cp - line;
  1491. X    
  1492. X    if (*cp == '-') {
  1493. X    if (blanks > MAX_BLANKS_DASH) goto next_line;
  1494. X    
  1495. X    while (*cp == '-') cp++;
  1496. X    if (cp - line - blanks > MIN_DASHES) {
  1497. X        while (*cp && (*cp == '-' || (isascii(*cp) && isspace(*cp)))) cp++;
  1498. X        if (*cp == NUL) {
  1499. X        TEST("DASHED", 0, 0);
  1500. X        
  1501. X        line_type[backup_index] = LN_DASHED;
  1502. X        }
  1503. X        
  1504. X    }
  1505. X    goto next_line;
  1506. X    }
  1507. X        
  1508. X    if (*cp == '*') {
  1509. X    if (blanks > MAX_BLANKS_ASTERISK) goto next_line;
  1510. X    
  1511. X    while (*cp == '*') cp++;
  1512. X    if (cp - line - blanks > MIN_ASTERISKS) {
  1513. X        while (*cp && (*cp == '*' || (isascii(*cp) && isspace(*cp)))) cp++;
  1514. X        if (*cp == NUL) {
  1515. X        TEST("ASTERISK", 0, 0);
  1516. X        line_type[backup_index] = LN_ASTERISK;
  1517. X        end_or_asterisks++;
  1518. X        }
  1519. X    }
  1520. X    goto next_line;
  1521. X    }
  1522. X
  1523. X    if (blanks <= MAX_BLANKS_END_OF && 
  1524. X    *cp == 'E' && strncmp(cp, "End of ", 7) == 0) {
  1525. X    TEST("END_OF_", 0, 0);
  1526. X    line_type[backup_index] = LN_END_OF;
  1527. X    end_or_asterisks++;
  1528. X    goto next_line;
  1529. X    }
  1530. X    
  1531. X    if (blanks == 0) {
  1532. X    if (dg_hdr_field(line, 0)) {
  1533. X        TEST("HEADER", 0, 0);
  1534. X        
  1535. X        line_type[backup_index] = LN_HEADER;
  1536. X        if (++more_header_lines < MIN_HEADER_LINES) 
  1537. X        goto next_possible_header_line;
  1538. X
  1539. X        /* found block with MIN_HEADER_LINES */
  1540. X
  1541. X        /* search for beginning of header */
  1542. X
  1543. X        TEST("\nSearch for start of header\n", 0, 0);
  1544. X
  1545. X        for (;;) {
  1546. X        fseek(f, backup_p[backup_index], 0);
  1547. X        --digest.dg_lines;
  1548. X        if (--backup_count == 0) break;
  1549. X        decrease_index();
  1550. X        if ((line_type[backup_index] & (LN_HEADER | LN_TEXT)) == 0)
  1551. X            break;
  1552. X        }
  1553. X
  1554. X        if (digest.dg_lines == 0) {
  1555. X        TEST("Skipped empty article\n", 0, 0);
  1556. X        return 0;
  1557. X        }
  1558. X        
  1559. X        for (;;) {
  1560. X        digest.dg_lpos = backup_p[backup_index];
  1561. X        if (--backup_count < 0) break;
  1562. X        decrease_index();
  1563. X        if ((line_type[backup_index] & (LN_BLANK | LN_DASHED)) == 0)
  1564. X            break;
  1565. X        --digest.dg_lines;
  1566. X        }
  1567. X
  1568. X        return (digest.dg_lines == 0) ? -1 : 1;
  1569. X    }
  1570. X    goto next_possible_header_line;
  1571. X    }
  1572. X    
  1573. X    goto next_line;
  1574. X}    
  1575. X
  1576. X
  1577. Xparse_digest_header(f, all, hdrbuf)
  1578. XFILE *f;
  1579. Xint all;
  1580. Xnews_header_buffer hdrbuf;
  1581. X{
  1582. X    extern char *parse_header(), **dg_hdr_field();
  1583. X    
  1584. X    digest.dg_date = digest.dg_from = digest.dg_subj = digest.dg_to = NULL;
  1585. X     
  1586. X    parse_header(f, dg_hdr_field, all, hdrbuf);
  1587. X    
  1588. X    return digest.dg_from || digest.dg_subj;
  1589. X}
  1590. X
  1591. X
  1592. Xstatic char **dg_hdr_field(lp, all)
  1593. Xregister char *lp;
  1594. Xint all;
  1595. X{
  1596. X    
  1597. X#define check(name, lgt, field) \
  1598. X    if (strncmp(name, lp, lgt) == 0) { \
  1599. X    TEST("MATCH: field ", 0, 0); \
  1600. X    return &digest.field; \
  1601. X    }
  1602. X    
  1603. X
  1604. X    TEST("\nPARSE[%.20s] ==>> ", lp, 0);
  1605. X    
  1606. X    switch (*lp++) {
  1607. X
  1608. X     case 'D':
  1609. X     case 'd':    
  1610. X    check("ate: ",    5, dg_date);
  1611. X    break;
  1612. X
  1613. X     case 'F':
  1614. X     case 'f':
  1615. X    check("rom: ",    5, dg_from);
  1616. X    break;
  1617. X
  1618. X     case 'R':
  1619. X     case 'r':
  1620. X    if (!all) break;
  1621. X    check("e: ",    3, dg_subj);
  1622. X    break;
  1623. X
  1624. X     case 'S':
  1625. X     case 's':
  1626. X    check("ubject",    6, dg_subj);
  1627. X    break;
  1628. X
  1629. X     case 'T':
  1630. X     case 't':
  1631. X    check("itle: ",    6, dg_subj);
  1632. X    if (!all) break;
  1633. X    check("o: ",    3, dg_to);
  1634. X    break;
  1635. X    }
  1636. X    
  1637. X#undef check
  1638. X    TEST("NOT MATCHED ", 0, 0);
  1639. X    
  1640. X    return NULL;
  1641. X}
  1642. NO_NEWS_IS_GOOD_NEWS
  1643. chmod 0644 digest.c || echo "restore of digest.c fails"
  1644. set `wc -c digest.c`;Sum=$1
  1645. if test "$Sum" != "8891"
  1646. then echo original size 8891, current size $Sum;fi
  1647. echo "x - extracting execute.c (Text)"
  1648. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > execute.c &&
  1649. X#include <signal.h>
  1650. X#include <errno.h>
  1651. X#include "config.h"
  1652. X#include "term.h"
  1653. X
  1654. Xchar *user_shell;
  1655. X
  1656. Xinit_execute()
  1657. X{
  1658. X    if ((user_shell = getenv("SHELL")) == NULL)
  1659. X    user_shell = SHELL;
  1660. X}
  1661. X
  1662. Xexecute(path, args)
  1663. Xchar *path, **args;
  1664. X{
  1665. X    int was_raw, pid, i, status;
  1666. X    sig_type  (*quit)(), (*intr)(), (*cont)();
  1667. X    extern int errno;
  1668. X    
  1669. X    was_raw = no_raw();
  1670. X    
  1671. X    while ((pid = fork()) == -1) sleep(1);
  1672. X    
  1673. X    if (pid == 0) {
  1674. X    for (i = 3 ; i < 20 ; i++)
  1675. X        close(i);
  1676. X    
  1677. X    execv(path, args);
  1678. X    
  1679. X    fprintf(stderr, "%s: not found\n", path);
  1680. X    nn_exit(20);
  1681. X    }
  1682. X    quit = signal(SIGQUIT, SIG_IGN);
  1683. X    intr = signal(SIGINT,  SIG_IGN);
  1684. X#ifdef HAVE_JOBCONTROL
  1685. X    cont = signal(SIGCONT, SIG_DFL);
  1686. X#endif
  1687. X    while ((i = wait(&status)) != pid && (i != -1 || errno == EINTR));
  1688. X    
  1689. X    signal(SIGQUIT, quit);
  1690. X    signal(SIGINT,  intr);
  1691. X#ifdef HAVE_JOBCONTROL
  1692. X    signal(SIGCONT, cont);
  1693. X#endif
  1694. X    
  1695. X    if (was_raw) raw();
  1696. X    
  1697. X    return status != 0;
  1698. X}
  1699. X
  1700. X
  1701. Xshell_escape()
  1702. X{
  1703. X    static char command[FILENAME] = "";
  1704. X    char *cmd;
  1705. X    int first = 1;
  1706. X
  1707. X    prompt("!");
  1708. X    
  1709. Xagain:    
  1710. X    
  1711. X    cmd = get_s(command, NONE, NONE, NO_COMPLETION);
  1712. X    if (cmd == NULL) return !first;
  1713. X    
  1714. X    strcpy(command, cmd);
  1715. X    
  1716. X    if (!run_shell(command, first)) return !first;
  1717. X    first = 0;
  1718. X    
  1719. X    if (any_key(0) == '!') {        /* should use key map here */
  1720. X    putchar(CR);
  1721. X    putchar('!');
  1722. X    clrline();
  1723. X    goto again;
  1724. X    }
  1725. X    
  1726. X    return 1;
  1727. X}
  1728. X
  1729. X
  1730. Xstatic char *exec_sh_args[] = {
  1731. X    "nnsh", 
  1732. X    "-c", 
  1733. X    (char *)NULL, /* cmdstring */
  1734. X    (char *)NULL 
  1735. X};
  1736. X    
  1737. Xrun_shell(command, clear)
  1738. Xchar *command;
  1739. Xint clear;
  1740. X{
  1741. X    char cmdstring[512];
  1742. X    
  1743. X    if (!expand_file_name(cmdstring, command))
  1744. X    return 0;
  1745. X    
  1746. X    if (clear) {
  1747. X    clrdisp();
  1748. X    fl;
  1749. X    } else {
  1750. X    putchar(CR);
  1751. X    putchar(NL);
  1752. X    }
  1753. X
  1754. X    exec_sh_args[2] = cmdstring;
  1755. X    
  1756. X    execute(user_shell, exec_sh_args);
  1757. X    return 1;
  1758. X}
  1759. X    
  1760. X#ifndef HAVE_JOBCONTROL
  1761. Xstatic char *exec_suspend_args[] = {
  1762. X    "nnsh", 
  1763. X    "-i", 
  1764. X    (char *)NULL
  1765. X};
  1766. X#endif
  1767. X
  1768. Xsuspend_nn()
  1769. X{
  1770. X    int was_raw;
  1771. X    
  1772. X    was_raw = no_raw();
  1773. X    gotoxy(0, Lines-1);
  1774. X    clrline();
  1775. X
  1776. X#ifdef HAVE_JOBCONTROL
  1777. X    kill(process_id, SIGTSTP);
  1778. X#else
  1779. X    execute(user_shell, exec_suspend_args);
  1780. X#endif
  1781. X
  1782. X    s_redraw++;
  1783. X    if (was_raw) raw();
  1784. X    
  1785. X    return 1;
  1786. X}
  1787. NO_NEWS_IS_GOOD_NEWS
  1788. chmod 0644 execute.c || echo "restore of execute.c fails"
  1789. set `wc -c execute.c`;Sum=$1
  1790. if test "$Sum" != "2254"
  1791. then echo original size 2254, current size $Sum;fi
  1792. echo "x - extracting expire.c (Text)"
  1793. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > expire.c &&
  1794. X/*
  1795. X *    Expire will remove all entries in the index and data files
  1796. X *    corresponding to the articles before the first article registered
  1797. X *    in the active file.  No attempt is made to eliminate other
  1798. X *    expired articles.
  1799. X */
  1800. X
  1801. X#include "config.h"
  1802. X#include "db.h"
  1803. X
  1804. Ximport int trace;
  1805. X
  1806. X#define expire_error(msg) { \
  1807. X    err_message = msg; \
  1808. X    goto error_handler; \
  1809. X}
  1810. X
  1811. Xexpire_group(gh)
  1812. Xregister group_header *gh;
  1813. X{
  1814. X    FILE *old_x, *old_d;
  1815. X    FILE        *new;
  1816. X    off_t        index_offset, data_offset, new_offset;
  1817. X    long        count, expire_count;
  1818. X    char         *err_message;
  1819. X
  1820. X    old_x = old_d = new = NULL;
  1821. X    
  1822. X
  1823. X    if (trace)
  1824. X    log_entry('T', "Exp %s (%d -> %d)",
  1825. X          gh->group_name, gh->first_l_article, gh->first_article);
  1826. X    
  1827. X    /*
  1828. X     * check whether new first article is collected
  1829. X     */
  1830. X
  1831. X    if (!art_collected(gh, gh->first_article)) {
  1832. X    expire_count = gh->first_l_article - gh->last_l_article + 1;
  1833. X    err_message = NULL;
  1834. X    goto error_handler;    /* renumbering, collect from start */
  1835. X    }
  1836. X    
  1837. X    expire_count = gh->first_article - gh->first_l_article;
  1838. X
  1839. X    new = NULL;
  1840. X
  1841. X    /*
  1842. X     *  Open old files, unlink after open
  1843. X     */
  1844. X
  1845. X    old_x = open_data_file(gh, 'x', OPEN_READ|OPEN_UNLINK);
  1846. X    old_d = open_data_file(gh, 'd', OPEN_READ|OPEN_UNLINK);
  1847. X
  1848. X    if (old_x == NULL || old_d == NULL)
  1849. X    expire_error("INDEX or DATA file missing");
  1850. X    
  1851. X    /*
  1852. X     *    Create new index file; copy from old
  1853. X     */
  1854. X
  1855. X    new = open_data_file(gh, 'x', OPEN_CREATE);
  1856. X    if (new == NULL)
  1857. X    expire_error("INDEX: cannot create");
  1858. X
  1859. X    /*
  1860. X     *    index_offset is the offset into the old index file for the
  1861. X     *    first entry in the new index file
  1862. X     */
  1863. X
  1864. X    index_offset = get_index_offset(gh, gh->first_article);
  1865. X
  1866. X    /*
  1867. X     *    adjust the group's index write offset (the next free entry)
  1868. X     */
  1869. X
  1870. X    gh->index_write_offset -= index_offset;
  1871. X
  1872. X    /*
  1873. X     *    calculate the number of entries to copy
  1874. X     */
  1875. X
  1876. X    count = gh->index_write_offset / sizeof(off_t);
  1877. X
  1878. X    /*
  1879. X     *    data offset is the offset into the old data file for the
  1880. X     *    first byte in the new data file; it is initialized in the
  1881. X     *    loop below, by reading the entry in the old index file at
  1882. X     *    offset 'index_offset'.
  1883. X     */
  1884. X
  1885. X    data_offset = (off_t)0;
  1886. X
  1887. X    /*
  1888. X     *    read 'count' entries from the old index file starting from
  1889. X     *    index_offset, subtract the 'data_offset', and output the
  1890. X     *    new offset to the new index file.
  1891. X     */
  1892. X
  1893. X    fseek(old_x, index_offset, 0);
  1894. X    
  1895. X    while (--count >= 0) {
  1896. X    if (!db_read_offset(old_x, &new_offset))
  1897. X        expire_error("INDEX: too short");
  1898. X
  1899. X    if (data_offset == (off_t)0) data_offset = new_offset;
  1900. X
  1901. X    new_offset -= data_offset;
  1902. X    if (!db_write_offset(new, &new_offset))
  1903. X        expire_error("NEW INDEX: cannot write");
  1904. X    }
  1905. X
  1906. X    fclose(new);
  1907. X    fclose(old_x); old_x = NULL;
  1908. X    
  1909. X    /*
  1910. X     *    copy from old data file to new data file
  1911. X     */
  1912. X
  1913. X    new = open_data_file(gh, 'd', OPEN_CREATE);
  1914. X    if (new == NULL)
  1915. X    expire_error("DATA: cannot create");
  1916. X    
  1917. X    /*
  1918. X     *    calculate offset for next free entry in the new data file
  1919. X     */
  1920. X
  1921. X    gh->data_write_offset -= data_offset;
  1922. X
  1923. X    /*
  1924. X     *    calculate number of bytes to copy (piece of cake)
  1925. X     */
  1926. X
  1927. X    count = gh->data_write_offset;
  1928. X    
  1929. X    /*
  1930. X     *    copy 'count' bytes from the old data file, starting at offset
  1931. X     *     'data_offset', to the new data file
  1932. X     */
  1933. X
  1934. X    fseek(old_d, data_offset, 0);
  1935. X    while (count > 0) {
  1936. X    char block[1024];
  1937. X    int  count1;
  1938. X    
  1939. X    count1 = fread(block, sizeof(char), 1024, old_d);
  1940. X    if (count1 <= 0)
  1941. X        expire_error("DATA: read error");
  1942. X
  1943. X    if (fwrite(block, sizeof(char), count1, new) != count1)
  1944. X        expire_error("DATA: write error");
  1945. X    
  1946. X    count -= count1;
  1947. X    }
  1948. X    
  1949. X    fclose(new);
  1950. X    fclose(old_d);
  1951. X    
  1952. X    /*
  1953. X     *    Update group entry
  1954. X     */
  1955. X
  1956. X    gh->first_l_article = gh->first_article;
  1957. X
  1958. X    /*
  1959. X     *    Return number of expired articles
  1960. X     */
  1961. X
  1962. X    return expire_count;
  1963. X    
  1964. X
  1965. X
  1966. X    /*
  1967. X     *    Errors end up here.
  1968. X     *    We simply recollect the whole group once more.
  1969. X     */
  1970. X
  1971. Xerror_handler:
  1972. X
  1973. X    if (new) fclose(new);
  1974. X    if (old_x) fclose(old_x);
  1975. X    if (old_d) fclose(old_d);
  1976. X    
  1977. X    if (err_message)
  1978. X    log_entry('E', "Expire Error (%s): %s", gh->group_name, err_message);
  1979. X
  1980. X    clean_group(gh);
  1981. X
  1982. X    /* will be saved & unblocked later */
  1983. X
  1984. X    /*
  1985. X     *    We cannot say whether any articles actually had to be expired,
  1986. X     *    but then we must guess...
  1987. X     */
  1988. X
  1989. X    return expire_count;
  1990. X}
  1991. NO_NEWS_IS_GOOD_NEWS
  1992. chmod 0644 expire.c || echo "restore of expire.c fails"
  1993. set `wc -c expire.c`;Sum=$1
  1994. if test "$Sum" != "4363"
  1995. then echo original size 4363, current size $Sum;fi
  1996. echo "x - extracting folder.c (Text)"
  1997. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > folder.c &&
  1998. X/*
  1999. X * folder handling
  2000. X */
  2001. X
  2002. X#include <errno.h>
  2003. X#include "config.h"
  2004. X#include "articles.h"
  2005. X#include "news.h"
  2006. X#include "term.h"
  2007. X#include "menu.h"
  2008. X
  2009. X
  2010. Xexport int  dont_sort_folders = 0;
  2011. Xexport char *folder_directory  = NULL;
  2012. X
  2013. X/*
  2014. X *     file name completion and expansion
  2015. X */
  2016. X
  2017. X
  2018. Xexpand_file_name(dest, src)
  2019. Xchar *dest, *src;
  2020. X{
  2021. X    register char *cp, *dp, c;
  2022. X    int parse, remap;
  2023. X    char *cur_grp, *cur_art;
  2024. X    
  2025. X    cur_grp = current_group ? current_group->group_name : NULL;
  2026. X    cur_art = (group_file_name && *group_file_name) ? group_path_name : NULL;
  2027. X    
  2028. X    for (dp = dest, parse = 1; c = *src; src++) {
  2029. X    
  2030. X    if (parse) {
  2031. X        
  2032. X        if (c == '+') {
  2033. X        if (folder_directory == NULL) {
  2034. X            if (!(cp = getenv("FOLDER")))
  2035. X            cp = FOLDER_DIRECTORY;
  2036. X            folder_directory = home_relative(cp);
  2037. X        }
  2038. X        
  2039. X        cp = folder_directory;
  2040. X        goto cp_str;
  2041. X        }
  2042. X        
  2043. X        if (c == '~') {
  2044. X        if (src[1] != '/') {
  2045. X            msg("Can't handle ~user expansion (yet)");
  2046. X            return 0;
  2047. X        }
  2048. X        
  2049. X        cp = home_directory;
  2050. X        
  2051. X         cp_str:
  2052. X        while (*cp) *dp++ = *cp++;
  2053. X        if (dp[-1] != '/') *dp++ = '/';
  2054. X        goto no_parse;
  2055. X        }
  2056. X
  2057. X        if (cur_art && c == '%' && (src[1] == ' ' || src[1] == NUL)) {
  2058. X        cp = cur_art;
  2059. X        while (*cp) *dp++ = *cp++;
  2060. X        goto no_parse;
  2061. X        }
  2062. X        
  2063. X    }
  2064. X
  2065. X    if (c == '$' && !isalnum(src[2])) {
  2066. X        remap = 0;
  2067. X        cp = NULL;
  2068. X        
  2069. X        switch (src[1]) {
  2070. X         case 'A':
  2071. X        cp = cur_art;
  2072. X        break;
  2073. X         case 'F':
  2074. X        cp = cur_grp;
  2075. X        remap = 1;
  2076. X        break;
  2077. X         case 'G':
  2078. X        cp = cur_grp;
  2079. X        break;
  2080. X         case 'L':
  2081. X        if (cp = strrchr(cur_grp, '.')) 
  2082. X            cp++;
  2083. X        else 
  2084. X            cp = cur_grp;
  2085. X        break;
  2086. X         case 'N':
  2087. X        if (cur_art) cp = group_file_name;
  2088. X        break;
  2089. X         default:
  2090. X        goto copy;
  2091. X        }        
  2092. X        src++;
  2093. X
  2094. X        if (!cp) {
  2095. X        msg("$%c not defined on this level", c);
  2096. X        return 0;
  2097. X        }
  2098. X
  2099. X        while (*cp) 
  2100. X        if (remap && *cp == '.')
  2101. X            cp++, *dp++ = '/';
  2102. X        else
  2103. X            *dp++ = *cp++;
  2104. X        goto no_parse;
  2105. X    }
  2106. X    
  2107. X    if (c == '/')
  2108. X        if (dp != dest && dp[-1] == '/') goto no_parse;
  2109. X        
  2110. X     copy:
  2111. X    *dp++ = c;
  2112. X    parse = isspace(c);
  2113. X    continue;
  2114. X
  2115. X     no_parse:
  2116. X        parse = 0;
  2117. X    }
  2118. X
  2119. X    *dp = NUL;
  2120. X
  2121. X    return 1;
  2122. X}
  2123. X
  2124. X
  2125. Xfile_completion(path, index)
  2126. Xchar *path;
  2127. Xint index;
  2128. X{
  2129. X    static dir_in_use = 0;
  2130. X    static char *head, *tail = NULL;
  2131. X    static int  tail_offset;
  2132. X    
  2133. X    char nbuf[FILENAME], buffer[FILENAME];
  2134. X    char *dir, *base;
  2135. X
  2136. X    if (path) {
  2137. X    if (dir_in_use) {
  2138. X        close_directory();
  2139. X        dir_in_use = 0;
  2140. X    }
  2141. X    
  2142. X    if (index < 0) return 0;
  2143. X
  2144. X    head = path;
  2145. X    tail = path + index;
  2146. X    }
  2147. X    
  2148. X    if (!dir_in_use) {
  2149. X    path = head;
  2150. X    *tail = NUL;
  2151. NO_NEWS_IS_GOOD_NEWS
  2152. echo "End of part 3"
  2153. echo "File folder.c is continued in part 4"
  2154. echo "4" > s2_seq_.tmp
  2155. exit 0
  2156. ---
  2157. Kim F. Storm        storm@texas.dk        Tel +45 429 174 00
  2158. Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
  2159.       No news is good news, but nn is better!
  2160.  
  2161.