home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / articles.c < prev    next >
C/C++ Source or Header  |  1996-08-04  |  15KB  |  640 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Basic article access and management
  5.  */
  6.  
  7. #include "config.h"
  8. #include "db.h"
  9. #include "articles.h"
  10. #include "regexp.h"
  11. #ifdef ART_GREP
  12. #include "news.h"
  13. #endif /* ART_GREP */
  14.  
  15. /* articles.c */
  16.  
  17. static void new_thunk __APROTO((thunk *t, char *ptr, long size));
  18. static int grep_article __APROTO((group_header *gh, article_header *ah, char *pattern, int (*fcn)()));
  19.  
  20.  
  21. export int  seq_cross_filtering = 1;
  22. export int  select_leave_next = 0;     /* ask to select left over art. */
  23. export int  ignore_re = 0;
  24. export int  body_search_header = 0;
  25.  
  26. import int  ignore_fancy_select;
  27. import int  killed_articles;
  28.  
  29. extern attr_type test_article();
  30.  
  31. /*
  32.  * memory management
  33.  */
  34.  
  35. static thunk
  36.     dummy_str_t = {
  37.     NULL,
  38.     NULL,
  39.     0L
  40.     },
  41.     dummy_art_t = {
  42.     NULL,
  43.     NULL,
  44.     0L
  45.     };
  46.  
  47.  
  48. static thunk *first_str_t = &dummy_str_t;
  49. static thunk *current_str_t = &dummy_str_t;
  50. static thunk *first_art_t = &dummy_art_t;
  51. static thunk *current_art_t = &dummy_art_t;
  52. static long  cur_str_size = 0, cur_art_size = 0;
  53. static char *next_str;
  54. static article_header *next_art;
  55. static article_header **art_array = NULL;
  56.  
  57. static article_number max_articles = 0, mem_offset = 0;
  58.  
  59. /*
  60.  * allocate one article header
  61.  */
  62.  
  63. #ifndef ART_THUNK_SIZE
  64. #define ART_THUNK_SIZE    127
  65. #endif
  66.  
  67. static void
  68. new_thunk(t, ptr, size)
  69. thunk *t;
  70. char *ptr;
  71. long size;
  72. {
  73.     thunk *new;
  74.  
  75.     new = newobj(thunk, 1);
  76.  
  77.     new->next_thunk = t->next_thunk;
  78.     t->next_thunk = new;
  79.  
  80.     new->this_thunk = ptr;
  81.     new->thunk_size = size;
  82. }
  83.  
  84.  
  85. article_header *alloc_art()
  86. {
  87.     if (cur_art_size == 0) {
  88.     if (current_art_t->next_thunk == NULL)
  89.         new_thunk(current_art_t,
  90.               (char *)newobj(article_header, ART_THUNK_SIZE),
  91.               (long)ART_THUNK_SIZE);
  92.  
  93.     current_art_t = current_art_t->next_thunk;
  94.     next_art = (article_header *)current_art_t->this_thunk;
  95.     cur_art_size = current_art_t->thunk_size;
  96.     }
  97.  
  98.     cur_art_size--;
  99.     return next_art++;
  100. }
  101.  
  102. /*
  103.  * allocate a string of length 'len'
  104.  */
  105.  
  106. #ifndef STR_THUNK_SIZE
  107. #define STR_THUNK_SIZE    ((1<<14) - 32)    /* leave room for malloc header */
  108. #endif
  109.  
  110. char *alloc_str(len)
  111. int len;
  112. {
  113.     /* allow space for '\0', and align on word boundary */
  114.     int size  = (len + sizeof(int)) & ~(sizeof(int) - 1);
  115.     char *ret;
  116.  
  117.     if (cur_str_size < size) {
  118.     if (current_str_t->next_thunk == NULL)
  119.         new_thunk(current_str_t,
  120.               newstr(STR_THUNK_SIZE), (long)STR_THUNK_SIZE);
  121.  
  122.     current_str_t = current_str_t->next_thunk;
  123.     next_str = current_str_t->this_thunk;
  124.     cur_str_size = current_str_t->thunk_size;
  125.     }
  126.     /* XXX else should do something reasonable */
  127.  
  128.     ret = next_str;
  129.     cur_str_size -= size;
  130.     next_str += size;
  131.  
  132.     ret[len] = NUL;    /* ensure string is null terminated */
  133.     return ret;
  134. }
  135.  
  136. /*
  137.  * "free" the allocated memory
  138.  */
  139.  
  140. void
  141. free_memory()
  142. {
  143.     current_str_t = first_str_t;
  144.     current_art_t = first_art_t;
  145.     cur_str_size  = 0;
  146.     cur_art_size  = 0;
  147.     n_articles      = 0;
  148. }
  149.  
  150.  
  151. /*
  152.  * mark/release memory
  153.  */
  154.  
  155.  
  156. void
  157. mark_str(str_marker)
  158. string_marker *str_marker;
  159. {
  160.     str_marker->sm_cur_t = current_str_t;
  161.     str_marker->sm_size  = cur_str_size;
  162.     str_marker->sm_next  = next_str;
  163. }
  164.  
  165. void
  166. release_str(str_marker)
  167. string_marker *str_marker;
  168. {
  169.     current_str_t = str_marker->sm_cur_t;
  170.     cur_str_size  = str_marker->sm_size;
  171.     next_str      = str_marker->sm_next;
  172. }
  173.  
  174.  
  175. void
  176. mark_memory(mem_marker)
  177. memory_marker *mem_marker;
  178. {
  179.     mark_str(&(mem_marker->mm_string));
  180.  
  181.     mem_marker->mm_cur_t = current_art_t;
  182.     mem_marker->mm_size  = cur_art_size;
  183.     mem_marker->mm_next  = next_art;
  184.  
  185.     mem_marker->mm_nart     = n_articles;
  186.     mem_offset += n_articles;
  187.  
  188.     n_articles = 0;
  189.     articles = art_array + mem_offset;
  190. }
  191.  
  192. void
  193. release_memory(mem_marker)
  194. memory_marker *mem_marker;
  195. {
  196.     release_str(&(mem_marker->mm_string));
  197.  
  198.     current_art_t = mem_marker->mm_cur_t;
  199.     cur_art_size  = mem_marker->mm_size;
  200.     next_art      = mem_marker->mm_next;
  201.  
  202.     n_articles = mem_marker->mm_nart;
  203.  
  204.     mem_offset -= n_articles;
  205.     articles = art_array + mem_offset;
  206. }
  207.  
  208. /*
  209.  * merge all memory chunks into one.
  210.  */
  211.  
  212. void 
  213. merge_memory()
  214. {
  215.     n_articles += mem_offset;
  216.     mem_offset = 0;
  217.     articles = art_array;
  218. }
  219.  
  220.  
  221. /*
  222.  * save article header in 'articles' array
  223.  * 'articles' is enlarged if too small
  224.  */
  225.  
  226. #define    FIRST_ART_ARRAY_SIZE    500    /* malloc header */
  227. #define    NEXT_ART_ARRAY_SIZE    512
  228.  
  229. void
  230. add_article(art)
  231. article_header *art;
  232. {
  233.     if ((n_articles + mem_offset) == max_articles) {
  234.     /* must increase size of 'articles' */
  235.  
  236.     if (max_articles == 0) {
  237.         /* allocate initial 'articles' array */
  238.         max_articles = FIRST_ART_ARRAY_SIZE;
  239.     } else {
  240.         max_articles += NEXT_ART_ARRAY_SIZE;
  241.     }
  242.     art_array = resizeobj(art_array, article_header *, max_articles);
  243.     articles = art_array + mem_offset;
  244.     }
  245.  
  246.     articles[n_articles] = art;
  247.     n_articles++;
  248. }
  249.  
  250.  
  251. int
  252. access_group(gh, first_article, last_article, flags, mask)
  253. register group_header *gh;
  254. article_number first_article, last_article;
  255. register flag_type flags;
  256. char *mask;
  257. {
  258.     register article_header *ah;
  259.     int skip_digest, n;
  260.     group_header *cpgh;
  261. #ifdef NOV
  262.     int dbstatus;
  263. #else /* NOV */
  264.     FILE *data;
  265.     off_t data_offset, data_size;
  266. #endif /* NOV */
  267.     cross_post_number cross_post;
  268.     attr_type leave_attr;
  269.     memory_marker mem_marker;
  270.     static regexp *rexp = NULL;
  271.     static char subptext[80];
  272.  
  273.     if (first_article < gh->first_db_article)
  274.     first_article = gh->first_db_article;
  275.  
  276.     if (last_article > gh->last_db_article)
  277.     last_article = gh->last_db_article;
  278.  
  279.     if (last_article == 0 || first_article > last_article) return 0;
  280.  
  281. #ifdef NOV
  282.     /*
  283.      * Some trickery, since we normally avoid reading all of the
  284.      * XOVER or .overview data:
  285.      */
  286.     if (flags & ACC_ALSO_READ_ARTICLES)
  287.     first_article = gh->first_a_article;
  288.     db_read_group(gh, first_article, last_article);
  289. #else
  290.     data = open_data_file(gh, 'd', OPEN_READ);
  291.     if (data == NULL) return -10;
  292.  
  293.     if ((data_offset = get_data_offset(gh, first_article)) == (off_t)(-1))
  294.     return -11;
  295. #endif /* NOV */
  296.  
  297.     if (mask == NULL) {
  298.     if (rexp != NULL) {
  299.         freeobj(rexp);
  300.         rexp = NULL;
  301.     }
  302.     } else {
  303.     if (*mask == '/') {
  304.         mask++;
  305.         if (rexp != NULL) {
  306.         if (strncmp(mask, subptext, 80) != 0) {
  307.             freeobj(rexp);
  308.             rexp = NULL;
  309.         }
  310.         }
  311.         if (rexp == NULL) {
  312.         strncpy(subptext, mask, 80);
  313.         rexp = regcomp(mask);
  314.         if (rexp == NULL) return -1;
  315.         }
  316.         /* notice mask is still non-NULL */
  317.     }
  318.     }
  319.  
  320.     if ((flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
  321.     leave_attr = 0;
  322.     else if (select_leave_next)
  323.     leave_attr = A_READ;    /* will prompt */
  324.     else
  325.     leave_attr = A_LEAVE_NEXT;
  326.  
  327.     if (!(flags & ACC_SPEW_MODE))
  328.     use_newsrc(gh, (flags & ACC_MERGED_NEWSRC) ? 2 :
  329.                (flags & ACC_ORIG_NEWSRC) ? 1 : 0);
  330.  
  331.     if ((flags & ACC_EXTRA_ARTICLES) == 0)
  332.     mark_memory(&mem_marker);
  333.  
  334.     ah = alloc_art();
  335.  
  336.     skip_digest = 0;
  337.  
  338. #ifdef NOV
  339.     /* XXX: db_read_art takes a FILE * in nnmaster version */
  340.     while ((dbstatus = db_read_art((FILE *) -1)) > 0) {
  341. #ifdef ART_GREP
  342.     if (s_keyboard || s_hangup) break; /* add to allow interrupt of GREP */
  343. #endif
  344.         if (db_hdr.dh_number < first_article)
  345.         continue;
  346. #else
  347.     fseek(data, data_offset, 0);
  348.  
  349.     while (data_offset < gh->data_write_offset) {
  350. #ifdef ART_GREP
  351.     if (s_keyboard || s_hangup) break; /* add to allow interrupt of GREP */
  352. #endif
  353.     data_size = db_read_art(data);
  354.     if (data_size <= 0) {
  355.         fclose(data);
  356.         if ((flags & ACC_EXTRA_ARTICLES) == 0)
  357.         release_memory(&mem_marker);
  358.         return -2;
  359.     }
  360.     data_offset += data_size;
  361. #endif /* NOV */
  362.     if (db_hdr.dh_lpos == (off_t)0)
  363.         continue;    /* article not accessible */
  364.  
  365.     if (db_hdr.dh_number > gh->last_db_article
  366.         || db_hdr.dh_number < gh->first_db_article)
  367.         goto data_error;
  368.  
  369.     if (skip_digest && db_data.dh_type == DH_SUB_DIGEST)
  370.         continue;
  371.  
  372.     skip_digest = 0;
  373.  
  374.     if (db_hdr.dh_cross_postings && !(flags & ACC_ALSO_CROSS_POSTINGS)) {
  375.         for (n = 0; n < (int)db_hdr.dh_cross_postings; n++) {
  376.         cross_post = NETW_CROSS_INT(db_data.dh_cross[n]);
  377.         if (cross_post < 0 || cross_post >= master.number_of_groups)
  378.             continue;
  379.         cpgh = ACTIVE_GROUP(cross_post);
  380.         if (cpgh == gh) {
  381.             if (seq_cross_filtering) continue;
  382.             n = db_hdr.dh_cross_postings;
  383.             break;
  384.         }
  385.         if (!(flags & ACC_ALSO_UNSUB_GROUPS))
  386.             if (cpgh->group_flag & G_UNSUBSCRIBED) continue;
  387.  
  388.         if (!seq_cross_filtering) break;
  389.         if (cpgh->preseq_index > 0 &&
  390.             cpgh->preseq_index < gh->preseq_index) break;
  391.         }
  392.  
  393.         if (n < (int)db_hdr.dh_cross_postings) {
  394.         if (db_data.dh_type == DH_DIGEST_HEADER) skip_digest++;
  395.         continue;
  396.         }
  397.     }
  398.  
  399.     ah->flag = 0;
  400.  
  401.     switch (db_data.dh_type) {
  402.      case DH_DIGEST_HEADER:
  403.         if (flags & ACC_DONT_SPLIT_DIGESTS)
  404.         skip_digest++;
  405.         else
  406.         if ((flags & ACC_ALSO_FULL_DIGEST) == 0)
  407.             continue;    /* don't want the full digest when split */
  408.         ah->flag |= A_FULL_DIGEST;
  409.         break;
  410.      case DH_SUB_DIGEST:
  411.         ah->flag |= A_DIGEST;
  412.         break;
  413.     }
  414.  
  415.     ah->a_number = db_hdr.dh_number;
  416.     if (ah->a_number > last_article) break;
  417.  
  418.     if (flags & ACC_SPEW_MODE) {
  419.         fold_string(db_data.dh_subject);
  420.         tprintf("%x:%s\n", (int)(gh->group_num), db_data.dh_subject);
  421.         continue;
  422.     }
  423.  
  424.     ah->hpos = db_hdr.dh_hpos;
  425.     ah->fpos = ah->hpos + (off_t)(db_hdr.dh_fpos);
  426.     ah->lpos = db_hdr.dh_lpos;
  427.  
  428.     ah->attr = test_article(ah);
  429.  
  430.     if (flags & ACC_MERGED_NEWSRC) {
  431.         if (ah->attr == A_KILL) continue;
  432.     } else if (ah->attr != A_READ && (flags & ACC_ONLY_READ_ARTICLES))
  433.         continue;
  434.  
  435. #ifdef ART_GREP
  436.     if((flags & ACC_ON_GREP_UNREAD) && (ah->attr == A_READ))
  437.         continue;
  438. #endif /* ART_GREP */
  439.  
  440.     if (rexp != NULL) {
  441.         if (flags & ACC_ON_SUBJECT)
  442.         if (regexec_cf(rexp, db_data.dh_subject)) goto match_ok;
  443.         if (flags & ACC_ON_SENDER)
  444.         if (regexec_cf(rexp, db_data.dh_sender)) goto match_ok;
  445. #ifdef ART_GREP
  446.         if (flags & (ACC_ON_GREP_UNREAD | ACC_ON_GREP_ALL))
  447.         if (grep_article(gh, ah, (char *)rexp, regexec_cf)) goto match_ok;
  448. #endif /* ART_GREP */
  449.         continue;
  450.     } else
  451.     if (mask != NULL) {
  452.         if (flags & ACC_ON_SUBJECT)
  453.         if (strmatch_cf(mask, db_data.dh_subject)) goto match_ok;
  454.         if (flags & ACC_ON_SENDER)
  455.         if (strmatch_cf(mask, db_data.dh_sender)) goto match_ok;
  456. #ifdef ART_GREP
  457.         if (flags & (ACC_ON_GREP_UNREAD | ACC_ON_GREP_ALL))
  458.         if (grep_article(gh, ah, mask, strmatch_cf)) goto match_ok;
  459. #endif /* ART_GREP */
  460.         continue;
  461.     }
  462.      match_ok:
  463.  
  464.      attr_again:
  465.  
  466.     switch (ah->attr) {
  467.      case A_LEAVE:
  468.         if (mask) {
  469.         ah->attr = 0;
  470.         break;
  471.         }
  472.  
  473.         if (leave_attr == A_READ) {
  474.         clrdisp();
  475.         prompt("Select left over articles in group %s? ", gh->group_name);
  476.         leave_attr = yes(0) > 0 ? A_SELECT : A_LEAVE_NEXT;
  477.         }
  478.         ah->attr = leave_attr;
  479.         goto attr_again;
  480.  
  481.      case A_SELECT:
  482.         if (mask) ah->attr = 0;
  483.         break;
  484.  
  485.      case A_READ:
  486.         if (flags & ACC_MERGED_NEWSRC) break;
  487.  
  488.         if (!(flags & (ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES)))
  489.         if (ah->a_number > gh->last_article)
  490.             continue;
  491.  
  492.         /* FALL THRU */
  493.      case A_SEEN:
  494.      case 0:
  495.         if (flags & ACC_DO_KILL) {
  496.         ah->replies = db_hdr.dh_replies;
  497.         ah->sender = db_data.dh_sender;
  498.         ah->subject = db_data.dh_subject;
  499.         if (kill_article(ah)) continue;
  500.         }
  501.  
  502.         /* The 'P' command to a read group must show articles as read */
  503.              if (gh->unread_count <= 0)
  504.                ah->attr = A_READ;
  505.        break;
  506.  
  507.      default:
  508.         break;
  509.     }
  510.  
  511.     if (db_hdr.dh_sender_length) {
  512.         ah->name_length = db_hdr.dh_sender_length;
  513.         ah->sender = alloc_str((int)db_hdr.dh_sender_length);
  514.         strcpy(ah->sender, db_data.dh_sender);
  515.     } else
  516.         ah->sender = "";
  517.  
  518.     if (db_hdr.dh_subject_length) {
  519.         ah->subj_length = db_hdr.dh_subject_length;
  520.         ah->subject = alloc_str((int)db_hdr.dh_subject_length);
  521.         strcpy(ah->subject, db_data.dh_subject);
  522.     } else
  523.         ah->subject = "";
  524.  
  525.     ah->replies = db_hdr.dh_replies;
  526.     ah->lines = db_hdr.dh_lines;
  527.     ah->t_stamp = db_hdr.dh_date;
  528.  
  529.     ah->a_group = (flags & ACC_MERGED_MENU) ? current_group : NULL;
  530.  
  531.     add_article(ah);
  532.     ah = alloc_art();
  533.     }
  534.  
  535. #ifdef NOV
  536.     if (dbstatus < 0) {        /* Error reading DB? */
  537.         if ((flags & ACC_EXTRA_ARTICLES) == 0)
  538.         release_memory(&mem_marker);
  539.     return -2;
  540.     }
  541.     /* else EOF reading DB */
  542. #else
  543.     fclose(data);
  544. #endif /* NOV */
  545.     if ((flags & ACC_DONT_SORT_ARTICLES) == 0)
  546.     sort_articles(-1);
  547.     else
  548.     no_sort_articles();
  549.  
  550.     if (ignore_re && !ignore_fancy_select && !(flags & ACC_ALSO_READ_ARTICLES)) {
  551.     int nexta, roota, killa, newa;
  552.         for (nexta = killa = newa = roota = 0; nexta < n_articles; nexta++) {
  553.             ah = articles[nexta];
  554.             if (ah->flag & A_ROOT_ART) roota = ah->replies & 127 ;
  555.             if (roota && ah->replies && !auto_select_article(ah,1)) killa++;
  556.             else articles[newa++] = ah;
  557.         }
  558.         n_articles -= killa;  
  559.         killed_articles += killa;
  560.     }
  561.  
  562.     return n_articles > 0 ? 1 : 0;
  563.  
  564. data_error:
  565.     log_entry('E', "%s: data inconsistency", gh->group_name);
  566. #ifndef NOV
  567.     fclose(data);
  568. #endif /* NOV */
  569.     if ((flags & ACC_EXTRA_ARTICLES) == 0)
  570.     release_memory(&mem_marker);
  571.     return -12;
  572. }
  573.  
  574. #ifdef ART_GREP
  575.  
  576. /* GREP ARTICLE -- search the article for the pattern using the specified function
  577.    Return 1 if pattern occurs in the article else 0
  578.    Also return 0 if any malloc or file open or read error occurs
  579.    Global data_header db_hdr is set by caller
  580. todo:
  581.    doesn't work for folders (hangs)
  582. */
  583.  
  584. static int
  585. grep_article(gh, ah, pattern, fcn)
  586.   group_header *gh;
  587.   article_header *ah;
  588.   char *pattern;
  589.   int (*fcn)();
  590. {
  591.     char *line, *end;
  592.     FILE *art;
  593.     news_header_buffer buffer1, buffer2;
  594.     static char *buf;
  595.     static int bufsize, count;
  596.     int size;
  597.  
  598.     count++;
  599.     art = open_news_article(ah, FILL_OFFSETS |
  600.         (body_search_header ? 0 : SKIP_HEADER), buffer1, buffer2);
  601.     if(!art) {
  602.       /*msg("Cannot open article");*/
  603.       return 0;
  604.     }
  605.     size = ah->lpos - ftell(art) + 1;
  606.     if(bufsize < size) {
  607.       if(buf)
  608.         free(buf);
  609.       buf = (char *)malloc(size + 10);
  610.       if(!buf) {
  611.         msg("Cannot malloc %d bytes", size);
  612.         bufsize = 0;
  613.         return 0;
  614.       }
  615.       bufsize = size;
  616.     }
  617.     if(fread(buf, size-1, 1, art) != 1) {
  618.       fclose(art);
  619.       /*msg("Cannot read article");*/
  620.       return 0;
  621.     }
  622.     fclose(art);
  623.     /* print status message every so often */
  624.     if(!(count % 27))
  625.       msg("Searching %d of %d", 
  626.         ah->a_number - gh->first_db_article, 
  627.         gh->last_db_article - gh->first_db_article );
  628.  
  629.     buf[size] = 0; /* make buf a giant string so strchr below terminates */
  630.     line = buf;
  631.     while((end = strchr(line, '\n'))) {
  632.       *end++ = 0; /* make the line a nul terminated string */
  633.       if((*fcn)(pattern, line))
  634.         return 1;
  635.       line = end;
  636.     }
  637.     return 0;
  638. }
  639. #endif /* ART_GREP */
  640.