tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

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