home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / nn / part12 < prev    next >
Text File  |  1989-06-22  |  50KB  |  2,087 lines

  1. Subject:  v19i073:  NN, a Usenet news reader, Part12/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 73
  8. Archive-name: nn/part12
  9.  
  10. #!/bin/sh
  11. # this is part 12 of a multipart archive
  12. # do not concatenate these parts, unpack them in order with /bin/sh
  13. # file pack_date.c continued
  14. #
  15. CurArch=12
  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 pack_date.c"
  26. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' >> pack_date.c
  27. X *    as long as the "ordering" is ok.
  28. X *
  29. X *    The result is NOT a time_t value, i.e. ctime() will
  30. X *    not produce the original Date string.
  31. X *
  32. X *    The date must have format:  [D]D Mmm YY hh:mm:ss GMT
  33. X */
  34. X
  35. Xpack_date(destp, date)
  36. Xtime_stamp *destp;
  37. Xchar *date;
  38. X{
  39. X    time_stamp res;
  40. X    register int min, hour, day, mon, year;
  41. X
  42. X    *destp = 0;
  43. X    if (date == NULL) return;
  44. X    
  45. X    if ((day = next_int(&date)) == 0) return;
  46. X    
  47. X    switch (*date) {
  48. X     case 'J':
  49. X    if (date[1] == 'a') { mon = 0; break; }
  50. X    if (date[2] == 'n') { mon = 5; break; }
  51. X    mon = 6; break;
  52. X     case 'F':
  53. X    mon = 1; break;
  54. X     case 'M':
  55. X    if (date[2] == 'r') { mon = 2; break; }
  56. X    mon = 4; break;
  57. X     case 'A':
  58. X    if (date[1] == 'p') { mon = 3; break; }
  59. X    mon = 7; break;
  60. X     case 'S':
  61. X    mon = 8; break;
  62. X     case 'O':
  63. X    mon = 9; break;
  64. X     case 'N':
  65. X    mon = 10; break;
  66. X     case 'D':
  67. X    mon = 11; break;
  68. X     default:
  69. X    return;
  70. X    }
  71. X    
  72. X    date += 4;
  73. X    
  74. X    year = next_int(&date);
  75. X    hour = next_int(&date);
  76. X    min = next_int(&date);
  77. X    
  78. X    year -= 87;    /* base is 1987 */
  79. X    if (year < 0) year += 100;
  80. X    
  81. X    res = (year * 12 + mon) * 31 + day - 1;
  82. X    res *= 24 * 60;
  83. X    res += (hour * 60) + min;
  84. X
  85. X    *destp = res;
  86. X}
  87. X
  88. X
  89. Xstatic next_int(dp)
  90. Xchar **dp;
  91. X{
  92. X    register char *str = *dp;
  93. X    register i;
  94. X    
  95. X    i = 0;
  96. X    while (*str && isdigit(*str))
  97. X    i = (i * 10) + *str++ - '0';
  98. X    
  99. X    while (*str && (isspace(*str) || *str == ':')) str++;
  100. X
  101. X    *dp = str;
  102. X    return i;
  103. X}
  104. X
  105. X
  106. X#ifdef DATE_TEST
  107. X
  108. X
  109. Xmain()
  110. X{
  111. X    char buffer[128];
  112. X    char *dp;
  113. X    unsigned long t;
  114. X    
  115. X    while (fgets(buffer, 128, stdin)) {
  116. X    dp = strchr(buffer, ':');
  117. X    if (dp == NULL) continue;
  118. X    dp++;
  119. X    while (isspace(*dp)) dp++;
  120. X    pack_date(&t, dp);
  121. X    printf("%lu\t%s\n", t, dp);
  122. X    }
  123. X    
  124. X    nn_exit(0);
  125. X}
  126. X
  127. X#endif
  128. NO_NEWS_IS_GOOD_NEWS
  129. echo "File pack_date.c is complete"
  130. chmod 0644 pack_date.c || echo "restore of pack_date.c fails"
  131. set `wc -c pack_date.c`;Sum=$1
  132. if test "$Sum" != "1938"
  133. then echo original size 1938, current size $Sum;fi
  134. echo "x - extracting pack_name.c (Text)"
  135. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > pack_name.c &&
  136. X/*
  137. X *     pack_name(packed, name, length)
  138. X *    pack sender's name into something sensible, return in packed
  139. X *
  140. X */
  141. X
  142. X#include "config.h"
  143. X
  144. X
  145. X#define    SEP_DOT        0    /* . */
  146. X#define    SEP_PERCENT    1    /* % */
  147. X#define    SEP_SCORE    2    /* _ */
  148. X#define    SEP_AMPERSAND    3    /* @ */
  149. X#define    SEP_BANG    4    /* ! */
  150. X#define    SEP_MAXIMUM    5
  151. X
  152. X
  153. X#define CL_OK        0x0100        /* letter or digit */
  154. X#define CL_SPACE    0x0200        /* cvt to space */
  155. X#define CL_IGNORE    0x0400        /* ignore */
  156. X#define CL_RANGE(c)    0x0800+c    /* space range, end with c */
  157. X#define CL_HYPHEN    0x1000        /* convert to - */
  158. X#define CL_STOP        0x2000        /* discard rest of name */
  159. X#define    CL_SEP        | 0x4000 +        /* address separator */
  160. X
  161. X#define    IS_OK(c)    (Class[c] & CL_OK)
  162. X#define IS_SPACE(c)    (Class[c] & CL_SPACE)
  163. X#define IGNORE(c)    (c & 0x80 || Class[c] & CL_IGNORE)
  164. X#define BEGIN_RANGE(c)    (Class[c] & CL_RANGE(0))
  165. X#define END_RANGE(c)    (Class[c] & 0xff)
  166. X#define IS_HYPHEN(c)    (Class[c] & CL_HYPHEN)
  167. X#define IS_STOP(c)    (Class[c] & CL_STOP)
  168. X#define    IS_SEPARATOR(c)    (Class[c] & (0 CL_SEP 0))
  169. X    
  170. Xstatic short Class[128] = {
  171. X    /* NUL */        CL_STOP ,
  172. X    /* SOH */        CL_IGNORE ,
  173. X    /* STX */        CL_IGNORE ,
  174. X    /* ETX */        CL_IGNORE ,
  175. X    /* EOT */        CL_IGNORE ,
  176. X    /* ENQ */        CL_IGNORE ,
  177. X    /* ACK */        CL_IGNORE ,
  178. X    /* BEL */        CL_IGNORE ,
  179. X    /* BS  */        CL_IGNORE ,
  180. X    /* TAB */        CL_SPACE ,
  181. X    /* NL  */        CL_IGNORE ,
  182. X    /* VT  */        CL_IGNORE ,
  183. X    /* FF  */        CL_IGNORE ,
  184. X    /* CR  */        CL_IGNORE ,
  185. X    /* SO  */        CL_IGNORE ,
  186. X    /* SI  */        CL_IGNORE ,
  187. X    /* DLE */        CL_IGNORE ,
  188. X    /* DC1 */        CL_IGNORE ,
  189. X    /* DC2 */        CL_IGNORE ,
  190. X    /* DC3 */        CL_IGNORE ,
  191. X    /* DC4 */        CL_IGNORE ,
  192. X    /* NAK */        CL_IGNORE ,
  193. X    /* SYN */        CL_IGNORE ,
  194. X    /* ETB */        CL_IGNORE ,
  195. X    /* CAN */        CL_IGNORE ,
  196. X    /* EM  */        CL_IGNORE ,
  197. X    /* SUB */        CL_IGNORE ,
  198. X    /* ESC */        CL_IGNORE ,
  199. X    /* FS  */        CL_IGNORE ,
  200. X    /* GS  */        CL_IGNORE ,
  201. X    /* RS  */        CL_IGNORE ,
  202. X    /* US  */        CL_IGNORE ,
  203. X    
  204. X    /* space */        CL_SPACE ,
  205. X    /*   !   */        CL_SPACE CL_SEP SEP_BANG,
  206. X    /*   "   */        CL_RANGE( '"' ) ,
  207. X    /*   #   */        CL_OK ,
  208. X    /*   $   */        CL_OK ,
  209. X    /*   %   */        CL_OK CL_SEP SEP_PERCENT,
  210. X    /*   &   */        CL_OK ,
  211. X    /*   '   */        CL_OK ,
  212. X    /*   (   */        CL_RANGE( ')' ) ,
  213. X    /*   )   */        CL_IGNORE ,
  214. X    /*   *   */        CL_HYPHEN ,
  215. X    /*   +   */        CL_HYPHEN ,
  216. X    /*   ,   */        CL_STOP ,
  217. X    /*   -   */        CL_HYPHEN ,
  218. X    /*   .   */        CL_SPACE CL_SEP SEP_DOT,
  219. X    /*   /   */        CL_OK ,
  220. X    /*   0   */        CL_OK ,
  221. X    /*   1   */        CL_OK ,
  222. X    /*   2   */        CL_OK ,
  223. X    /*   3   */        CL_OK ,
  224. X    /*   4   */        CL_OK ,
  225. X    /*   5   */        CL_OK ,
  226. X    /*   6   */        CL_OK ,
  227. X    /*   7   */        CL_OK ,
  228. X    /*   8   */        CL_OK ,
  229. X    /*   9   */        CL_OK ,
  230. X    /*   :   */        CL_IGNORE ,
  231. X    /*   ;   */        CL_STOP ,
  232. X    /*   <   */        CL_IGNORE ,
  233. X    /*   =   */        CL_HYPHEN ,
  234. X    /*   >   */        CL_IGNORE ,
  235. X    /*   ?   */        CL_IGNORE ,
  236. X    /*   @   */        CL_OK CL_SEP SEP_AMPERSAND,
  237. X    /*   A   */        CL_OK ,
  238. X    /*   B   */        CL_OK ,
  239. X    /*   C   */        CL_OK ,
  240. X    /*   D   */        CL_OK ,
  241. X    /*   E   */        CL_OK ,
  242. X    /*   F   */        CL_OK ,
  243. X    /*   G   */        CL_OK ,
  244. X    /*   H   */        CL_OK ,
  245. X    /*   I   */        CL_OK ,
  246. X    /*   J   */        CL_OK ,
  247. X    /*   K   */        CL_OK ,
  248. X    /*   L   */        CL_OK ,
  249. X    /*   M   */        CL_OK ,
  250. X    /*   N   */        CL_OK ,
  251. X    /*   O   */        CL_OK ,
  252. X    /*   P   */        CL_OK ,
  253. X    /*   Q   */        CL_OK ,
  254. X    /*   R   */        CL_OK ,
  255. X    /*   S   */        CL_OK ,
  256. X    /*   T   */        CL_OK ,
  257. X    /*   U   */        CL_OK ,
  258. X    /*   V   */        CL_OK ,
  259. X    /*   W   */        CL_OK ,
  260. X    /*   X   */        CL_OK ,
  261. X    /*   Y   */        CL_OK ,
  262. X    /*   Z   */        CL_OK ,
  263. X    /*   [   */        CL_OK ,
  264. X    /*   \   */        CL_OK ,
  265. X    /*   ]   */        CL_OK ,
  266. X    /*   ^   */        CL_IGNORE ,
  267. X    /*   _   */        CL_SPACE CL_SEP SEP_SCORE,
  268. X    /*   `   */        CL_IGNORE ,
  269. X    /*   a   */        CL_OK ,
  270. X    /*   b   */        CL_OK ,
  271. X    /*   c   */        CL_OK ,
  272. X    /*   d   */        CL_OK ,
  273. X    /*   e   */        CL_OK ,
  274. X    /*   f   */        CL_OK ,
  275. X    /*   g   */        CL_OK ,
  276. X    /*   h   */        CL_OK ,
  277. X    /*   i   */        CL_OK ,
  278. X    /*   j   */        CL_OK ,
  279. X    /*   k   */        CL_OK ,
  280. X    /*   l   */        CL_OK ,
  281. X    /*   m   */        CL_OK ,
  282. X    /*   n   */        CL_OK ,
  283. X    /*   o   */        CL_OK ,
  284. X    /*   p   */        CL_OK ,
  285. X    /*   q   */        CL_OK ,
  286. X    /*   r   */        CL_OK ,
  287. X    /*   s   */        CL_OK ,
  288. X    /*   t   */        CL_OK ,
  289. X    /*   u   */        CL_OK ,
  290. X    /*   v   */        CL_OK ,
  291. X    /*   w   */        CL_OK ,
  292. X    /*   x   */        CL_OK ,
  293. X    /*   y   */        CL_OK ,
  294. X    /*   z   */        CL_OK ,
  295. X    /*   {   */        CL_OK ,
  296. X    /*   |   */        CL_OK ,
  297. X    /*   }   */        CL_OK ,
  298. X    /*   ~   */        CL_HYPHEN ,
  299. X    /*  DEL  */        CL_IGNORE 
  300. X} ;
  301. X
  302. X
  303. Xpack_name(dest, source, length)
  304. Xchar *dest, *source;
  305. Xint length;
  306. X{
  307. X    register char *p, *q, *r, c;
  308. X    register int n;
  309. X    char namebuf[129], *name;
  310. X    char *maxq;
  311. X    int lname, lfirst, lmiddle, llast, sep, i;
  312. X    int drop_space, prev_space;
  313. X    char *separator[SEP_MAXIMUM];
  314. X    
  315. X    dest[0] = NUL;
  316. X    
  317. X    if (source == NULL || source[0] == NUL)
  318. X    return 0;
  319. X
  320. X    p = source, q = namebuf, n = 0;
  321. X    
  322. Xnew_partition:
  323. X    for (i = SEP_MAXIMUM; --i >= 0; separator[i] = NULL);
  324. X    
  325. X    while ( c = *p++ ) {
  326. X    if (c == '<') {
  327. X        while (q > namebuf && q[-1] == SP) q--;
  328. X        if (q == namebuf) continue;
  329. X        break;
  330. X    }
  331. X    if (IGNORE(c)) continue;
  332. X    if (q == namebuf && IS_SPACE(c)) continue;
  333. X    if (c == '(') {
  334. X        if (*p == ')') {
  335. X        p++;
  336. X        continue;
  337. X        }
  338. X        if (n++ == 0) {
  339. X        q = namebuf;
  340. X        goto new_partition;
  341. X        }
  342. X        continue;
  343. X    }
  344. X    if (c == ')') {
  345. X        if (--n == 0) break;
  346. X        continue;
  347. X    }
  348. X    if (n > 1) continue;
  349. X    *q++ = c;
  350. X    if (IS_SEPARATOR(c)) {
  351. X        switch (sep = (Class[c] & 0xff)) {
  352. X        
  353. X         case SEP_DOT:
  354. X        if (separator[SEP_AMPERSAND] && q - namebuf <= length)
  355. X            break;
  356. X        continue;
  357. X
  358. X         case SEP_BANG:
  359. X        if (separator[SEP_AMPERSAND]) continue;
  360. X        break;
  361. X        
  362. X         default:
  363. X        if (separator[sep]) continue;
  364. X        break;
  365. X        }
  366. X        
  367. X        separator[sep] = q - 1;
  368. X    }
  369. X    }
  370. X
  371. X    *q = NUL;
  372. X    
  373. X    if (namebuf[0] == NUL) return 0;
  374. X    
  375. X    name = namebuf;
  376. X
  377. X    if (name[0] == '"') {
  378. X    name++;
  379. X    if (q[-1] == '"') *--q = NUL;
  380. X    }
  381. X    
  382. X    if (q - name <= length) goto name_ok;
  383. X    
  384. X    /* sorry for the many goto's -- the 3B2 C compiler does not */
  385. X    /* make correct code for complicated logical expressions!!  */
  386. X    /* not even without -O                    */
  387. X
  388. X    /* We must pack the name to make it fit */
  389. X    
  390. X    /* Name_of_person%... -> Name_of_person */
  391. X
  392. X    if (r = separator[SEP_PERCENT]) {
  393. X    if (!(q = separator[SEP_SCORE]) || q > r ) 
  394. X        goto no_percent;
  395. X    if ((q = separator[SEP_AMPERSAND]) && q < r)
  396. X        goto no_percent;
  397. X    if ((q = separator[SEP_BANG]) && q < r)
  398. X        goto no_percent;
  399. X    *r = NUL;
  400. X    goto parse_name;
  401. X    }
  402. X
  403. X no_percent:
  404. X
  405. X    /* name@site.domain -> name@site */
  406. X   if (r = separator[SEP_AMPERSAND]) {
  407. X
  408. X       if ((q = separator[SEP_PERCENT]) && q < r) {
  409. X       *r = NUL;
  410. X       if (r - name <= length) goto name_ok;
  411. X
  412. X       *q = NUL;
  413. X
  414. X       if (((p = separator[SEP_BANG]) && p < q)
  415. X         || ((p = strrchr(name, '!')) && p < q)) {
  416. X           name = p + 1;
  417. X       }
  418. X
  419. X       if (strchr(name, '.')) 
  420. X           goto parse_name;
  421. X       
  422. X       goto name_ok;
  423. X       }
  424. X
  425. X       if (q = separator[SEP_DOT]) {
  426. X       *q = NUL;
  427. X       goto name_ok;
  428. X       }    
  429. X    
  430. X       *r = NUL;
  431. X       if (r - name <= length) goto name_ok;
  432. X       
  433. X       if ((q = separator[SEP_BANG]) && q < r) {
  434. X       name = q + 1;
  435. X       goto name_ok;
  436. X       }
  437. X
  438. X#ifdef NOTDEF
  439. X       if (strchr(name, '!') == NULL) 
  440. X       goto parse_name; /* take the chance ... */
  441. X#endif
  442. X    goto name_ok;    /* can't do it any better */
  443. X    }
  444. X    
  445. X    
  446. X    /* Phase 1: Normalization (remove superfluous characters) */
  447. X    
  448. X parse_name:
  449. X    
  450. X    for (p = name, lname = 0, prev_space = 0; c = *p; p++) {
  451. X
  452. X/*    
  453. X    if (IGNORE(c)) {
  454. X        *p = TAB;
  455. X        if (p == name) name++;
  456. X        continue;
  457. X    }
  458. X*/
  459. X    
  460. X    if (IS_OK(c)) {
  461. X        lname++;
  462. X        prev_space = 0;
  463. X        continue;
  464. X    }
  465. X    
  466. X    if (IS_HYPHEN(c)) {
  467. X        if (p == name) {
  468. X        name++;
  469. X        continue;
  470. X        }
  471. X        if (prev_space)
  472. X        *p = TAB;
  473. X        else {
  474. X        *p = '-';
  475. X        lname++;
  476. X        }
  477. X        continue;
  478. X    }
  479. X    
  480. X    if (BEGIN_RANGE(c)) {
  481. X        
  482. X        if (p == name) {
  483. X        name++;
  484. X        continue;
  485. X        }
  486. X        
  487. X        c = END_RANGE(c);
  488. X        for (q = p+1; *q && *q != c; q++);
  489. X        if (*q) {
  490. X        if (p[-1] != ' ') lname++;
  491. X        while (p <= q) *p++ = ' ';
  492. X        p--;
  493. X        prev_space++;
  494. X        continue;
  495. X        }
  496. X        c = ' ';
  497. X    }
  498. X    
  499. X    if (IS_SPACE(c)) {
  500. X        *p = ' ';
  501. X        if (p == name) 
  502. X        name++;
  503. X        else
  504. X        if (!prev_space) {
  505. X            lname++;
  506. X            prev_space++;
  507. X        }
  508. X        continue;
  509. X    }
  510. X    
  511. X    if (IS_STOP(c)) {
  512. X        *p = NUL;
  513. X        break;
  514. X    }
  515. X    }
  516. X drop_last_name:
  517. X    while (p > name && (*--p == ' ' || *p == TAB)) *p = NUL;
  518. X    
  519. X    if (lname < length) goto name_ok;
  520. X    
  521. X    
  522. X    /* Phase 2: Reduce middle names */
  523. X    
  524. X    for (r = p, llast = 0; r > name && *r != ' '; r--)
  525. X    if (*r != TAB) llast++;
  526. X    
  527. X    /* r points to space before last name */
  528. X    
  529. X    if (strncmp(r, " Jr", 3) == 0 || strncmp(r, " II", 3) == 0) {
  530. X    p = r+1;
  531. X    lname -= llast;
  532. X    goto drop_last_name;
  533. X    }        
  534. X    
  535. X    if (r == name) goto phase6;    /* only last name */
  536. X    
  537. X    for (q = name, lfirst = 0; *q && *q != ' '; q++)
  538. X    if (*q != TAB) lfirst++;
  539. X    
  540. X    /* q points at space after first name */
  541. X    
  542. X    for (p = q, lmiddle = 0; p < r; ) {
  543. X    /* find next middle name */
  544. X    while (p < r && (*p == ' ' || *p == TAB)) p++;
  545. X    
  546. X    if (p >= r) break; /* found last name */
  547. X    
  548. X    p++; /* skip first char of middle name */
  549. X    for (;*p != ' '; p++) { /* remove rest */
  550. X        if (*p == TAB) continue;
  551. X        *p = TAB;
  552. X        lname--;
  553. X    }
  554. X    lmiddle += 2;    /* initial + space */
  555. X    }
  556. X    
  557. X    if (lname < length) goto name_ok;
  558. X    
  559. X    /* If removing middle names is not enough, but reducing first name instead is, do it that way */
  560. X    
  561. X    if (lname - lmiddle >= length && lname - lfirst + 1 < length) goto phase4;
  562. X    
  563. X    
  564. X    /* Phase 3: Remove middle names */
  565. X    
  566. X    for (p = q; p < r; p++) {
  567. X    if (*p == TAB) continue;
  568. X    if (*p == ' ') continue;
  569. X    *p = TAB;
  570. X    lname -= 2;
  571. X    }
  572. X    
  573. X    if (lname < length) goto name_ok;
  574. X    
  575. X    
  576. X    /* Phase 4: Reduce first name */
  577. X    
  578. X phase4:
  579. X    for (p = name+1; p < q; p++) {
  580. X    if (*p == TAB) continue;
  581. X    if (*p == ' ') continue;
  582. X    *p = TAB;
  583. X    lname--;
  584. X    }
  585. X    
  586. X    if (lname < length) goto name_ok;
  587. X    
  588. X    /* Phase 5: Remove first name */
  589. X    
  590. X    name = r+1;
  591. X    lname--;
  592. X    
  593. X    if (lname < length) goto name_ok;
  594. X    
  595. X    /* Phase 6: Cut last name */
  596. X phase6:
  597. X    goto name_ok;
  598. X    
  599. X name_ok:
  600. X
  601. X    q = dest;
  602. X    maxq = q + length;
  603. X
  604. X    drop_space = 1;
  605. X    
  606. X    for (p = name; *p && q < maxq ; p++) {
  607. X    if (*p == TAB) continue;
  608. X    
  609. X    if ( *p == ' ' ) {
  610. X        if (!drop_space) {
  611. X        drop_space = 1;
  612. X        *q++ = ' ';
  613. X        }
  614. X        continue;
  615. X    }
  616. X    drop_space = 0;
  617. X    *q++ = *p;
  618. X    }
  619. X    
  620. X    *q = NUL;
  621. X    
  622. X    return strlen(dest);
  623. X}    
  624. X
  625. NO_NEWS_IS_GOOD_NEWS
  626. chmod 0644 pack_name.c || echo "restore of pack_name.c fails"
  627. set `wc -c pack_name.c`;Sum=$1
  628. if test "$Sum" != "10093"
  629. then echo original size 10093, current size $Sum;fi
  630. echo "x - extracting pack_subject.c (Text)"
  631. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > pack_subject.c &&
  632. X/*
  633. X * pack subject by eliminating RE prefixes and - (nf) suffixes
  634. X */
  635. X
  636. X#include "config.h"
  637. X
  638. Xpack_subject(dest, src, re_counter_ptr, max_length)
  639. Xregister char *dest, *src;
  640. Xint *re_counter_ptr, max_length;
  641. X{
  642. X    int re;
  643. X    char *start_dest;
  644. X    register char *max_dest;
  645. X    
  646. X    re = 0;
  647. X
  648. X    if (src) {
  649. X    start_dest = dest;
  650. X    max_dest = dest + max_length;
  651. X    
  652. X    while (*src) {
  653. X        if (isspace(*src)) {
  654. X        src++;
  655. X        continue;
  656. X        }
  657. X        
  658. X        /* count and remove 'Re: Re: ...' */
  659. X
  660. X        if (*src != 'R' && *src != 'r') break;
  661. X        *dest++ = *src++;
  662. X        
  663. X        if (*src != 'e' && *src != 'E') break;
  664. X        *dest++ = *src++;
  665. X
  666. X        if (*src == ':' || *src == ' ') {
  667. X        src++;
  668. X        dest = start_dest;
  669. X        re++;
  670. X        continue;
  671. X        }
  672. X        
  673. X        if (*src != '^') break;
  674. X        
  675. X        src++;
  676. X        dest = start_dest;
  677. X        
  678. X        while (isdigit(*src)) *dest++ = *src++;
  679. X        if (dest == start_dest) 
  680. X        re++;
  681. X        else {
  682. X        *dest = NUL;
  683. X        dest = start_dest;
  684. X        re += atoi(dest);
  685. X        }
  686. X        if (*src == ':') src++;
  687. X    }
  688. X    
  689. X    while (*src && dest < max_dest) {
  690. X        if (*src == '-' && strncmp("- (nf)", src, 5) == 0) break;
  691. X        *dest++ = *src++;
  692. X    }
  693. X    }
  694. X    
  695. X    *dest = NUL;
  696. X    *re_counter_ptr = (char)re;
  697. X    
  698. X    return dest - start_dest;
  699. X}
  700. NO_NEWS_IS_GOOD_NEWS
  701. chmod 0644 pack_subject.c || echo "restore of pack_subject.c fails"
  702. set `wc -c pack_subject.c`;Sum=$1
  703. if test "$Sum" != "1207"
  704. then echo original size 1207, current size $Sum;fi
  705. echo "x - extracting patchlevel.h (Text)"
  706. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > patchlevel.h &&
  707. X/*
  708. X * Current patch level (initial level is zero)
  709. X *
  710. X * Modification history:
  711. X *
  712. X *    1988-07-20:  Beta-test release 6.0     (Denmark)
  713. X *    1988-11-01:  Distributed release 6.1     (Europe)
  714. X *    1989-03-21:  Distributed release 6.2beta (FTP)
  715. X *    1989-05-30:  Distributed release 6.3    (World)
  716. X */
  717. X
  718. X#define PATCHLEVEL 0
  719. X
  720. NO_NEWS_IS_GOOD_NEWS
  721. chmod 0644 patchlevel.h || echo "restore of patchlevel.h fails"
  722. set `wc -c patchlevel.h`;Sum=$1
  723. if test "$Sum" != "305"
  724. then echo original size 305, current size $Sum;fi
  725. echo "x - extracting prefix.sh (Text)"
  726. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > prefix.sh &&
  727. XWARNING: DON'T CHANGE THE ORDER OR CONTENTS OF THE FOLLOWING LINES
  728. X
  729. X#include "config.h"
  730. X#include "patchlevel.h"
  731. X#include "update.h"
  732. X
  733. X--------CUT PREFIX HERE--------
  734. X&!/bin/sh
  735. X
  736. X& Release RELEASE,VERSION,PATCHLEVEL, No. UPDATE
  737. X
  738. X& Do not edit this file directly.
  739. X& It is generated from the corresponding .sh file.
  740. X
  741. X
  742. XSPOOL=NEWS_DIRECTORY
  743. X
  744. XTMP=TMP_DIRECTORY
  745. X
  746. XLIB=LIB_DIRECTORY
  747. X
  748. XDB=DB_DIRECTORY
  749. X
  750. XINEWS=INEWS_PATH
  751. X
  752. XRECMAIL=REC_MAIL
  753. X
  754. X#ifdef APPEND_SIGNATURE
  755. XAPPENDSIG=true
  756. X#else
  757. XAPPENDSIG=false
  758. X#endif
  759. X
  760. XPG=PAGER
  761. X
  762. X#ifdef NNTP
  763. X#undef NNTP
  764. XNNTP=true
  765. XACTIVE=$DB/ACTIVE
  766. X#else
  767. XNNTP=false
  768. XACTIVE=NEWS_ACTIVE
  769. X#endif
  770. NO_NEWS_IS_GOOD_NEWS
  771. chmod 0644 prefix.sh || echo "restore of prefix.sh fails"
  772. set `wc -c prefix.sh`;Sum=$1
  773. if test "$Sum" != "600"
  774. then echo original size 600, current size $Sum;fi
  775. echo "x - extracting rc.c (Text)"
  776. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > rc.c &&
  777. X/*
  778. X * rc management routines
  779. X */
  780. X
  781. X#include "config.h"
  782. X#include "term.h"
  783. X#include "debug.h"
  784. X
  785. Xexport int  keep_rc_backup = 1;
  786. Xexport int  no_update = 0;
  787. Xexport int  use_newsrc = 0;
  788. X
  789. Xexport long unread_articles;    /* estimate of unread articles */
  790. Xexport int  unread_groups;
  791. X
  792. X
  793. Xstatic FILE *rc = NULL;        /* rc_file descriptor */
  794. X
  795. Xstatic char RC[] = "rc";
  796. Xstatic char BAK[] = "rc.bak";
  797. Xstatic char NEWSRC[] = ".newsrc";
  798. X
  799. Xstatic int  has_newsrc = 0;
  800. X
  801. X/* RC lines have the format:  */
  802. X/*    SUBSCR space LASTART space GROUPNUM space NAME */
  803. X
  804. X#define    SUBSCRZ            1
  805. X#define    SUBSCRPOS        0
  806. X#define SUBSCR(buf)        buf[0]
  807. X
  808. X/* NOTICE THAT LASTARTZ IS HARDCODED IN A printf FORMAT STRING LATER ON */
  809. X
  810. X#define LASTARTZ        6
  811. X#define    LASTARTPOS        (SUBSCRPOS + SUBSCRZ + 1)
  812. X#define    LASTART(buf)        atol(buf + LASTARTPOS)
  813. X
  814. X#define GROUPNAMEPOS        (LASTARTPOS + LASTARTZ + 1)
  815. X#define    GROUPNAME(buf)        (buf + GROUPNAMEPOS)
  816. X
  817. X#define    NEW_OFFSET    ((off_t)1)    /* append to rc_file when written */
  818. X
  819. X/*
  820. X * read rc file info to group headers
  821. X * master file has been read in
  822. X */
  823. X
  824. X#define    G_OLD    G_NEW    /* inverse use during rc reading */
  825. X#define G_RENUM    G_DONE
  826. X
  827. X
  828. Xvisit_rc_file()
  829. X{
  830. X    FILE *bak, *newsrc;
  831. X    register int c;
  832. X    register group_header *gh;
  833. X    register char *cp;
  834. X    char line[512];
  835. X    off_t rcpos;        /* position in rc */
  836. X    int warn_duplicates = 0, mk_bak, rd_newsrc, bak_used;
  837. X    time_t m_rc, m_newsrc;
  838. X    char rc_path[FILENAME], bak_path[FILENAME];
  839. X    
  840. X    strcpy(rc_path, relative(nn_directory, RC));
  841. X    strcpy(bak_path, relative(nn_directory, BAK));
  842. X
  843. X    rc = NULL;    /* open rc-file */
  844. X    rewind_rc(rc_path, OPEN_READ);
  845. X
  846. X    m_rc = 0;
  847. X    if (rc != NULL) {
  848. X    fseek(rc, (off_t)0, 2);
  849. X    if (ftell(rc) > (off_t)0) {
  850. X        fseek(rc, 0L, 0);
  851. X            m_rc = m_time(rc);
  852. X    }
  853. X    }
  854. X    
  855. X    rd_newsrc = use_newsrc;
  856. X    m_newsrc = 0;
  857. X    mk_bak = keep_rc_backup;
  858. X    bak_used = 0;
  859. X    
  860. X    if (m_rc == 0) {        /* rc empty (i.e. new or corrupted) */
  861. X    if ((bak = open_file(bak_path, OPEN_READ)) != NULL) {
  862. X        m_rc = m_time(bak);
  863. X        if (m_rc >= m_newsrc) {
  864. X        printf("\nRestoring %s from %s\n", RC, BAK);
  865. X        rewind_rc(rc_path, OPEN_CREATE | MUST_EXIST);
  866. X        while ((c = getc(bak)) != EOF) putc(c, rc);
  867. X        rewind_rc(rc_path, OPEN_READ | MUST_EXIST);
  868. X        fclose(bak);
  869. X        mk_bak = 0;
  870. X        bak_used = 1;
  871. X        }
  872. X    } else {
  873. X        rd_newsrc = 1;
  874. X        display_help("welcome");
  875. X    }
  876. X    }
  877. X
  878. X    if (rd_newsrc) {
  879. X    newsrc = open_file(relative(home_directory, NEWSRC), OPEN_READ);
  880. X    if (newsrc != NULL) {
  881. X        m_newsrc = m_time(newsrc);
  882. X        has_newsrc = 1;
  883. X    }
  884. X    }
  885. X    
  886. X    if (has_newsrc) {
  887. X    if (m_rc < m_newsrc) {
  888. X        if (bak_used) {
  889. X        printf("\n%s is newer than %s -- use %s ? ",
  890. X               NEWSRC, BAK, NEWSRC);
  891. X        fl;
  892. X        if (!yes(0)) m_newsrc = 0;
  893. X        }
  894. X
  895. X        if (m_newsrc != 0) {
  896. X        
  897. X        printf("\nReading from %s\n", NEWSRC);
  898. X
  899. X        if (m_rc != 0) {
  900. X            fclose(rc);
  901. X            rc = NULL;
  902. X            
  903. X            unlink(bak_path);
  904. X            if (link(rc_path, bak_path) < 0 || unlink(rc_path) < 0)
  905. X            user_error("Cannot backup %s file\n", RC);
  906. X            
  907. X            mk_bak = 0;
  908. X        }    
  909. X        
  910. X        rewind_rc(rc_path, OPEN_CREATE | MUST_EXIST);
  911. X        read_newsrc(newsrc);
  912. X        rewind_rc(rc_path, OPEN_READ | MUST_EXIST);
  913. X        }
  914. X    }
  915. X    
  916. X    fclose(newsrc);
  917. X    }
  918. X    
  919. X    if (no_update) mk_bak = 0;
  920. X
  921. X    bak = mk_bak ? open_file(bak_path, OPEN_CREATE | MUST_EXIST) : NULL;
  922. X
  923. X    for(;;) {
  924. X    rcpos = ftell(rc);
  925. X    
  926. X    c = getc(rc);
  927. X    
  928. X    cp = line;
  929. X    while (c != NL) {
  930. X        if (c == EOF) goto endloop;
  931. X        *cp++ = c;
  932. X        c = getc(rc);
  933. X    }
  934. X    *cp = NUL;
  935. X    if (bak != NULL) {
  936. X        fputs(line, bak);
  937. X        fputc(NL, bak);
  938. X    }
  939. X    
  940. X    if (SUBSCR(line) != '+' && SUBSCR(line) != '!') {
  941. X        /* unrecognized line */
  942. X        continue;
  943. X    }
  944. X    
  945. X    if ((gh = lookup(GROUPNAME(line))) == NULL) continue;
  946. X
  947. X    if (gh->group_flag & G_OLD) {
  948. X        printf("Duplicated entry in rc file: %s\n", gh->group_name);
  949. X        warn_duplicates++;
  950. X    }
  951. X    gh->rc_offset = rcpos;
  952. X    
  953. X    gh->group_flag |= G_OLD;
  954. X    if (SUBSCR(line) == '+')
  955. X        gh->group_flag |= G_SUBSCRIPTION;
  956. X    
  957. X    gh->last_article = LASTART(line);
  958. X    
  959. X    if (gh->last_article > gh->last_l_article)
  960. X        gh->group_flag |= G_RENUM;    /* mark for use below */
  961. X    
  962. X    if (gh->first_l_article > gh->last_article ||
  963. X        gh->last_article > gh->last_l_article)
  964. X        gh->last_article = gh->first_l_article - 1;
  965. X    
  966. X    if (gh->last_article < 0) gh->last_article = 0;
  967. X    }    
  968. X
  969. Xendloop:
  970. X    if (warn_duplicates) {
  971. X    printf("You can repair this using \"nntidy\"\n");
  972. X    any_key(0);
  973. X    }    
  974. X
  975. X    rewind_rc(rc_path, OPEN_UPDATE | MUST_EXIST);
  976. X
  977. X    Loop_Groups_Header(gh) {
  978. X    if (gh->group_flag & G_OLD) {
  979. X        if (gh->group_flag & G_RENUM) /* group is renumbered */
  980. X        write_rc_entry(gh, 0);
  981. X        gh->group_flag &= ~(G_NEW | G_RENUM);
  982. X    } else {
  983. X        gh->group_flag |= G_SUBSCRIPTION | G_NEW;
  984. X        gh->last_article = gh->first_l_article - 1;
  985. X        if (gh->last_article < 0) gh->last_article = 0;
  986. X        gh->rc_offset = NEW_OFFSET;
  987. X    }
  988. X    gh->first_article = gh->last_article;
  989. X    }
  990. X    
  991. X    if (bak != NULL) fclose(bak);
  992. X    
  993. X    if (no_update) {
  994. X    fclose(rc);
  995. X    rc = NULL;
  996. X    } else
  997. X    fflush(rc);
  998. X}
  999. X
  1000. X
  1001. Xrewind_rc(path, mode)
  1002. Xchar *path;
  1003. X{
  1004. X    if (rc != NULL) fclose(rc);
  1005. X    rc = open_file(path, mode);
  1006. X}
  1007. X
  1008. X
  1009. Xrestore_bak()
  1010. X{
  1011. X    if (no_update) 
  1012. X    return 1;
  1013. X    
  1014. X    if (!keep_rc_backup) {
  1015. X    msg("No %s file ('backup' is not set)", BAK);
  1016. X    return 0;
  1017. X    }
  1018. X
  1019. X    prompt("Are you sure? ");
  1020. X    if (!yes(1)) return 0;
  1021. X    
  1022. X    fclose(rc);    /* cannot use close_rc() since it would update .newsrc */
  1023. X    rc = NULL;
  1024. X    
  1025. X    if (chdir(nn_directory) < 0) goto err;
  1026. X
  1027. X    if (unlink(RC) < 0) goto err;
  1028. X    if (link(BAK, RC) < 0) goto err;
  1029. X    if (unlink(BAK) < 0) goto err;
  1030. X    
  1031. X    return 1;
  1032. X
  1033. X err:
  1034. X    clrdisp();
  1035. X    printf("Restore of %s file failed\n\n", RC);
  1036. X    printf("Check state of %s and %s files\n", RC, BAK);
  1037. X    nn_exit(1);
  1038. X    /*NOTREACHED*/
  1039. X}
  1040. X
  1041. Xupdate_rc(gh)
  1042. Xregister group_header *gh;
  1043. X{
  1044. X    add_unread(gh, -1);
  1045. X
  1046. X    if (no_update || gh->group_flag & G_RC_UPDATED) return;
  1047. X
  1048. X    gh->last_article = gh->last_l_article;
  1049. X
  1050. X#ifdef RC_TEST    
  1051. X    if (Debug & RC_TEST) 
  1052. X    fprintf(stderr, "upd_rc(%s) pos=%ld, artno=%ld\n",
  1053. X        gh->group_name, gh->rc_offset, gh->last_article);
  1054. X#endif
  1055. X
  1056. X    write_rc_entry(gh, 0);
  1057. X    
  1058. X    if (gh->group_flag & G_READ) return;
  1059. X    
  1060. X    gh->group_flag |= G_READ;
  1061. X
  1062. X    if ((gh->group_flag & G_SUBSCRIPTION) == 0) return;
  1063. X}
  1064. X
  1065. X
  1066. Xrestore_rc(gh, count)
  1067. Xregister group_header *gh;
  1068. Xlong count;
  1069. X{
  1070. X    if (no_update || (count == 0 && (gh->group_flag & G_RC_UPDATED) == 0))
  1071. X    return 0;
  1072. X
  1073. X    if (gh->group_flag & G_READ || count > 0) {
  1074. X    add_unread(gh, -1);
  1075. X    
  1076. X    if (count > 0) {
  1077. X        gh->last_article = gh->last_l_article - count;
  1078. X        if (gh->last_article < gh->first_l_article)
  1079. X        gh->last_article = gh->first_l_article - 1;
  1080. X        gh->first_article = gh->last_article;
  1081. X    } else
  1082. X        gh->last_article = gh->first_article;
  1083. X
  1084. X#ifdef RC_TEST    
  1085. X    if (Debug & RC_TEST) 
  1086. X        fprintf(stderr, "restore_rc(%s) pos=%ld, artno=%ld\n",
  1087. X            gh->group_name, gh->rc_offset, gh->last_article);
  1088. X#endif
  1089. X
  1090. X    write_rc_entry(gh, 0);
  1091. X    
  1092. X    gh->group_flag &= ~(G_READ|G_RC_UPDATED);
  1093. X    
  1094. X    add_unread(gh, 1);
  1095. X    
  1096. X    return 1;
  1097. X    }
  1098. X    return 0;
  1099. X}
  1100. X
  1101. X
  1102. Xclose_rc()
  1103. X{
  1104. X    off_t endrc;
  1105. X    
  1106. X    if (rc == NULL) return;
  1107. X    
  1108. X    if (use_newsrc) {
  1109. X    write_newsrc();
  1110. X
  1111. X    fflush(rc);
  1112. X    fseek(rc, 0L, 2);    /* touch rc file */
  1113. X    if ((endrc = ftell(rc)) == 0)
  1114. X        fprintf(rc, "#\n");
  1115. X    else {
  1116. X        fflush(rc);
  1117. X        fseek(rc, endrc - 1, 0);
  1118. X        fputc(NL, rc);
  1119. X    }
  1120. X    }
  1121. X    
  1122. X    fclose(rc);
  1123. X    rc = NULL;
  1124. X}
  1125. X
  1126. X
  1127. X
  1128. Xcount_unread_articles(trace)
  1129. Xint trace;
  1130. X{
  1131. X    register group_header *gh;
  1132. X    long n;
  1133. X    
  1134. X    unread_articles = 0;
  1135. X    unread_groups = 0;
  1136. X    
  1137. X    Loop_Groups_Header(gh) {
  1138. X    gh->group_flag &= ~G_UNREAD_COUNT;
  1139. X
  1140. X    if ((gh->group_flag & G_SUBSCRIPTION) == 0) continue;
  1141. X
  1142. X    if (gh->last_l_article > gh->last_article) {
  1143. X        n = unread_articles;
  1144. X        add_unread(gh, 1);
  1145. X        if (trace)
  1146. X        printf("%s: %d\n", gh->group_name, unread_articles - n);
  1147. X    }
  1148. X    }
  1149. X}
  1150. X
  1151. X
  1152. Xprt_unread(format)
  1153. Xregister char *format;
  1154. X{
  1155. X    if (format == NULL) {
  1156. X    printf("No News (is good news)\n");
  1157. X    return;
  1158. X    }
  1159. X    
  1160. X    while (*format) {
  1161. X    if (*format != '%') {
  1162. X        putchar(*format++);
  1163. X        continue;
  1164. X    }
  1165. X    format++;
  1166. X    switch (*format++) {
  1167. X     case 'u':
  1168. X        printf("%ld unread article%s", 
  1169. X           unread_articles, 
  1170. X           unread_articles == 1 ? "" : "s");
  1171. X        continue;
  1172. X     case 'g':
  1173. X        printf("%d group%s",
  1174. X           unread_groups, 
  1175. X           unread_groups == 1 ? "" : "s");
  1176. X        continue;
  1177. X     case 'i':
  1178. X        printf(unread_articles == 1 ? "is" : "are");
  1179. X        continue;
  1180. X     case 'U':
  1181. X        printf("%ld", unread_articles);
  1182. X        continue;
  1183. X     case 'G':
  1184. X        printf("%d", unread_groups);
  1185. X        continue;
  1186. X    }
  1187. X    }
  1188. X}
  1189. X
  1190. X
  1191. Xadd_unread(gh, mode)
  1192. Xgroup_header *gh;
  1193. Xint mode;    /* +1 => add, -1 => subtract */
  1194. X{
  1195. X    long art;
  1196. X    int was_unread;
  1197. X    
  1198. X    art = gh->last_l_article - gh->last_article;
  1199. X    was_unread = (gh->group_flag & G_UNREAD_COUNT);
  1200. X    
  1201. X    if (mode > 0) {
  1202. X    if (was_unread) return 0;
  1203. X    unread_articles += art;
  1204. X    unread_groups++;
  1205. X    gh->group_flag |= G_UNREAD_COUNT;
  1206. X    } else {
  1207. X    if (!was_unread) return 0;
  1208. X    unread_articles -= art;
  1209. X    unread_groups--;
  1210. X    gh->group_flag &= ~G_UNREAD_COUNT;
  1211. X    }    
  1212. X    
  1213. X    return was_unread;
  1214. X}
  1215. X
  1216. X
  1217. X/*
  1218. X * write one line on rc_file
  1219. X */
  1220. X
  1221. Xwrite_rc_entry(gh, new)
  1222. Xgroup_header *gh;
  1223. Xint new;    /* 0 => old, 1 => quick append, 2 => normal append */
  1224. X{
  1225. X    if (gh->rc_offset == NEW_OFFSET) new = 2;
  1226. X    
  1227. X    if (new) {
  1228. X    if (new == 2) fseek(rc, (off_t)0, 2);
  1229. X    gh->rc_offset = ftell(rc);
  1230. X    } else
  1231. X    if (fseek(rc, gh->rc_offset, 0) < 0)
  1232. X        user_error("Seek error on %s file", RC);
  1233. X
  1234. X    /*
  1235. X     * the 'last article' is not updated in the rc file
  1236. X     * when a group is unsubscribed; if it is later resubscribed,
  1237. X     * the present articles will still be unread (if they exist)
  1238. X     */
  1239. X
  1240. X    /* update article number */
  1241. X    
  1242. X    fprintf(rc, "%c %06ld",    /* MUST CHANGE IF LASTARTZ CHANGES */
  1243. X        (gh->group_flag & G_SUBSCRIPTION) ? '+' : '!',
  1244. X        (long)(gh->last_article));
  1245. X
  1246. X    if (new) {
  1247. X    fputc(' ', rc);
  1248. X    fputs(gh->group_name, rc);
  1249. X    fputc(NL, rc);
  1250. X    }
  1251. X
  1252. X    fflush(rc);
  1253. X}
  1254. X
  1255. X
  1256. X/*
  1257. X * Old-style .newsrc support
  1258. X */
  1259. X
  1260. Xstatic read_newsrc(newsrc)
  1261. XFILE *newsrc;
  1262. X{
  1263. X    copy_newsrc(newsrc, (FILE *)NULL);
  1264. X}
  1265. X
  1266. Xstatic write_newsrc()
  1267. X{
  1268. X    char newsrc_path[FILENAME], bak_path[FILENAME];
  1269. X    FILE *newsrc, *bak;
  1270. X    
  1271. X    strcpy(newsrc_path, relative(home_directory, NEWSRC));
  1272. X    sprintf(bak_path, "%s.bak", newsrc_path);
  1273. X    
  1274. X    if (has_newsrc) {
  1275. X    unlink(bak_path);
  1276. X    if (link(newsrc_path, bak_path) < 0 || unlink(newsrc_path) < 0)
  1277. X        user_error("Cannot backup %s file\n", newsrc_path);
  1278. X
  1279. X    bak = open_file(bak_path, OPEN_READ | MUST_EXIST);
  1280. X    } else
  1281. X    bak = NULL;
  1282. X
  1283. X    if (file_exist(newsrc_path, (char *)NULL)) {
  1284. X    /* This is real paranoia ... don't let people lose their .newsrc */
  1285. X    /* This should not happen - but it has been seen */
  1286. X    log_entry('E', "failed to backup %s", newsrc_path);
  1287. X    fprintf(stderr, "PROBLEM... YOUR %s WAS NOT UPDATED\n", NEWSRC);
  1288. X    if (bak != NULL) fclose(bak);
  1289. X    return;
  1290. X    }
  1291. X    
  1292. X    newsrc = open_file(newsrc_path, OPEN_CREATE | MUST_EXIST);
  1293. X    copy_newsrc(bak, newsrc);
  1294. X    if (bak != NULL) fclose(bak);
  1295. X    fclose(newsrc);
  1296. X}
  1297. X
  1298. Xstatic copy_newsrc(old_rc, new_rc)
  1299. XFILE *old_rc, *new_rc;
  1300. X{
  1301. X    char buf[2048];
  1302. X    char *sub, *last, subscr;
  1303. X    long atol();
  1304. X    register group_header *gh;
  1305. X    
  1306. X    Loop_Groups_Header(gh)
  1307. X    gh->group_flag &= ~G_DONE;
  1308. X
  1309. X    if (old_rc != NULL) {
  1310. X    /* NEWSRC lines have the following format         */
  1311. X    /*    NAME(n)SUBSCR(1) space NUM[,NUM][-NUM]...     */
  1312. X
  1313. X    while (fgets(buf, 2048, old_rc) != NULL) {
  1314. X        subscr = 0;
  1315. X        if (sub = strchr(buf, ':'))
  1316. X        subscr = 1;
  1317. X        else 
  1318. X        sub = strchr(buf, '!');
  1319. X        
  1320. X        if (sub == NULL) {
  1321. X        if (new_rc != NULL) goto output_unchanged;
  1322. X        continue;
  1323. X        }
  1324. X        
  1325. X        *sub = NUL;
  1326. X        gh = lookup(buf);
  1327. X        *sub++ = subscr ? ':' : '!';
  1328. X        
  1329. X        if (gh == NULL) {
  1330. X        if (new_rc != NULL) goto output_unchanged;
  1331. X        continue;
  1332. X        }
  1333. X        
  1334. X        if (new_rc != NULL) {
  1335. X        if (gh->group_flag & G_DONE) continue;
  1336. X        gh->group_flag |= G_DONE;
  1337. X        if (!subscr) goto output_unchanged;
  1338. X        write_newsrc_entry(new_rc, gh, (*sub == NL) ? 1 : 0);
  1339. X        continue;
  1340. X        }
  1341. X        
  1342. X        /* Notice: unread articles before the last read article are lost */
  1343. X        
  1344. X        if (*sub == NL)    /* new group */
  1345. X        continue;
  1346. X        
  1347. X        if (subscr) {
  1348. X        last = strrchr(sub, '-');
  1349. X        if (last == NULL) last = strrchr(sub, ',');
  1350. X        if (last == NULL) last = strrchr(sub, ' ');
  1351. X        if (last == NULL) last = "0"; else last++;
  1352. X        
  1353. X        gh->last_article = atol(last);
  1354. X        gh->group_flag |= G_SUBSCRIPTION;
  1355. X        } else
  1356. X        gh->last_article = 0;
  1357. X        
  1358. X        gh->rc_offset = NEW_OFFSET;
  1359. X        
  1360. X        write_rc_entry(gh, 0);
  1361. X        continue;
  1362. X        
  1363. X     output_unchanged:
  1364. X        fputs(buf, new_rc);
  1365. X    }
  1366. X    }    
  1367. X
  1368. X    Loop_Groups_Header(gh) {
  1369. X    if (new_rc != NULL) {
  1370. X        if (gh->group_flag & G_DONE) continue;
  1371. X        write_newsrc_entry(new_rc, gh, -1);
  1372. X    } else {
  1373. X        gh->rc_offset = 0;
  1374. X        gh->last_article = 0;
  1375. X        gh->group_flag &= G_MASTER_FLAGS;
  1376. X    }
  1377. X    }
  1378. X
  1379. X    return 1;
  1380. X}
  1381. X
  1382. Xwrite_newsrc_entry(newsrc, gh, also_new)
  1383. XFILE *newsrc;
  1384. Xregister group_header *gh;
  1385. Xint also_new;
  1386. X{
  1387. X    if ((gh->group_flag & G_READ) == 0 && (gh->group_flag & G_NEW)) {
  1388. X    if (also_new < 0) return;
  1389. X    } else
  1390. X    also_new = 0;
  1391. X    
  1392. X    fprintf(newsrc, "%s%c", gh->group_name,
  1393. X        (gh->group_flag & G_SUBSCRIPTION) ? ':' : '!');
  1394. X
  1395. X    if (also_new) {
  1396. X    fputc(NL, newsrc);
  1397. X    return;
  1398. X    }
  1399. X    
  1400. X    if (gh->first_l_article > gh->last_article) 
  1401. X    fprintf(newsrc, " %s%d\n", 
  1402. X        gh->first_l_article > 2 ? "1-" : "",
  1403. X        gh->first_l_article - 1);
  1404. X    else
  1405. X    fprintf(newsrc, " %d-%d\n", gh->first_l_article, gh->last_article);
  1406. X}
  1407. NO_NEWS_IS_GOOD_NEWS
  1408. chmod 0644 rc.c || echo "restore of rc.c fails"
  1409. set `wc -c rc.c`;Sum=$1
  1410. if test "$Sum" != "13314"
  1411. then echo original size 13314, current size $Sum;fi
  1412. echo "x - extracting regexp.c (Text)"
  1413. sed 's/^X//' << 'NO_NEWS_IS_GOOD_NEWS' > regexp.c &&
  1414. X/*
  1415. X * regexp.c - regular expression matching
  1416. X *
  1417. X * NOTICE: THIS CODE HAS BEEN MODIFIED TO FIT THE NN ENVIRONMENT.
  1418. X *
  1419. X * DESCRIPTION
  1420. X *
  1421. X *    This source was taken from the pax posting in comp.sources.unix.
  1422. X *
  1423. X *    Underneath the reformatting and comment blocks which were added to 
  1424. X *    make it consistent with the rest of the code, you will find a
  1425. X *    modified version of Henry Specer's regular expression library.
  1426. X *    Henry's functions were modified to provide the minimal regular
  1427. X *    expression matching, as required by P1003.  Henry's code was
  1428. X *    copyrighted, and copy of the copyright message and restrictions
  1429. X *    are provided, verbatim, below:
  1430. X *
  1431. X *    Copyright (c) 1986 by University of Toronto.
  1432. X *    Written by Henry Spencer.  Not derived from licensed software.
  1433. X *
  1434. X *    Permission is granted to anyone to use this software for any
  1435. X *    purpose on any computer system, and to redistribute it freely,
  1436. X *    subject to the following restrictions:
  1437. X *
  1438. X *    1. The author is not responsible for the consequences of use of
  1439. X *         this software, no matter how awful, even if they arise
  1440. X *       from defects in it.
  1441. X *
  1442. X *    2. The origin of this software must not be misrepresented, either
  1443. X *       by explicit claim or by omission.
  1444. X *
  1445. X *    3. Altered versions must be plainly marked as such, and must not
  1446. X *       be misrepresented as being the original software.
  1447. X *
  1448. X *     Beware that some of this code is subtly aware of the way operator
  1449. X *     precedence is structured in regular expressions.  Serious changes in
  1450. X *     regular-expression syntax might require a total rethink.
  1451. X *
  1452. X * AUTHORS
  1453. X *
  1454. X *     Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
  1455. X *     Henry Spencer, University of Torronto (henry@utzoo.edu)
  1456. X *
  1457. X * Sponsored by The USENIX Association for public distribution. 
  1458. X *
  1459. X * $Log:    regexp.c,v $
  1460. X * Revision 1.1  88/12/23  18:02:32  mark
  1461. X * Initial revision
  1462. X * 
  1463. X */
  1464. X
  1465. X#define NN
  1466. X
  1467. X/* Headers */
  1468. X
  1469. X#ifdef NN
  1470. X#include "config.h"
  1471. X#include "regexp.h"
  1472. X#else
  1473. X#include "pax.h"
  1474. X
  1475. X#ifndef lint
  1476. Xstatic char    *Ident = "$Id: regexp.c,v 1.1 88/12/23 18:02:32 mark Rel $";
  1477. X#endif
  1478. X#endif
  1479. X
  1480. X/*
  1481. X * The "internal use only" fields in regexp.h are present to pass info from
  1482. X * compile to execute that permits the execute phase to run lots faster on
  1483. X * simple cases.  They are:
  1484. X *
  1485. X * regstart    char that must begin a match; '\0' if none obvious
  1486. X * reganch    is the match anchored (at beginning-of-line only)?
  1487. X * regmust    string (pointer into program) that match must include, or NULL
  1488. X * regmlen    length of regmust string
  1489. X *
  1490. X * Regstart and reganch permit very fast decisions on suitable starting points
  1491. X * for a match, cutting down the work a lot.  Regmust permits fast rejection
  1492. X * of lines that cannot possibly match.  The regmust tests are costly enough
  1493. X * that regcomp() supplies a regmust only if the r.e. contains something
  1494. X * potentially expensive (at present, the only such thing detected is * or +
  1495. X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
  1496. X * supplied because the test in regexec() needs it and regcomp() is computing
  1497. X * it anyway.
  1498. X */
  1499. X
  1500. X/*
  1501. X * Structure for regexp "program".  This is essentially a linear encoding
  1502. X * of a nondeterministic finite-state machine (aka syntax charts or
  1503. X * "railroad normal form" in parsing technology).  Each node is an opcode
  1504. X * plus a "nxt" pointer, possibly plus an operand.  "Nxt" pointers of
  1505. X * all nodes except BRANCH implement concatenation; a "nxt" pointer with
  1506. X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
  1507. X * have one of the subtle syntax dependencies:  an individual BRANCH (as
  1508. X * opposed to a collection of them) is never concatenated with anything
  1509. X * because of operator precedence.)  The operand of some types of node is
  1510. X * a literal string; for others, it is a node leading into a sub-FSM.  In
  1511. X * particular, the operand of a BRANCH node is the first node of the branch.
  1512. X * (NB this is *not* a tree structure:  the tail of the branch connects
  1513. X * to the thing following the set of BRANCHes.)  The opcodes are:
  1514. X */
  1515. X
  1516. X/* definition    number    opnd?    meaning */
  1517. X#define    END    0        /* no    End of program. */
  1518. X#define    BOL    1        /* no    Match "" at beginning of line. */
  1519. X#define    EOL    2        /* no    Match "" at end of line. */
  1520. X#define    ANY    3        /* no    Match any one character. */
  1521. X#define    ANYOF    4        /* str    Match any character in this string. */
  1522. X#define    ANYBUT    5        /* str    Match any character not in this
  1523. X                 * string. */
  1524. X#define    BRANCH    6        /* node    Match this alternative, or the
  1525. X                 * nxt... */
  1526. X#define    BACK    7        /* no    Match "", "nxt" ptr points backward. */
  1527. X#define    EXACTLY    8        /* str    Match this string. */
  1528. X#define    NOTHING    9        /* no    Match empty string. */
  1529. X#define    STAR    10        /* node    Match this (simple) thing 0 or more
  1530. X                 * times. */
  1531. X#define    OPEN    20        /* no    Mark this point in input as start of
  1532. X                 * #n. */
  1533. X /* OPEN+1 is number 1, etc. */
  1534. X#define    CLOSE    30        /* no    Analogous to OPEN. */
  1535. X
  1536. X/*
  1537. X * Opcode notes:
  1538. X *
  1539. X * BRANCH    The set of branches constituting a single choice are hooked
  1540. X *        together with their "nxt" pointers, since precedence prevents
  1541. X *        anything being concatenated to any individual branch.  The
  1542. X *        "nxt" pointer of the last BRANCH in a choice points to the
  1543. X *        thing following the whole choice.  This is also where the
  1544. X *        final "nxt" pointer of each individual branch points; each
  1545. X *        branch starts with the operand node of a BRANCH node.
  1546. X *
  1547. X * BACK        Normal "nxt" pointers all implicitly point forward; BACK
  1548. X *        exists to make loop structures possible.
  1549. X *
  1550. X * STAR        complex '*', are implemented as circular BRANCH structures 
  1551. X *        using BACK.  Simple cases (one character per match) are 
  1552. X *        implemented with STAR for speed and to minimize recursive 
  1553. X *        plunges.
  1554. X *
  1555. X * OPEN,CLOSE    ...are numbered at compile time.
  1556. X */
  1557. X
  1558. X/*
  1559. X * A node is one char of opcode followed by two chars of "nxt" pointer.
  1560. X * "Nxt" pointers are stored as two 8-bit pieces, high order first.  The
  1561. X * value is a positive offset from the opcode of the node containing it.
  1562. X * An operand, if any, simply follows the node.  (Note that much of the
  1563. X * code generation knows about this implicit relationship.)
  1564. X *
  1565. X * Using two bytes for the "nxt" pointer is vast overkill for most things,
  1566. X * but allows patterns to get big without disasters.
  1567. X */
  1568. X#define    OP(p)    (*(p))
  1569. X#define    NEXT(p)    (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
  1570. X#define    OPERAND(p)    ((p) + 3)
  1571. X
  1572. X/*
  1573. X * Utility definitions.
  1574. X */
  1575. X
  1576. X#define    FAIL(m)    { regerror(m); return(NULL); }
  1577. X#define    ISMULT(c)    ((c) == '*')
  1578. X#define    META    "^$.[()|*\\"
  1579. X#ifndef CHARBITS
  1580. X#define    UCHARAT(p)    ((int)*(unsigned char *)(p))
  1581. X#else
  1582. X#define    UCHARAT(p)    ((int)*(p)&CHARBITS)
  1583. X#endif
  1584. X
  1585. X/*
  1586. X * Flags to be passed up and down.
  1587. X */
  1588. X#define    HASWIDTH    01    /* Known never to match null string. */
  1589. X#define    SIMPLE        02    /* Simple enough to be STAR operand. */
  1590. X#define    SPSTART        04    /* Starts with * */
  1591. X#define    WORST        0    /* Worst case. */
  1592. X
  1593. X/*
  1594. X * Global work variables for regcomp().
  1595. X */
  1596. Xstatic char    *regparse;    /* Input-scan pointer. */
  1597. Xstatic int      regnpar;    /* () count. */
  1598. Xstatic char     regdummy;
  1599. Xstatic char    *regcode;    /* Code-emit pointer; ®dummy = don't. */
  1600. Xstatic long     regsize;    /* Code size. */
  1601. X
  1602. X/*
  1603. X * Forward declarations for regcomp()'s friends.
  1604. X */
  1605. X#ifndef STATIC
  1606. X#define    STATIC    static
  1607. X#endif
  1608. XSTATIC char    *reg();
  1609. XSTATIC char    *regbranch();
  1610. XSTATIC char    *regpiece();
  1611. XSTATIC char    *regatom();
  1612. XSTATIC char    *regnode();
  1613. XSTATIC char    *regnext();
  1614. XSTATIC void     regc();
  1615. XSTATIC void     reginsert();
  1616. XSTATIC void     regtail();
  1617. XSTATIC void     regoptail();
  1618. X#ifdef STRCSPN
  1619. XSTATIC int      strcspn();
  1620. X#endif
  1621. X
  1622. X/*
  1623. X - regcomp - compile a regular expression into internal code
  1624. X *
  1625. X * We can't allocate space until we know how big the compiled form will be,
  1626. X * but we can't compile it (and thus know how big it is) until we've got a
  1627. X * place to put the code.  So we cheat:  we compile it twice, once with code
  1628. X * generation turned off and size counting turned on, and once "for real".
  1629. X * This also means that we don't allocate space until we are sure that the
  1630. X * thing really will compile successfully, and we never have to move the
  1631. X * code and thus invalidate pointers into it.  (Note that it has to be in
  1632. X * one piece because free() must be able to free it all.)
  1633. X *
  1634. X * Beware that the optimization-preparation code in here knows about some
  1635. X * of the structure of the compiled regexp.
  1636. X */
  1637. Xregexp *regcomp(exp)
  1638. Xchar           *exp;
  1639. X{
  1640. X    register regexp *r;
  1641. X    register char  *scan;
  1642. X    register char  *longest;
  1643. X    register int    len;
  1644. X    int             flags;
  1645. X    extern char    *malloc();
  1646. X
  1647. X    if (exp == NULL)
  1648. X    FAIL("NULL argument");
  1649. X
  1650. X    /* First pass: determine size, legality. */
  1651. X    regparse = exp;
  1652. X    regnpar = 1;
  1653. X    regsize = 0L;
  1654. X    regcode = ®dummy;
  1655. X    regc(MAGIC);
  1656. X    if (reg(0, &flags) == NULL)
  1657. X    return (NULL);
  1658. X
  1659. X    /* Small enough for pointer-storage convention? */
  1660. X    if (regsize >= 32767L)    /* Probably could be 65535L. */
  1661. X    FAIL("regexp too big");
  1662. X
  1663. X    /* Allocate space. */
  1664. X    r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize);
  1665. X    if (r == NULL)
  1666. X    FAIL("out of space");
  1667. X
  1668. X    /* Second pass: emit code. */
  1669. X    regparse = exp;
  1670. X    regnpar = 1;
  1671. X    regcode = r->program;
  1672. X    regc(MAGIC);
  1673. X    if (reg(0, &flags) == NULL)
  1674. X    return (NULL);
  1675. X
  1676. X    /* Dig out information for optimizations. */
  1677. X    r->regstart = '\0';        /* Worst-case defaults. */
  1678. X    r->reganch = 0;
  1679. X    r->regmust = NULL;
  1680. X    r->regmlen = 0;
  1681. X    scan = r->program + 1;    /* First BRANCH. */
  1682. X    if (OP(regnext(scan)) == END) {    /* Only one top-level choice. */
  1683. X    scan = OPERAND(scan);
  1684. X
  1685. X    /* Starting-point info. */
  1686. X    if (OP(scan) == EXACTLY)
  1687. X        r->regstart = *OPERAND(scan);
  1688. X    else if (OP(scan) == BOL)
  1689. X        r->reganch++;
  1690. X
  1691. X    /*
  1692. X     * If there's something expensive in the r.e., find the longest
  1693. X     * literal string that must appear and make it the regmust.  Resolve
  1694. X     * ties in favor of later strings, since the regstart check works
  1695. X     * with the beginning of the r.e. and avoiding duplication
  1696. X     * strengthens checking.  Not a strong reason, but sufficient in the
  1697. X     * absence of others. 
  1698. X     */
  1699. X    if (flags & SPSTART) {
  1700. X        longest = NULL;
  1701. X        len = 0;
  1702. X        for (; scan != NULL; scan = regnext(scan))
  1703. X        if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
  1704. X            longest = OPERAND(scan);
  1705. X            len = strlen(OPERAND(scan));
  1706. X        }
  1707. X        r->regmust = longest;
  1708. X        r->regmlen = len;
  1709. X    }
  1710. X    }
  1711. X    return (r);
  1712. X}
  1713. X
  1714. X/*
  1715. X - reg - regular expression, i.e. main body or parenthesized thing
  1716. X *
  1717. X * Caller must absorb opening parenthesis.
  1718. X *
  1719. X * Combining parenthesis handling with the base level of regular expression
  1720. X * is a trifle forced, but the need to tie the tails of the branches to what
  1721. X * follows makes it hard to avoid.
  1722. X */
  1723. Xstatic char *reg(paren, flagp)
  1724. Xint             paren;        /* Parenthesized? */
  1725. Xint            *flagp;
  1726. X{
  1727. X    register char  *ret;
  1728. X    register char  *br;
  1729. X    register char  *ender;
  1730. X    register int    parno;
  1731. X    int             flags;
  1732. X
  1733. X    *flagp = HASWIDTH;        /* Tentatively. */
  1734. X
  1735. X    /* Make an OPEN node, if parenthesized. */
  1736. X    if (paren) {
  1737. X    if (regnpar >= NSUBEXP)
  1738. X        FAIL("too many ()");
  1739. X    parno = regnpar;
  1740. X    regnpar++;
  1741. X    ret = regnode(OPEN + parno);
  1742. X    } else
  1743. X    ret = NULL;
  1744. X
  1745. X    /* Pick up the branches, linking them together. */
  1746. X    br = regbranch(&flags);
  1747. X    if (br == NULL)
  1748. X    return (NULL);
  1749. X    if (ret != NULL)
  1750. X    regtail(ret, br);    /* OPEN -> first. */
  1751. X    else
  1752. X    ret = br;
  1753. X    if (!(flags & HASWIDTH))
  1754. X    *flagp &= ~HASWIDTH;
  1755. X    *flagp |= flags & SPSTART;
  1756. X    while (*regparse == '|') {
  1757. X    regparse++;
  1758. X    br = regbranch(&flags);
  1759. X    if (br == NULL)
  1760. X        return (NULL);
  1761. X    regtail(ret, br);    /* BRANCH -> BRANCH. */
  1762. X    if (!(flags & HASWIDTH))
  1763. X        *flagp &= ~HASWIDTH;
  1764. X    *flagp |= flags & SPSTART;
  1765. X    }
  1766. X
  1767. X    /* Make a closing node, and hook it on the end. */
  1768. X    ender = regnode((paren) ? CLOSE + parno : END);
  1769. X    regtail(ret, ender);
  1770. X
  1771. X    /* Hook the tails of the branches to the closing node. */
  1772. X    for (br = ret; br != NULL; br = regnext(br))
  1773. X    regoptail(br, ender);
  1774. X
  1775. X    /* Check for proper termination. */
  1776. X    if (paren && *regparse++ != ')') {
  1777. X    FAIL("unmatched ()");
  1778. X    } else if (!paren && *regparse != '\0') {
  1779. X    if (*regparse == ')') {
  1780. X        FAIL("unmatched ()");
  1781. X    } else
  1782. X        FAIL("junk on end");/* "Can't happen". */
  1783. X    /* NOTREACHED */
  1784. X    }
  1785. X    return (ret);
  1786. X}
  1787. X
  1788. X/*
  1789. X - regbranch - one alternative of an | operator
  1790. X *
  1791. X * Implements the concatenation operator.
  1792. X */
  1793. Xstatic char  *regbranch(flagp)
  1794. Xint            *flagp;
  1795. X{
  1796. X    register char  *ret;
  1797. X    register char  *chain;
  1798. X    register char  *latest;
  1799. X    int             flags;
  1800. X
  1801. X    *flagp = WORST;        /* Tentatively. */
  1802. X
  1803. X    ret = regnode(BRANCH);
  1804. X    chain = NULL;
  1805. X    while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
  1806. X    latest = regpiece(&flags);
  1807. X    if (latest == NULL)
  1808. X        return (NULL);
  1809. X    *flagp |= flags & HASWIDTH;
  1810. X    if (chain == NULL)    /* First piece. */
  1811. X        *flagp |= flags & SPSTART;
  1812. X    else
  1813. X        regtail(chain, latest);
  1814. X    chain = latest;
  1815. X    }
  1816. X    if (chain == NULL)        /* Loop ran zero times. */
  1817. X    regnode(NOTHING);
  1818. X
  1819. X    return (ret);
  1820. X}
  1821. X
  1822. X/*
  1823. X - regpiece - something followed by possible [*]
  1824. X *
  1825. X * Note that the branching code sequence used for * is somewhat optimized:  
  1826. X * they use the same NOTHING node as both the endmarker for their branch 
  1827. X * list and the body of the last branch.  It might seem that this node could 
  1828. X * be dispensed with entirely, but the endmarker role is not redundant.
  1829. X */
  1830. Xstatic char *regpiece(flagp)
  1831. Xint            *flagp;
  1832. X{
  1833. X    register char  *ret;
  1834. X    register char   op;
  1835. X    register char  *nxt;
  1836. X    int             flags;
  1837. X
  1838. X    ret = regatom(&flags);
  1839. X    if (ret == NULL)
  1840. X    return (NULL);
  1841. X
  1842. X    op = *regparse;
  1843. X    if (!ISMULT(op)) {
  1844. X    *flagp = flags;
  1845. X    return (ret);
  1846. X    }
  1847. X    if (!(flags & HASWIDTH))
  1848. X    FAIL("* operand could be empty");
  1849. X    *flagp = (WORST | SPSTART);
  1850. X
  1851. X    if (op == '*' && (flags & SIMPLE))
  1852. X    reginsert(STAR, ret);
  1853. X    else if (op == '*') {
  1854. X    /* Emit x* as (x&|), where & means "self". */
  1855. X    reginsert(BRANCH, ret);    /* Either x */
  1856. X    regoptail(ret, regnode(BACK));    /* and loop */
  1857. X    regoptail(ret, ret);    /* back */
  1858. X    regtail(ret, regnode(BRANCH));    /* or */
  1859. X    regtail(ret, regnode(NOTHING));    /* null. */
  1860. X    } 
  1861. X    regparse++;
  1862. X    if (ISMULT(*regparse))
  1863. X    FAIL("nested *");
  1864. X
  1865. X    return (ret);
  1866. X}
  1867. X
  1868. X/*
  1869. X - regatom - the lowest level
  1870. X *
  1871. X * Optimization:  gobbles an entire sequence of ordinary characters so that
  1872. X * it can turn them into a single node, which is smaller to store and
  1873. X * faster to run.  Backslashed characters are exceptions, each becoming a
  1874. X * separate node; the code is simpler that way and it's not worth fixing.
  1875. X */
  1876. Xstatic char *regatom(flagp)
  1877. Xint            *flagp;
  1878. X{
  1879. X    register char  *ret;
  1880. X    int             flags;
  1881. X
  1882. X    *flagp = WORST;        /* Tentatively. */
  1883. X
  1884. X    switch (*regparse++) {
  1885. X    case '^':
  1886. X    ret = regnode(BOL);
  1887. X    break;
  1888. X    case '$':
  1889. X    ret = regnode(EOL);
  1890. X    break;
  1891. X    case '.':
  1892. X    ret = regnode(ANY);
  1893. X    *flagp |= HASWIDTH | SIMPLE;
  1894. X    break;
  1895. X    case '[':{
  1896. X        register int    class;
  1897. X        register int    classend;
  1898. X
  1899. X        if (*regparse == '^') {    /* Complement of range. */
  1900. X        ret = regnode(ANYBUT);
  1901. X        regparse++;
  1902. X        } else
  1903. X        ret = regnode(ANYOF);
  1904. X        if (*regparse == ']' || *regparse == '-')
  1905. X        regc(*regparse++);
  1906. X        while (*regparse != '\0' && *regparse != ']') {
  1907. X        if (*regparse == '-') {
  1908. X            regparse++;
  1909. X            if (*regparse == ']' || *regparse == '\0')
  1910. X            regc('-');
  1911. X            else {
  1912. X            class = UCHARAT(regparse - 2) + 1;
  1913. X            classend = UCHARAT(regparse);
  1914. X            if (class > classend + 1)
  1915. X                FAIL("invalid [] range");
  1916. X            for (; class <= classend; class++)
  1917. X                regc(class);
  1918. X            regparse++;
  1919. X            }
  1920. X        } else
  1921. X            regc(*regparse++);
  1922. X        }
  1923. X        regc('\0');
  1924. X        if (*regparse != ']')
  1925. X        FAIL("unmatched []");
  1926. X        regparse++;
  1927. X        *flagp |= HASWIDTH | SIMPLE;
  1928. X    }
  1929. X    break;
  1930. X    case '(':
  1931. X    ret = reg(1, &flags);
  1932. X    if (ret == NULL)
  1933. X        return (NULL);
  1934. X    *flagp |= flags & (HASWIDTH | SPSTART);
  1935. X    break;
  1936. X    case '\0':
  1937. X    case '|':
  1938. X    case ')':
  1939. X    FAIL("internal urp");    /* Supposed to be caught earlier. */
  1940. X    break;
  1941. X    case '*':
  1942. X    FAIL("* follows nothing");
  1943. X    break;
  1944. X    case '\\':
  1945. X    if (*regparse == '\0')
  1946. X        FAIL("trailing \\");
  1947. X    ret = regnode(EXACTLY);
  1948. X    regc(*regparse++);
  1949. X    regc('\0');
  1950. X    *flagp |= HASWIDTH | SIMPLE;
  1951. X    break;
  1952. X    default:{
  1953. X        register int    len;
  1954. X        register char   ender;
  1955. X
  1956. X        regparse--;
  1957. X        len = strcspn(regparse, META);
  1958. X        if (len <= 0)
  1959. X        FAIL("internal disaster");
  1960. X        ender = *(regparse + len);
  1961. X        if (len > 1 && ISMULT(ender))
  1962. X        len--;        /* Back off clear of * operand. */
  1963. X        *flagp |= HASWIDTH;
  1964. X        if (len == 1)
  1965. X        *flagp |= SIMPLE;
  1966. X        ret = regnode(EXACTLY);
  1967. X        while (len > 0) {
  1968. X        regc(*regparse++);
  1969. X        len--;
  1970. X        }
  1971. X        regc('\0');
  1972. X    }
  1973. X    break;
  1974. X    }
  1975. X
  1976. X    return (ret);
  1977. X}
  1978. X
  1979. X/*
  1980. X - regnode - emit a node
  1981. X */
  1982. Xstatic char *regnode(op)
  1983. Xchar            op;
  1984. X{
  1985. X    register char  *ret;
  1986. X    register char  *ptr;
  1987. X
  1988. X    ret = regcode;
  1989. X    if (ret == ®dummy) {
  1990. X    regsize += 3;
  1991. X    return (ret);
  1992. X    }
  1993. X    ptr = ret;
  1994. X    *ptr++ = op;
  1995. X    *ptr++ = '\0';        /* Null "nxt" pointer. */
  1996. X    *ptr++ = '\0';
  1997. X    regcode = ptr;
  1998. X
  1999. X    return (ret);
  2000. X}
  2001. X
  2002. X/*
  2003. X - regc - emit (if appropriate) a byte of code
  2004. X */
  2005. Xstatic void regc(b)
  2006. Xchar            b;
  2007. X{
  2008. X    if (regcode != ®dummy)
  2009. X    *regcode++ = b;
  2010. X    else
  2011. X    regsize++;
  2012. X}
  2013. X
  2014. X/*
  2015. X - reginsert - insert an operator in front of already-emitted operand
  2016. X *
  2017. X * Means relocating the operand.
  2018. X */
  2019. Xstatic void reginsert(op, opnd)
  2020. Xchar            op;
  2021. Xchar           *opnd;
  2022. X{
  2023. X    register char  *src;
  2024. X    register char  *dst;
  2025. X    register char  *place;
  2026. X
  2027. X    if (regcode == ®dummy) {
  2028. X    regsize += 3;
  2029. X    return;
  2030. X    }
  2031. X    src = regcode;
  2032. X    regcode += 3;
  2033. X    dst = regcode;
  2034. X    while (src > opnd)
  2035. X    *--dst = *--src;
  2036. X
  2037. X    place = opnd;        /* Op node, where operand used to be. */
  2038. X    *place++ = op;
  2039. X    *place++ = '\0';
  2040. X    *place++ = '\0';
  2041. X}
  2042. X
  2043. X/*
  2044. X - regtail - set the next-pointer at the end of a node chain
  2045. X */
  2046. Xstatic void regtail(p, val)
  2047. Xchar           *p;
  2048. Xchar           *val;
  2049. X{
  2050. X    register char  *scan;
  2051. X    register char  *temp;
  2052. X    register int    offset;
  2053. X
  2054. X    if (p == ®dummy)
  2055. X    return;
  2056. X
  2057. X    /* Find last node. */
  2058. X    scan = p;
  2059. X    for (;;) {
  2060. X    temp = regnext(scan);
  2061. X    if (temp == NULL)
  2062. X        break;
  2063. X    scan = temp;
  2064. X    }
  2065. X
  2066. X    if (OP(scan) == BACK)
  2067. X    offset = scan - val;
  2068. X    else
  2069. X    offset = val - scan;
  2070. X    *(scan + 1) = (offset >> 8) & 0377;
  2071. X    *(scan + 2) = offset & 0377;
  2072. X}
  2073. X
  2074. X/*
  2075. X - regoptail - regtail on operand of first argument; nop if operandless
  2076. NO_NEWS_IS_GOOD_NEWS
  2077. echo "End of part 12"
  2078. echo "File regexp.c is continued in part 13"
  2079. echo "13" > s2_seq_.tmp
  2080. exit 0
  2081. ---
  2082. Kim F. Storm        storm@texas.dk        Tel +45 429 174 00
  2083. Texas Instruments, Marielundvej 46E, DK-2730 Herlev, Denmark
  2084.       No news is good news, but nn is better!
  2085.  
  2086.