"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/feed.c" (12 Oct 2016, 30726 Bytes) of package /linux/misc/tin-2.4.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "feed.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4.0_vs_2.4.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : feed.c
    4  *  Author    : I. Lea
    5  *  Created   : 1991-08-31
    6  *  Updated   : 2016-10-10
    7  *  Notes     : provides same interface to mail,pipe,print,save & repost commands
    8  *
    9  * Copyright (c) 1991-2017 Iain Lea <iain@bricbrac.de>
   10  * All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. The name of the author may not be used to endorse or promote
   21  *    products derived from this software without specific prior written
   22  *    permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
   25  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
   28  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   30  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   32  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   35  */
   36 
   37 
   38 #ifndef TIN_H
   39 #   include "tin.h"
   40 #endif /* !TIN_H */
   41 #ifndef TCURSES_H
   42 #   include "tcurses.h"
   43 #endif /* !TCURSES_H */
   44 
   45 
   46 static t_bool confirm;                  /* only used for FEED_MAIL */
   47 static t_bool is_mailbox = FALSE;
   48 static t_bool redraw_screen = FALSE;
   49 static t_bool supersede = FALSE;        /* for reposting only */
   50 static t_function pproc_func;           /* Post-processing type when saving */
   51 #ifndef DONT_HAVE_PIPING
   52     static FILE *pipe_fp = (FILE *) 0;
   53     static t_bool got_epipe = FALSE;
   54 #endif /* !DONT_HAVE_PIPING */
   55 
   56 
   57 struct t_counters {
   58     int success;        /* # arts fed okay */
   59     int total;          /* # arts fed */
   60     int max;            /* initial guesstimate of total */
   61 };
   62 
   63 /*
   64  * Local prototypes
   65  */
   66 static char *get_save_filename(struct t_group *group, int function, char *filename, int filelen, int respnum);
   67 static t_bool expand_feed_filename(char *outpath, size_t outpath_len, const char *path);
   68 static t_bool feed_article(int art, int function, struct t_counters *counter, t_bool use_current, const char *data, struct t_group *group);
   69 static t_function get_feed_key(int function, int level, struct t_group *group, struct t_art_stat *thread, int respnum);
   70 static t_function get_post_proc_type(void);
   71 static void print_save_summary(t_function type, int fed);
   72 #ifndef DISABLE_PRINTING
   73     static t_bool print_file(const char *command, int respnum, t_openartinfo *artinfo);
   74 #endif /* !DISABLE_PRINTING */
   75 
   76 #ifndef DONT_HAVE_PIPING
   77 #   define handle_EPIPE()   if (got_epipe) goto got_epipe_while_piping
   78 #else
   79 #   define handle_EPIPE() /*nothing*/
   80 #endif /* !DONT_HAVE_PIPING */
   81 
   82 /*
   83  * 'filename' holds 'filelen' amount of storage in which to place the
   84  * filename to save-to. The filename is also returned after basic syntax
   85  * checking. We default to the global save filename or group specific
   86  * filename if it exists
   87  */
   88 static char *
   89 get_save_filename(
   90     struct t_group *group,
   91     int function,
   92     char *filename,
   93     int filelen,
   94     int respnum)
   95 {
   96     char default_savefile[PATH_LEN];
   97 
   98     filename[0] = '\0';
   99 
  100     /*
  101      * Group attribute savefile overrides tinrc default savefile
  102      */
  103     my_strncpy(default_savefile, (group->attribute->savefile ? group->attribute->savefile : tinrc.default_save_file), sizeof(default_savefile) - 1);
  104 
  105     /*
  106      * We don't ask when auto'S'aving or Archive-Name saving with auto_save
  107      */
  108     if (!(function == FEED_AUTOSAVE || (group->attribute->auto_save && arts[respnum].archive))) {
  109         if (!prompt_default_string(_(txt_save_filename), filename, filelen, default_savefile, HIST_SAVE_FILE)) {
  110             clear_message();
  111             return NULL;
  112         }
  113         str_trim(filename);
  114     }
  115 
  116     /*
  117      * Update tinrc.default_save_file if changed
  118      */
  119     if (*filename)
  120         my_strncpy(tinrc.default_save_file, filename, sizeof(tinrc.default_save_file) - 1);
  121     else {
  122         /*
  123          * None chosen (or AUTOSAVING), use tinrc default
  124          */
  125         if (*default_savefile)
  126             my_strncpy(filename, default_savefile, filelen - 1);
  127         else {                                  /* No default either */
  128             info_message(_(txt_no_filename));
  129             return NULL;
  130         }
  131     }
  132 
  133     /*
  134      * Punt invalid expansions
  135      */
  136     if ((filename[0] == '~' || filename[0] == '+') && filename[1] == '\0') {
  137         info_message(_(txt_no_filename));
  138         return NULL;
  139     }
  140     return filename;
  141 }
  142 
  143 
  144 /*
  145  * Generate a path/filename to save to, using 'path' as input.
  146  * The pathname is stored in 'outpath', which should be PATH_LEN in size
  147  * Expand metacharacters and use defaults as needed.
  148  * Return TRUE if the path is a mailbox, or FALSE otherwise.
  149  */
  150 static t_bool
  151 expand_feed_filename(
  152     char *outpath,
  153     size_t outpath_len,
  154     const char *path)
  155 {
  156     int ret = strfpath(path, outpath, PATH_LEN, curr_group, TRUE);
  157 
  158     /*
  159      * If no path exists or the above failed in some way, use sensible defaults
  160      * Put the generic path into 'outpath'
  161      */
  162     if ((ret == 0) || !(strrchr(outpath, DIRSEP))) {
  163         char buf[PATH_LEN];
  164 
  165         if (!strfpath((cmdline.args & CMDLINE_SAVEDIR) ? cmdline.savedir : curr_group->attribute->savedir, buf, sizeof(buf), curr_group, FALSE))
  166             joinpath(buf, sizeof(buf), homedir, DEFAULT_SAVEDIR);
  167         joinpath(outpath, outpath_len, buf, path);
  168         return FALSE;
  169     }
  170     return (ret == 1);
  171 }
  172 
  173 
  174 /*
  175  * Find out what post-processing to perform.
  176  * This is not used when saving to mailboxes (we don't postprocess mailboxes)
  177  * Also not used when using the auto-save feature because a default value is
  178  * taken from the group attributes
  179  * Returns POSTPROCESS_{NO,SHAR,YES} or GLOBAL_ABORT if aborting the save process
  180  */
  181 static t_function
  182 get_post_proc_type(
  183     void)
  184 {
  185     char keyno[MAXKEYLEN], keyyes[MAXKEYLEN], keyquit[MAXKEYLEN];
  186     char keyshar[MAXKEYLEN];
  187     t_function default_func, func;
  188 
  189     switch (curr_group->attribute->post_process_type) {
  190         case POST_PROC_YES:
  191             default_func = POSTPROCESS_YES;
  192             break;
  193 
  194         case POST_PROC_SHAR:
  195             default_func = POSTPROCESS_SHAR;
  196             break;
  197 
  198         case POST_PROC_NO:
  199         default:
  200             default_func = POSTPROCESS_NO;
  201             break;
  202     }
  203 
  204     func = prompt_slk_response(default_func, feed_post_process_keys, _(txt_choose_post_process_type),
  205                 printascii(keyno, func_to_key(POSTPROCESS_NO, feed_post_process_keys)),
  206                 printascii(keyyes, func_to_key(POSTPROCESS_YES, feed_post_process_keys)),
  207                 printascii(keyshar, func_to_key(POSTPROCESS_SHAR, feed_post_process_keys)),
  208                 printascii(keyquit, func_to_key(GLOBAL_QUIT, feed_post_process_keys)));
  209 
  210     if (func == GLOBAL_QUIT || func == GLOBAL_ABORT) {          /* exit */
  211         clear_message();
  212         return GLOBAL_ABORT;
  213     }
  214     return func;
  215 }
  216 
  217 
  218 /*
  219  * Return the key mapping for what we are intending to process or
  220  * GLOBAL_ABORT if save process is being aborted
  221  * Key can be (current) article, (current) thread, tagged articles,
  222  * hot articles, or articles matching a pattern
  223  * This is automatic in the various auto-save cases, in other
  224  * cases this is prompted for based on a chosen default
  225  */
  226 static t_function
  227 get_feed_key(
  228     int function,
  229     int level,
  230     struct t_group *group,
  231     struct t_art_stat *thread,
  232     int respnum)
  233 {
  234     constext *prompt;
  235     t_function default_func, func;
  236 
  237     switch (function) {
  238         case FEED_MAIL:
  239             prompt = txt_mail;
  240             break;
  241 
  242         case FEED_MARK_READ:
  243         case FEED_MARK_UNREAD:
  244             prompt = txt_mark;
  245             break;
  246 
  247 #ifndef DONT_HAVE_PIPING
  248         case FEED_PIPE:
  249             prompt = txt_pipe;
  250             break;
  251 #endif /* !DONT_HAVE_PIPING */
  252 
  253 #ifndef DISABLE_PRINTING
  254         case FEED_PRINT:
  255             prompt = txt_print;
  256             break;
  257 #endif /* !DISABLE_PRINTING */
  258 
  259         /* FEED_AUTOSAVE doesn't prompt */
  260         case FEED_SAVE:
  261             prompt = txt_save;
  262             break;
  263 
  264         case FEED_REPOST:
  265             if (!can_post) {                /* Get this over with before asking any Q's */
  266                 info_message(_(txt_cannot_post));
  267                 return NOT_ASSIGNED;
  268             }
  269             prompt = txt_repost;
  270             break;
  271 
  272         default:
  273             prompt = "";
  274             break;
  275     }
  276 
  277     /*
  278      * Try and work out what default the user wants
  279      * thread->total = # arts in thread
  280      */
  281     default_func = (range_active ? FEED_RANGE :
  282                     num_of_tagged_arts ? FEED_TAGGED :
  283                     (arts_selected() ? FEED_HOT :
  284                     ((level == GROUP_LEVEL && thread->total > 1) ? FEED_THREAD :
  285                     (thread->selected_total ? FEED_HOT :
  286                     FEED_ARTICLE))));
  287 
  288     /*
  289      * Don't bother querying when:
  290      *  auto'S'aving and there are tagged or selected(hot) articles
  291      *  using the auto_save feature on Archive postings
  292      */
  293     if ((function == FEED_AUTOSAVE && (range_active || num_of_tagged_arts || arts_selected()))
  294             || (function == FEED_SAVE && group->attribute->auto_save && arts[respnum].archive))
  295         func = default_func;
  296     else {
  297         char buf[LEN];
  298         char keyart[MAXKEYLEN], keythread[MAXKEYLEN], keyrange[MAXKEYLEN], keyhot[MAXKEYLEN];
  299         char keypat[MAXKEYLEN], keytag[MAXKEYLEN], keyquit[MAXKEYLEN];
  300 
  301         snprintf(buf, sizeof(buf), _(txt_art_thread_regex_tag),
  302             printascii(keyart, func_to_key(FEED_ARTICLE, feed_type_keys)),
  303             printascii(keythread, func_to_key(FEED_THREAD, feed_type_keys)),
  304             printascii(keyrange, func_to_key(FEED_RANGE, feed_type_keys)),
  305             printascii(keyhot, func_to_key(FEED_HOT, feed_type_keys)),
  306             printascii(keypat, func_to_key(FEED_PATTERN, feed_type_keys)),
  307             printascii(keytag, func_to_key(FEED_TAGGED, feed_type_keys)),
  308             printascii(keyquit, func_to_key(GLOBAL_QUIT, feed_type_keys)));
  309 
  310         func = prompt_slk_response(default_func, feed_type_keys, "%s %s", _(prompt), buf);
  311     }
  312 
  313     switch (func) {
  314         case FEED_PATTERN:
  315             {
  316                 char *tmp = fmt_string(_(txt_feed_pattern), tinrc.default_pattern);
  317 
  318                 if (!(prompt_string_default(tmp, tinrc.default_pattern, _(txt_no_match), HIST_REGEX_PATTERN))) {
  319                     free(tmp);
  320                     return GLOBAL_ABORT;
  321                 }
  322                 free(tmp);
  323             }
  324             break;
  325 
  326         case FEED_RANGE:
  327             if (!range_active) {
  328                 if (set_range(level, 1, currmenu->max, currmenu->curr + 1))
  329                     range_active = TRUE;
  330                 else
  331                     return GLOBAL_ABORT;
  332             }
  333             break;
  334 
  335         case GLOBAL_QUIT:
  336         case GLOBAL_ABORT:
  337             clear_message();
  338             return GLOBAL_ABORT;
  339             /* NOTREACHED */
  340             break;
  341 
  342         default:
  343             break;
  344     }
  345 
  346     return func;
  347 }
  348 
  349 
  350 /*
  351  * Print a message like:
  352  * -- [Article|Thread|Tagged Articles] saved to [mailbox] [filenames] --
  353  * 'fed' is the number of articles we tried to save
  354  */
  355 static void
  356 print_save_summary(
  357     t_function type,
  358     int fed)
  359 {
  360     const char *first, *last;
  361     char buf[LEN];
  362     char what[LEN];
  363 
  364     if (fed != num_save)
  365         wait_message(2, _(txt_warn_not_all_arts_saved), fed, num_save);
  366 
  367     switch (type) {
  368         case FEED_HOT:
  369             snprintf(what, sizeof(what), _(txt_prefix_hot), PLURAL(fed, txt_article));
  370             break;
  371 
  372         case FEED_TAGGED:
  373             snprintf(what, sizeof(what), _(txt_prefix_tagged), PLURAL(fed, txt_article));
  374             break;
  375 
  376         case FEED_THREAD:
  377             STRCPY(what, _(txt_thread_upper));
  378             break;
  379 
  380         case FEED_ARTICLE:
  381         case FEED_PATTERN:
  382         default:
  383             snprintf(what, sizeof(what), "%s", PLURAL(fed, txt_article));
  384             break;
  385     }
  386 
  387     first = (save[0].mailbox) ? save[0].path : save[0].file;
  388     last = (save[num_save - 1].mailbox) ? save[num_save - 1].path : save[num_save - 1].file;
  389 
  390     /*
  391      * We report the range of saved-to files for regular saves of > 1 articles
  392      */
  393     if (num_save == 1 || save[0].mailbox)
  394         snprintf(buf, sizeof(buf), _(txt_saved_to),
  395             what, (save[0].mailbox ? _(txt_mailbox) : ""), first);
  396     else
  397         snprintf(buf, sizeof(buf), _(txt_saved_to_range),
  398             what, first, last);
  399 
  400     wait_message((tinrc.beginner_level) ? 4 : 2, buf);
  401 
  402     return;
  403 }
  404 
  405 
  406 /*
  407  * This is the handler that processes a single article for all the various
  408  * FEED_ functions.
  409  * Assumes no article is open when we enter - opens and closes the art being
  410  * processed. As a performance hack this is not done if 'use_current' is set.
  411  * Returns TRUE or FALSE
  412  * TODO: option to mail/pipe/print/repost raw vs. cooked?
  413  *       (all currently raw only) or should we feed according to what
  414  *       is currently on screen?
  415  */
  416 static t_bool
  417 feed_article(
  418     int art,                /* index in arts[] */
  419     int function,
  420     struct t_counters *counter, /* Accounting */
  421     t_bool use_current,     /* Use already open pager article */
  422     const char *data,       /* Extra data if needed, print command or save filename */
  423     struct t_group *group)
  424 {
  425     char *progress_mesg = NULL;
  426     t_bool ok = TRUE;       /* Assume success */
  427     t_openartinfo openart;
  428     t_openartinfo *openartptr = &openart;
  429 
  430     counter->total++;
  431 
  432     /*
  433      * Update the on-screen progress before art_open(), which is the bottleneck
  434      * timewise
  435      */
  436     switch (function) {
  437 #ifndef DONT_HAVE_PIPING
  438         case FEED_PIPE:
  439             progress_mesg = fmt_string("%s (%d/%d)", _(txt_piping), counter->total, counter->max);
  440             break;
  441 #endif /* !DONT_HAVE_PIPING */
  442 
  443 #ifndef DISABLE_PRINTING
  444         case FEED_PRINT:
  445             progress_mesg = fmt_string("%s (%d/%d)", _(txt_printing), counter->total, counter->max);
  446             break;
  447 #endif /* !DISABLE_PRINTING */
  448 
  449         case FEED_SAVE:
  450         case FEED_AUTOSAVE:
  451             progress_mesg = fmt_string("%s (%d/%d)", _(txt_saving), counter->total, counter->max);
  452             break;
  453     }
  454 
  455     if (progress_mesg != NULL) {
  456         if (!use_current)
  457             show_progress(progress_mesg, counter->total, counter->max);
  458         FreeAndNull(progress_mesg);
  459     }
  460 
  461     if (use_current)
  462         openartptr = &pgart;            /* Use art already open in pager */
  463     else {
  464         if (art_open(FALSE, &arts[art], group, openartptr, FALSE, NULL) < 0)
  465             /* User abort or an error */
  466             return FALSE;
  467     }
  468 
  469     switch (function) {
  470         case FEED_MAIL:
  471             switch (mail_to_someone(tinrc.default_mail_address, confirm, openartptr, group)) {
  472                 case POSTED_REDRAW:
  473                     redraw_screen = TRUE;
  474                     /* FALLTHROUGH */
  475                 case POSTED_NONE:
  476                     ok = FALSE;
  477                     break;
  478 
  479                 case POSTED_OK:
  480                     break;
  481             }
  482             confirm = bool_not(ok);     /* Only confirm the next one after a failure */
  483             break;
  484 
  485         case FEED_MARK_READ:
  486             if (arts[art].status == ART_UNREAD || arts[art].status == ART_WILL_RETURN)
  487                 art_mark(curr_group, &arts[art], ART_READ);
  488             else
  489                 ok = FALSE;
  490             break;
  491 
  492         case FEED_MARK_UNREAD:
  493             if (arts[art].status == ART_READ)
  494                 art_mark(curr_group, &arts[art], ART_WILL_RETURN);
  495             else
  496                 ok = FALSE;
  497             break;
  498 
  499 #ifndef DONT_HAVE_PIPING
  500         case FEED_PIPE:
  501             rewind(openartptr->raw);
  502             ok = copy_fp(openartptr->raw, pipe_fp);
  503             if (errno == EPIPE) /* broken pipe in copy_fp() */
  504                 got_epipe = TRUE;
  505             break;
  506 #endif /* !DONT_HAVE_PIPING */
  507 
  508 #ifndef DISABLE_PRINTING
  509         case FEED_PRINT:
  510             ok = print_file(data /*print_command*/, art, openartptr);
  511             break;
  512 #endif /* !DISABLE_PRINTING */
  513 
  514         case FEED_SAVE:
  515         case FEED_AUTOSAVE:
  516             ok = save_and_process_art(openartptr, &arts[art], is_mailbox, data /*filename*/, counter->max, (pproc_func != POSTPROCESS_NO));
  517             if (ok && curr_group->attribute->mark_saved_read)
  518                 art_mark(curr_group, &arts[art], ART_READ);
  519             break;
  520 
  521         case FEED_REPOST:
  522             if (repost_article(tinrc.default_repost_group, art, supersede, openartptr) == POSTED_NONE)
  523                 ok = FALSE;
  524             else            /* POSTED_REDRAW, POSTED_OK */
  525                 redraw_screen = TRUE;
  526             break;
  527 
  528         default:
  529             break;
  530     }
  531     if (ok)
  532         counter->success++;
  533 
  534     if (!use_current)
  535         art_close(openartptr);
  536     return ok;
  537 }
  538 
  539 
  540 /*
  541  * Single entry point for 'feed'ing article(s) to a backend
  542  * Function:
  543  *  FEED_PIPE, FEED_MAIL, FEED_PRINT, FEED_REPOST
  544  *  FEED_SAVE, FEED_AUTOSAVE, FEED_MARK_READ, FEED_MARK_UNREAD
  545  * Level:
  546  *  GROUP_LEVEL, THREAD_LEVEL, PAGE_LEVEL
  547  * Type:
  548  *  default feed_type; if NOT_ASSIGNED, query what to do
  549  * Respnum:
  550  *  Index in arts[] of starting article
  551  *
  552  * The following 'groups' of article can be processed:
  553  *  Single (current) article
  554  *  Current thread
  555  *  Range of articles
  556  *  Tagged articles
  557  *  Hot articles
  558  *  Articles matching a pattern
  559  *
  560  * The selection of Function depends on the key used to get here.
  561  * The selection of which article 'group' to process is managed
  562  * inside here, or by defaults.
  563  *
  564  * Returns:
  565  *   1  if there are no more unread arts in this group (FEED_MARK_READ)
  566  *   0  on success
  567  *  -1  on failure/abort
  568  */
  569 int
  570 feed_articles(
  571     int function,
  572     int level,
  573     t_function type,
  574     struct t_group *group,
  575     int respnum)
  576 {
  577     char *prompt;
  578     char outpath[PATH_LEN];
  579     int art;
  580     int i;
  581     int saved_curr_line = -1;
  582     int thread_base;
  583     struct t_art_stat sbuf;
  584     struct t_counters counter = { 0, 0, 0 };
  585     t_bool feed_mark_function = function == FEED_MARK_READ || function == FEED_MARK_UNREAD;
  586     t_bool mark_saved = FALSE;
  587     t_bool no_next_unread = FALSE;
  588     t_bool post_processed_ok = FALSE;
  589     t_bool use_current = FALSE;
  590     t_function feed_type;
  591 
  592 #ifdef DONT_HAVE_PIPING
  593     if (function == FEED_PIPE) {
  594         error_message(2, _(txt_piping_not_enabled));
  595         clear_message();
  596         return -1;
  597     }
  598 #endif /* DONT_HAVE_PIPING */
  599 
  600     if (function == FEED_AUTOSAVE) {
  601         if (!range_active && num_of_tagged_arts == 0 && !arts_selected()) {
  602             info_message(_(txt_no_marked_arts));
  603             return -1;
  604         }
  605     }
  606 
  607     set_xclick_off();       /* TODO: there is no corresponding set_xclick_on()? */
  608     if ((thread_base = which_thread(respnum)) >= 0)
  609         stat_thread(thread_base, &sbuf);
  610     else /* TODO: error message? */
  611         return -1;
  612 
  613     switch (type) {
  614         case FEED_ARTICLE:
  615         case FEED_THREAD:
  616         case FEED_RANGE:
  617             feed_type = type;
  618             break;
  619 
  620         default:
  621             if ((feed_type = get_feed_key(function, level, group, &sbuf, respnum)) == GLOBAL_ABORT)
  622                 return -1;
  623             break;
  624     }
  625 
  626     /*
  627      * Get whatever information is needed to proceed
  628      */
  629     switch (function) {
  630         /* Setup mail - get address to mail to */
  631         case FEED_MAIL:
  632             prompt = fmt_string(_(txt_mail_art_to), cCOLS - (strlen(_(txt_mail_art_to)) + 30), tinrc.default_mail_address);
  633             if (!(prompt_string_default(prompt, tinrc.default_mail_address, _(txt_no_mail_address), HIST_MAIL_ADDRESS))) {
  634                 free(prompt);
  635                 return -1;
  636             }
  637             free(prompt);
  638             break;
  639 
  640 #ifndef DONT_HAVE_PIPING
  641         /* Setup pipe - get pipe-to command and open the pipe */
  642         case FEED_PIPE:
  643             prompt = fmt_string(_(txt_pipe_to_command), cCOLS - (strlen(_(txt_pipe_to_command)) + 30), tinrc.default_pipe_command);
  644             if (!(prompt_string_default(prompt, tinrc.default_pipe_command, _(txt_no_command), HIST_PIPE_COMMAND))) {
  645                 free(prompt);
  646                 return -1;
  647             }
  648             free(prompt);
  649 
  650             got_epipe = FALSE;
  651             EndWin(); /* Turn off curses/windowing */
  652             Raw(FALSE);
  653             fflush(stdout);
  654             set_signal_catcher(FALSE);
  655             if ((pipe_fp = popen(tinrc.default_pipe_command, "w")) == NULL) {
  656                 perror_message(_(txt_command_failed), tinrc.default_pipe_command);
  657                 set_signal_catcher(TRUE);
  658                 Raw(TRUE);
  659                 InitWin();
  660                 return -1;
  661             }
  662             break;
  663 #endif /* !DONT_HAVE_PIPING */
  664 
  665 #ifndef DISABLE_PRINTING
  666         /* Setup printing - get print command line */
  667         case FEED_PRINT:
  668             snprintf(outpath, sizeof(outpath), "%s %s", tinrc.printer, REDIRECT_OUTPUT);
  669             break;
  670 #endif /* !DISABLE_PRINTING */
  671 
  672         /*
  673          * Setup saving, some of these are generated automatically
  674          *  Determine path/file to save to
  675          *  Determine post-processing type
  676          *  Determine if post processed file deletion required
  677          */
  678         case FEED_SAVE:
  679         case FEED_AUTOSAVE:
  680             {
  681                 char savefile[PATH_LEN];
  682 
  683                 /* This will force automatic selection unless changed by user */
  684                 savefile[0] = '\0';
  685 
  686                 if (get_save_filename(group, function, savefile, sizeof(savefile), respnum) == NULL)
  687                     return -1;
  688 
  689                 switch (curr_group->attribute->post_process_type) {
  690                     case POST_PROC_YES:
  691                         pproc_func = POSTPROCESS_YES;
  692                         break;
  693 
  694                     case POST_PROC_SHAR:
  695                         pproc_func = POSTPROCESS_SHAR;
  696                         break;
  697 
  698                     case POST_PROC_NO:
  699                     default:
  700                         pproc_func = POSTPROCESS_NO;
  701                         break;
  702                 }
  703 
  704                 /* We don't postprocess mailboxen */
  705                 if ((is_mailbox = expand_feed_filename(outpath, sizeof(outpath), savefile)) == TRUE)
  706                     pproc_func = POSTPROCESS_NO;
  707                 else {
  708                     if (function != FEED_AUTOSAVE && (pproc_func = get_post_proc_type()) == GLOBAL_ABORT)
  709                         return -1;
  710                 }
  711                 if (!create_path(outpath))
  712                     return -1;
  713             }
  714             break;
  715 
  716         /* repost (or supersede) article */
  717         case FEED_REPOST:
  718             {
  719                 char *tmp;
  720 #ifndef FORGERY
  721                 char from_name[PATH_LEN];
  722 
  723                 get_from_name(from_name, (struct t_group *) 0);
  724 
  725                 if (strstr(from_name, arts[respnum].from)) {
  726 #endif /* !FORGERY */
  727                     char *smsg;
  728                     char buf[LEN];
  729                     char keyrepost[MAXKEYLEN], keysupersede[MAXKEYLEN];
  730                     char keyquit[MAXKEYLEN];
  731                     t_function func;
  732 
  733                     /* repost or supersede? */
  734                     snprintf(buf, sizeof(buf), _(txt_supersede_article),
  735                             printascii(keyrepost, func_to_key(FEED_KEY_REPOST, feed_supersede_article_keys)),
  736                             printascii(keysupersede, func_to_key(FEED_SUPERSEDE, feed_supersede_article_keys)),
  737                             printascii(keyquit, func_to_key(GLOBAL_QUIT, feed_supersede_article_keys)));
  738                     func = prompt_slk_response(FEED_SUPERSEDE,
  739                                 feed_supersede_article_keys, "%s",
  740                                 sized_message(&smsg, buf, arts[respnum].subject));
  741                     free(smsg);
  742 
  743                     switch (func) {
  744                         case FEED_SUPERSEDE:
  745                             tmp = fmt_string(_(txt_supersede_group), tinrc.default_repost_group);
  746                             supersede = TRUE;
  747                             break;
  748 
  749                         case FEED_KEY_REPOST:
  750                             tmp = fmt_string(_(txt_repost_group), tinrc.default_repost_group);
  751                             supersede = FALSE;
  752                             break;
  753 
  754                         default:
  755                             clear_message();
  756                             return -1;
  757                     }
  758 #ifndef FORGERY
  759                 } else {
  760                     tmp = fmt_string(_(txt_repost_group), tinrc.default_repost_group);
  761                     supersede = FALSE;
  762                 }
  763 #endif /* !FORGERY */
  764                 if (!(prompt_string_default(tmp, tinrc.default_repost_group, _(txt_no_group), HIST_REPOST_GROUP))) {
  765                     free(tmp);
  766                     return -1;
  767                 }
  768                 free(tmp);
  769             }
  770             break;
  771 
  772         default:
  773             break;
  774     } /* switch (function) */
  775 
  776     confirm = TRUE;             /* Always confirm the first time */
  777     clear_message();
  778 
  779     /*
  780      * Performance hack - If we feed a single art from the pager then we can
  781      * re-use the currently open article
  782      * Also no need to fetch articles just to mark them (un)read
  783      */
  784     if (feed_mark_function || (level == PAGE_LEVEL && (feed_type == FEED_ARTICLE || feed_type == FEED_THREAD))) {
  785         saved_curr_line = curr_line;        /* Save where we were in pager */
  786         use_current = TRUE;
  787     }
  788 
  789     /*
  790      * This is the main loop
  791      * The general idea is to feed_article() for every article to be processed
  792      */
  793     switch (feed_type) {
  794         case FEED_ARTICLE:      /* article */
  795             counter.max = 1;
  796             if (!feed_article(respnum, function, &counter, use_current, outpath, group))
  797                 handle_EPIPE();
  798             break;
  799 
  800         case FEED_THREAD:       /* thread */
  801             /* Get accurate count first */
  802             for_each_art_in_thread(art, which_thread(respnum)) {
  803                 if (feed_mark_function || !(curr_group->attribute->process_only_unread && arts[art].status == ART_READ))
  804                     counter.max++;
  805             }
  806 
  807             for_each_art_in_thread(art, which_thread(respnum)) {
  808                 if (feed_mark_function || !(curr_group->attribute->process_only_unread && arts[art].status == ART_READ)) {
  809                     /* Keep going - don't abort on errors */
  810                     if (!feed_article(art, function, &counter, use_current, outpath, group))
  811                         handle_EPIPE();
  812                 }
  813             }
  814             break;
  815 
  816         case FEED_RANGE:
  817             /* Get accurate count first */
  818             for_each_art(art) {
  819                 if (arts[art].inrange)
  820                     counter.max++;
  821             }
  822 
  823             for_each_art(art) {
  824                 if (arts[art].inrange) {
  825                     arts[art].inrange = FALSE;
  826                     if (!feed_article(art, function, &counter, use_current, outpath, group))
  827                         handle_EPIPE();
  828                 }
  829             }
  830             range_active = FALSE;
  831             redraw_screen = TRUE;
  832             break;
  833 
  834         case FEED_TAGGED:       /* tagged articles */
  835             counter.max = num_of_tagged_arts;
  836             for (i = 1; i <= num_of_tagged_arts; i++) {
  837                 for_each_art(art) {
  838                     /* process_only_unread does NOT apply on tagged arts */
  839                     if (arts[art].tagged == i) {
  840                         /* Keep going - don't abort on errors */
  841                         if (!feed_article(art, function, &counter, use_current, outpath, group))
  842                             handle_EPIPE();
  843                     }
  844                 }
  845             }
  846             untag_all_articles();   /* TODO: this will untag even on partial failure */
  847             redraw_screen = TRUE;
  848             break;
  849 
  850         case FEED_HOT:      /* hot (auto-selected) articles */
  851         case FEED_PATTERN:  /* pattern matched articles */
  852             {
  853                 struct regex_cache cache = { NULL, NULL };
  854 
  855                 if ((feed_type == FEED_PATTERN) && tinrc.wildcard && !(compile_regex(tinrc.default_pattern, &cache, PCRE_CASELESS)))
  856                     break;
  857 
  858                 for_each_art(art) {
  859                     if (feed_type == FEED_PATTERN) {
  860                         if (!match_regex(arts[art].subject, tinrc.default_pattern, &cache, TRUE))
  861                             continue;
  862                     } else if (!arts[art].selected)
  863                         continue;
  864 
  865                     if (!feed_mark_function && (curr_group->attribute->process_only_unread && arts[art].status == ART_READ))
  866                         continue;
  867 
  868                     arts[art].matched = TRUE;
  869                     counter.max++;
  870                 }
  871 
  872                 if (tinrc.wildcard) {
  873                     FreeIfNeeded(cache.re);
  874                     FreeIfNeeded(cache.extra);
  875                 }
  876             }
  877 
  878             /* I think we nest like this to preserve any 'ordering' of the arts */
  879             for (i = 0; i < grpmenu.max; i++) {
  880                 for_each_art_in_thread(art, i) {
  881                     if (!arts[art].matched)
  882                         continue;
  883                     arts[art].matched = FALSE;
  884 
  885                     /* Keep going - don't abort on errors */
  886                     if (feed_article(art, function, &counter, use_current, outpath, group)) {
  887                         if (feed_type == FEED_HOT)
  888                             arts[art].selected = FALSE;
  889                     } else
  890                         handle_EPIPE();
  891                 }
  892             }
  893             redraw_screen = TRUE;
  894             break;
  895 
  896         default:            /* Should never get here */
  897             break;
  898     } /* switch (feed_type) */
  899 
  900     /*
  901      * Invoke post-processing if needed
  902      * Work out what (if anything) needs to be redrawn
  903      */
  904     if (INTERACTIVE_NONE == tinrc.interactive_mailer)
  905         redraw_screen |= mail_check();  /* in case of sending to oneself */
  906 
  907     switch (function) {
  908         case FEED_MARK_READ:
  909         case FEED_MARK_UNREAD:
  910             redraw_screen = FALSE;
  911             if (level == GROUP_LEVEL) {
  912                 no_next_unread = group_mark_postprocess(function, feed_type, respnum);
  913                 break;
  914             }
  915             if (level == THREAD_LEVEL)
  916                 no_next_unread = thread_mark_postprocess(function, feed_type, respnum);
  917             break;
  918 
  919 #ifndef DONT_HAVE_PIPING
  920         case FEED_PIPE:
  921 got_epipe_while_piping:
  922             if (got_epipe)
  923                 perror_message(_(txt_command_failed), tinrc.default_pipe_command);
  924             got_epipe = FALSE;
  925             fflush(pipe_fp);
  926             (void) pclose(pipe_fp);
  927             set_signal_catcher(TRUE);
  928             my_printf(cCRLF);
  929 #   ifdef USE_CURSES
  930             Raw(TRUE);
  931             InitWin();
  932 #   endif /* USE_CURSES */
  933             prompt_continue();
  934 #   ifndef USE_CURSES
  935             Raw(TRUE);
  936             InitWin();
  937 #   endif /* !USE_CURSES */
  938             redraw_screen = TRUE;
  939             break;
  940 #endif /* !DONT_HAVE_PIPING */
  941 
  942         case FEED_SAVE:
  943         case FEED_AUTOSAVE:
  944             if (num_save == 0) {
  945                 wait_message(1, _(txt_saved_nothing));
  946                 break;
  947             }
  948 
  949             if (redraw_screen) {
  950                 currmenu->redraw();
  951                 redraw_screen = FALSE;
  952             }
  953 
  954             print_save_summary(feed_type, counter.total);
  955             if (pproc_func != POSTPROCESS_NO) {
  956                 t_bool delete_post_proc = FALSE;
  957 
  958                 if (curr_group->attribute->delete_tmp_files)
  959                     delete_post_proc = TRUE;
  960                 else {
  961                     if (function != FEED_AUTOSAVE) {
  962                         if (prompt_yn(_(txt_delete_processed_files), TRUE) == 1)
  963                             delete_post_proc = TRUE;
  964                     }
  965                 }
  966                 post_processed_ok = post_process_files(pproc_func, delete_post_proc);
  967             }
  968             free_save_array();      /* NB: This is where num_save etc.. gets purged */
  969 
  970             if (level != PAGE_LEVEL)
  971                 mark_saved = curr_group->attribute->mark_saved_read;
  972             break;
  973 
  974         default:
  975             break;
  976     }
  977 
  978     if (mark_saved || post_processed_ok)
  979         redraw_screen = TRUE;
  980 
  981     if (level == PAGE_LEVEL && !feed_mark_function) {
  982         if (tinrc.force_screen_redraw)
  983             redraw_screen = TRUE;
  984 
  985         /*
  986          * If we were using the paged art return to our former position
  987          */
  988         if (use_current)
  989             curr_line = saved_curr_line;
  990 
  991         if (redraw_screen)
  992             draw_page(group->name, 0);
  993         else {
  994             if (function == FEED_PIPE)
  995                 clear_message();
  996         }
  997     } else {
  998         if (redraw_screen) {
  999             currmenu->redraw();
 1000             redraw_screen = FALSE;
 1001         }
 1002     }
 1003 
 1004     /*
 1005      * Finally print a status message
 1006      */
 1007     switch (function) {
 1008         case FEED_MAIL:
 1009             if (INTERACTIVE_NONE != tinrc.interactive_mailer)
 1010                 info_message(_(txt_external_mail_done));
 1011             else
 1012                 info_message(_(txt_articles_mailed), counter.success, PLURAL(counter.success, txt_article));
 1013             break;
 1014 
 1015         case FEED_MARK_READ:
 1016         case FEED_MARK_UNREAD:
 1017             if (no_next_unread)
 1018                 info_message(_(txt_no_next_unread_art));
 1019             else {
 1020                 if (counter.success && level != PAGE_LEVEL) {
 1021                     const char *ptr;
 1022 
 1023                     ptr = function == FEED_MARK_READ ? _(txt_marked_as_read) : _(txt_marked_as_unread);
 1024                     if (feed_type == FEED_THREAD) {
 1025                         info_message(ptr, _(txt_thread_upper));
 1026                     } else if (feed_type == FEED_ARTICLE) {
 1027                         info_message(ptr, _(txt_article_upper));
 1028                     } else {
 1029                         ptr = function == FEED_MARK_READ ? _(txt_marked_arts_as_read) : _(txt_marked_arts_as_unread);
 1030                         info_message(ptr, counter.success, counter.max, PLURAL(counter.max, txt_article));
 1031                     }
 1032                 }
 1033             }
 1034             break;
 1035 
 1036 #ifndef DONT_HAVE_PIPING
 1037         case FEED_PIPE:
 1038             info_message(_(txt_articles_piped), counter.success, PLURAL(counter.success, txt_article), tinrc.default_pipe_command);
 1039             break;
 1040 #endif /* !DONT_HAVE_PIPING */
 1041 
 1042 #ifndef DISABLE_PRINTING
 1043         case FEED_PRINT:
 1044             info_message(_(txt_articles_printed), counter.success, PLURAL(counter.success, txt_article));
 1045             break;
 1046 #endif /* !DISABLE_PRINTING */
 1047 
 1048         case FEED_SAVE:     /* Reporting done earlier */
 1049         case FEED_AUTOSAVE:
 1050         default:
 1051             break;
 1052     }
 1053     return no_next_unread ? 1 : 0;
 1054 }
 1055 
 1056 
 1057 #ifndef DISABLE_PRINTING
 1058 static t_bool
 1059 print_file(
 1060     const char *command,
 1061     int respnum,
 1062     t_openartinfo *artinfo)
 1063 {
 1064     FILE *fp;
 1065     struct t_header *hdr = &artinfo->hdr;
 1066     t_bool ok;
 1067 #   ifdef DONT_HAVE_PIPING
 1068     char cmd[PATH_LEN], file[PATH_LEN];
 1069 #   endif /* DONT_HAVE_PIPING */
 1070 
 1071 #   ifdef DONT_HAVE_PIPING
 1072     snprintf(file, sizeof(file), TIN_PRINTFILE, respnum);
 1073     if ((fp = fopen(file, "w")) == NULL) /* TODO: issue a more correct error message here */
 1074 #   else
 1075     if ((fp = popen(command, "w")) == NULL)
 1076 #   endif /* DONT_HAVE_PIPING */
 1077     {
 1078         perror_message(_(txt_command_failed), command);
 1079         return FALSE;
 1080     }
 1081 
 1082     rewind(artinfo->raw);
 1083     if (!curr_group->attribute->print_header && !(fseek(artinfo->raw, hdr->ext->offset, SEEK_SET))) {   /* -> start of body */
 1084         if (hdr->newsgroups)
 1085             fprintf(fp, "Newsgroups: %s\n", hdr->newsgroups);
 1086         if (arts[respnum].from == arts[respnum].name || arts[respnum].name == NULL)
 1087             fprintf(fp, "From: %s\n", arts[respnum].from);
 1088         else
 1089             fprintf(fp, "From: %s <%s>\n", arts[respnum].name, arts[respnum].from);
 1090         if (hdr->subj)
 1091             fprintf(fp, "Subject: %s\n", hdr->subj);
 1092         if (hdr->date)
 1093             fprintf(fp, "Date: %s\n\n", hdr->date);
 1094     }
 1095 
 1096     ok = copy_fp(artinfo->raw, fp);
 1097 
 1098 #   ifdef DONT_HAVE_PIPING
 1099     fclose(fp);
 1100     strncpy(cmd, command, sizeof(cmd) - 2);
 1101     strcat(cmd, " ");
 1102     strncat(cmd, file, sizeof(cmd) - strlen(cmd) - 1);
 1103     cmd[sizeof(cmd) - 1] = '\0';
 1104     invoke_cmd(cmd);
 1105     unlink(file);
 1106 #   else
 1107     fflush(fp);
 1108     pclose(fp);
 1109 #   endif /* DONT_HAVE_PIPING */
 1110 
 1111     return ok;
 1112 }
 1113 #endif /* !DISABLE_PRINTING */