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)  

newsrc.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : newsrc.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2021-08-27
7 * Notes : ArtCount = (ArtMax - ArtMin) + 1 [could have holes]
8 *
9 * Copyright (c) 1991-2022 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
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#ifndef TNNTP_H
48# include "tnntp.h"
49#endif /* !TNNTP_H */
50#ifndef NEWSRC_H
51# include "newsrc.h"
52#endif /* !NEWSRC_H */
53
54static mode_t newsrc_mode = 0;
55
56/*
57 * Local prototypes
58 */
59static FILE *open_subscription_fp(void);
60static char *parse_newsrc_line(char *line, int *sub);
61static char *parse_subseq(struct t_group *group, char *seq, t_artnum *low, t_artnum *high, t_artnum *sum);
62static char *parse_get_seq(char *seq, t_artnum *low, t_artnum *high);
63static int write_newsrc_line(FILE *fp, char *line);
64static t_bool create_newsrc(char *newsrc_file);
65static void auto_subscribe_groups(char *newsrc_file);
66static void get_subscribe_info(struct t_group *grp);
67static void parse_bitmap_seq(struct t_group *group, char *seq);
68static void print_bitmap_seq(FILE *fp, struct t_group *group);
69
70
71/*
72 * Read .newsrc into my_group[]. my_group[] ints point to active[] entries.
73 * If allgroups is set, then my_group[] is completely overwritten,
74 * otherwise, groups are appended. Any bogus groups will be handled
75 * accordingly. Bogus groups will _not_ be subscribed to as a design
76 * principle.
77 *
78 * Returns the number of lines read(useful for a check newsrc >= oldnewsrc)
79 * < 0 error
80 * >=0 number of lines read
81 */
82signed long int
84 char *newsrc_file,
85 t_bool allgroups)
86{
87 FILE *fp;
88 char *grp, *seq;
89 int sub, i;
90 signed long line_count = 0;
91 struct stat statbuf;
92
93 if (allgroups)
95
96 /*
97 * make a .newsrc if none exist & auto subscribe to set groups
98 */
99 if (stat(newsrc_file, &statbuf) == -1) {
100 if (!create_newsrc(newsrc_file))
101 return -1L; /* ouch */
102 auto_subscribe_groups(newsrc_file);
103 } else
104 newsrc_mode = statbuf.st_mode;
105
106 if ((fp = fopen(newsrc_file, "r")) != NULL) {
107 if (!batch_mode || verbose)
109
110 while ((grp = tin_fgets(fp, FALSE)) != NULL) {
111 strip_line(grp);
112 if (*grp == '#' || *grp == '\0') /* skip comments and empty lines */
113 continue;
114
115 line_count++; /* but count all other lines (incl. bogous ones) */
116 seq = parse_newsrc_line(grp, &sub);
117
118 if (sub == SUBSCRIBED) {
119 if ((i = my_group_add(grp, FALSE)) >= 0) {
120 if (!active[my_group[i]].bogus) {
123 }
124 } else
125 process_bogus(grp);
126 }
127 }
128 fclose(fp);
129 /* If you aborted with 'q', then you get what you get. */
130
131 if (!batch_mode || verbose)
132 my_fputc('\n', stdout);
133
134 if (!cmd_line && !batch_mode)
136 }
137 return line_count;
138}
139
140
141/*
142 * Parse a line from the newsrc file and write it back out with updated
143 * sequence information. Return number of lines written (ie, 0 or 1)
144 */
145static int
147 FILE *fp,
148 char *line)
149{
150 char *seq;
151 int sub;
152 struct t_group *group;
153
154 seq = parse_newsrc_line(line, &sub);
155
156 if (line[0] == '\0' || sub == 0) /* Insurance against blank line */
157 return 0;
158
159 if (seq == NULL) { /* line has no ':' or '!' in it */
162 return 0;
163 }
164
165 /*
166 * Find the group in active. If we cannot, then junk it if bogus groups
167 * are set to auto removal. Also check for bogus flag just in case
168 * strip_bogus was changed since tin started
169 */
170 group = group_find(line, FALSE);
171
173 if (group == NULL || group->bogus) { /* group doesn't exist */
175 return 0;
176 }
177 }
178
179 if ((group && group->newsrc.present) && (group->subscribed || !tinrc.strip_newsrc)) {
180 fprintf(fp, "%s%c ", group->name, SUB_CHAR(group->subscribed));
181 print_bitmap_seq(fp, group);
182 return 1;
183 } else {
184 if (sub == SUBSCRIBED || !tinrc.strip_newsrc) {
185 fprintf(fp, "%s%c %s\n", line, sub, seq);
186 return 1;
187 }
188 }
189 return 0;
190}
191
192
193/*
194 * Read in the users newsrc file and write a new file with all the changes
195 * changes from the current session. If this works, replace the original
196 * newsrc file.
197 * Returns < 0 on error
198 * >=0 number of lines written
199 */
200signed long int
202 void)
203{
204 FILE *fp_ip;
205 FILE *fp_op;
206 char *line;
207 signed long int tot = 0L;
208 struct stat note_stat_newsrc;
209 t_bool write_ok = FALSE;
210 int err;
211
212 if (no_write)
213 return 0L;
214
215 if ((fp_ip = fopen(newsrc, "r")) == NULL)
216 return -1L; /* can't open newsrc */
217
218 /* get size of original newsrc */
219 if (fstat(fileno(fp_ip), &note_stat_newsrc) != 0) {
220 fclose(fp_ip);
221 return -1L; /* can't access newsrc */
222 }
223
224 if (!note_stat_newsrc.st_size) {
225 fclose(fp_ip);
226 return 0L; /* newsrc is empty */
227 }
228
229 if ((fp_op = fopen(newnewsrc, "w")) != NULL) {
230 if (newsrc_mode)
231#ifdef HAVE_FCHMOD
232 fchmod(fileno(fp_op), newsrc_mode);
233#else
234# ifdef HAVE_CHMOD
235 chmod(newnewsrc, newsrc_mode);
236# endif /* HAVE_CHMOD */
237#endif /* HAVE_FCHMOD */
238
239 while ((line = tin_fgets(fp_ip, FALSE)) != NULL)
240 tot += write_newsrc_line(fp_op, line);
241
242 /*
243 * Don't rename if either fclose() fails or ferror() is set
244 */
245 if ((err = ferror(fp_op)) || fclose(fp_op)) {
248 if (err) {
249 clearerr(fp_op);
250 fclose(fp_op);
251 }
252 } else
253 write_ok = TRUE;
254 }
255
256 fclose(fp_ip);
257
258 if (tot < 1) {
261 return 0L; /* So we don't get prompted to try again */
262 }
263
264 if (write_ok)
266
267 return tot;
268}
269
270
271/*
272 * Create a newsrc from active[] groups. Subscribe to all groups.
273 */
274static t_bool
276 char *newsrc_file)
277{
278 FILE *fp;
279 int i;
280
281 if ((fp = fopen(newsrc_file, "w")) != NULL) {
283
285 fprintf(fp, "%s!\n", active[i].name);
286
287 if ((i = ferror(fp)) || fclose(fp)) {
289 if (i) {
290 clearerr(fp);
291 fclose(fp);
292 }
293 return FALSE;
294 }
295 return TRUE; /* newsrc created */
296 }
297 return FALSE;
298}
299
300
301/*
302 * Get a list of default groups to subscribe to
303 */
304static FILE *
306 void)
307{
308 if (!read_saved_news) {
309#ifdef NNTP_ABLE
310 if (read_news_via_nntp) {
311 /* RFC 6048 2.6 */
313 return NULL;
314 else
315 return (nntp_command("LIST SUBSCRIPTIONS", OK_GROUPS, NULL, 0));
316 } else
317#endif /* NNTP_ABLE */
318 return (fopen(subscriptions_file, "r"));
319 } else
320 return NULL;
321}
322
323
324/*
325 * Automatically subscribe user to newsgroups specified in
326 * NEWSLIBDIR/subscriptions (locally) or same file but from NNTP
327 * server (LIST SUBSCRIPTIONS) and create .newsrc
328 */
329static void
331 char *newsrc_file)
332{
333 FILE *fp_newsrc;
334 FILE *fp_subs;
335 char *ptr;
336 int err;
337
338 /*
339 * If subscription file exists then first unsubscribe to all groups
340 * and then subscribe to just the auto specified groups.
341 */
342 if ((fp_subs = open_subscription_fp()) == NULL)
343 return;
344
345 if (!batch_mode)
347
348 if ((fp_newsrc = fopen(newsrc_file, "w")) == NULL) {
349 TIN_FCLOSE(fp_subs);
350 return;
351 }
352
353 if (newsrc_mode)
354#ifdef HAVE_FCHMOD
355 fchmod(fileno(fp_newsrc), newsrc_mode);
356#else
357# ifdef HAVE_CHMOD
358 chmod(newsrc_file, newsrc_mode);
359# endif /* HAVE_CHMOD */
360#endif /* HAVE_FCHMOD */
361
362 /* TODO: test me! */
363 while ((ptr = tin_fgets(fp_subs, FALSE)) != NULL) {
364 if (ptr[0] != '#') {
365 if (group_find(ptr, FALSE) != NULL)
366 fprintf(fp_newsrc, "%s:\n", ptr);
367 }
368 }
369
370 /* We ignore user 'q'uits here. They will get them next time in any case */
371
372 if ((err = ferror(fp_newsrc)) || fclose(fp_newsrc)) {
374 if (err) {
375 clearerr(fp_newsrc);
376 fclose(fp_newsrc);
377 }
378 }
379
380 TIN_FCLOSE(fp_subs);
381}
382
383
384/*
385 * make a backup of users .newsrc in case of the bogie man
386 */
387void
389 void)
390{
391 char dirbuf[PATH_LEN];
392 char filebuf[PATH_LEN];
393 struct stat statbuf;
394
395#ifdef NNTP_ABLE
396 if (read_news_via_nntp && !read_saved_news && nntp_tcp_port != IPPORT_NNTP)
397 snprintf(filebuf, sizeof(filebuf), "%s:%u", nntp_server, nntp_tcp_port);
398 else
399#endif /* NNTP_ABLE */
400 {
402 }
403 joinpath(dirbuf, sizeof(dirbuf), rcdir, filebuf);
404 joinpath(filebuf, sizeof(filebuf), dirbuf, OLDNEWSRC_FILE);
405
406 if (stat(dirbuf, &statbuf) == -1) {
407 if (my_mkdir(dirbuf, (mode_t) (S_IRWXU)) == -1)
408 /* Can't create directory: Fall back on Homedir */
409 joinpath(filebuf, sizeof(filebuf), homedir, OLDNEWSRC_FILE);
410 }
411
412 if (!backup_file(newsrc, filebuf))
414}
415
416
417/*
418 * Find the total, max & min articles number for specified group
419 * Use nntp GROUP command or read local spool
420 * Return 0, or -error
421 */
422int
424 char *tin_spooldir,
425 char *groupname,
426 int grouptype,
427 t_artnum *art_count,
428 t_artnum *art_max,
429 t_artnum *art_min)
430{
431 DIR *dir;
432 DIR_BUF *direntry;
433 t_artnum artnum;
434
435 if (read_news_via_nntp && grouptype == GROUP_TYPE_NEWS) {
436#ifdef NNTP_ABLE
437 char line[NNTP_STRLEN];
438
439 snprintf(line, sizeof(line), "GROUP %s", groupname);
440# ifdef DEBUG
441 if ((debug & DEBUG_NNTP) && verbose > 1)
442 debug_print_file("NNTP", "group_get_art_info %s", line);
443# endif /* DEBUG */
444 put_server(line);
445
446 switch (get_respcode(line, sizeof(line))) {
447 case OK_GROUP:
448 if (sscanf(line, "%"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT" %"T_ARTNUM_SFMT, art_count, art_min, art_max) != 3) {
449# ifdef DEBUG
450 if ((debug & DEBUG_NNTP) && verbose > 1)
451 debug_print_file("NNTP", "Invalid response to GROUP command, %s", line);
452# endif /* DEBUG */
453 }
454 break;
455
456 case ERR_NOGROUP:
457 *art_count = T_ARTNUM_CONST(0);
458 *art_min = T_ARTNUM_CONST(1);
459 *art_max = T_ARTNUM_CONST(0);
460 return -ERR_NOGROUP;
461
462 case ERR_ACCESS:
463 tin_done(NNTP_ERROR_EXIT, "%s", line);
464 /* keep lint quiet: */
465 /* NOTREACHED */
466 break;
467
468 default:
469# ifdef DEBUG
470 if ((debug & DEBUG_NNTP) && verbose > 1)
471 debug_print_file("NNTP", "NOT_OK %s", line);
472# endif /* DEBUG */
473 return -1;
474 }
475#else
476 my_fprintf(stderr, _("Unreachable?\n")); /* TODO: -> lang.c */
477 return 0;
478#endif /* NNTP_ABLE */
479 } else {
480 char group_path[PATH_LEN];
481 *art_count = T_ARTNUM_CONST(0);
482 *art_min = T_ARTNUM_CONST(0);
483 *art_max = T_ARTNUM_CONST(0);
484
485 make_base_group_path(tin_spooldir, groupname, group_path, sizeof(group_path));
486
487 if ((dir = opendir(group_path)) != NULL) {
488 while ((direntry = readdir(dir)) != NULL) {
489 artnum = atoartnum(direntry->d_name); /* should be '\0' terminated... */
490 if (artnum >= T_ARTNUM_CONST(1)) {
491 if (artnum > *art_max) {
492 *art_max = artnum;
493 if (*art_min == T_ARTNUM_CONST(0))
494 *art_min = artnum;
495 } else if (artnum < *art_min)
496 *art_min = artnum;
497 (*art_count)++;
498 }
499 }
500 CLOSEDIR(dir);
501 if (*art_min == T_ARTNUM_CONST(0))
502 *art_min = T_ARTNUM_CONST(1);
503 } else {
504 *art_min = T_ARTNUM_CONST(1);
505 return -1;
506 }
507 }
508
509 return 0;
510}
511
512
513/*
514 * Get and fixup (if needed) the counters for a newly subscribed group
515 */
516static void
518 struct t_group *grp)
519{
520 t_artnum oldmin = grp->xmin;
521 t_artnum oldmax = grp->xmax;
522
523 group_get_art_info(grp->spooldir, grp->name, grp->type, &grp->count, &grp->xmax, &grp->xmin);
524
525 if (grp->newsrc.num_unread > grp->count) {
526#ifdef DEBUG
527 if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
528 my_printf(cCRLF "Unread WRONG %s unread=[%"T_ARTNUM_PFMT"] count=[%"T_ARTNUM_PFMT"]", grp->name, grp->newsrc.num_unread, grp->count);
529 my_flush();
530 }
531#endif /* DEBUG */
532 grp->newsrc.num_unread = grp->count;
533 }
534
535 if (grp->xmin != oldmin || grp->xmax != oldmax) {
536 expand_bitmap(grp, 0);
537#ifdef DEBUG
538 if (debug & DEBUG_NEWSRC) { /* TODO: is this the right debug-level? */
539 my_printf(cCRLF "Min/Max DIFF %s old=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] new=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"]", grp->name, oldmin, oldmax, grp->xmin, grp->xmax);
540 my_flush();
541 }
542#endif /* DEBUG */
543 }
544}
545
546
547/*
548 * Subscribe/unsubscribe to a group in .newsrc.
549 * This involves rewriting the .newsrc with the new info
550 * If get_info is set we are allowed to issue NNTP commands if needed
551 */
552void
554 struct t_group *group,
555 int sub_state,
556 t_bool get_info)
557{
558 FILE *fp;
559 FILE *newfp;
560 char *line;
561 char *seq;
562 int sub;
563 t_bool found = FALSE;
564
565 if (no_write)
566 return;
567
568 if ((newfp = fopen(newnewsrc, "w")) == NULL)
569 return;
570
571 if (newsrc_mode)
572#ifdef HAVE_FCHMOD
573 fchmod(fileno(newfp), newsrc_mode);
574#else
575# ifdef HAVE_CHMOD
576 chmod(newnewsrc, newsrc_mode);
577# endif /* HAVE_CHMOD */
578#endif /* HAVE_FCHMOD */
579
580 if ((fp = fopen(newsrc, "r")) != NULL) {
581 while ((line = tin_fgets(fp, FALSE)) != NULL) {
582 if ((seq = parse_newsrc_line(line, &sub))) {
583 if (STRCMPEQ(line, group->name)) {
584 fprintf(newfp, "%s%c %s\n", line, sub_state, seq);
585 group->subscribed = SUB_BOOL(sub_state);
586
587 /* If previously subscribed to in .newsrc, load up any existing information */
588 if (sub_state == SUBSCRIBED)
589 parse_bitmap_seq(group, seq);
590
591 found = TRUE;
592 } else
593 fprintf(newfp, "%s%c %s\n", line, sub, seq);
594 }
595 }
596
597 fclose(fp);
598
599 if (!found) {
601 group->subscribed = SUB_BOOL(sub_state);
602 if (sub_state == SUBSCRIBED) {
603 fprintf(newfp, "%s%c ", group->name, sub_state);
604 if (get_info) {
605 get_subscribe_info(group);
606 print_bitmap_seq(newfp, group);
607 } else /* we are not allowed to issue NNTP cmds during AUTOSUBSCRIBE loop */
608 fprintf(newfp, "1\n");
609 } else
610 fprintf(newfp, "%s%c\n", group->name, sub_state);
611 }
612 }
613
614 if ((sub = ferror(newfp)) || fclose(newfp)) {
616 if (sub) {
617 clearerr(newfp);
618 fclose(newfp);
619 }
621 } else
623}
624
625
626void
628 void)
629{
630 FILE *fp;
631 FILE *newfp;
632 char *line;
633 int sub, i;
634
635 if (!no_write && (newfp = fopen(newnewsrc, "w")) != NULL) {
636 if (newsrc_mode)
637#ifdef HAVE_FCHMOD
638 fchmod(fileno(newfp), newsrc_mode);
639#else
640# ifdef HAVE_CHMOD
641 chmod(newnewsrc, newsrc_mode);
642# endif /* HAVE_CHMOD */
643#endif /* HAVE_FCHMOD */
644
645 if ((fp = fopen(newsrc, "r")) != NULL) {
646 while ((line = tin_fgets(fp, FALSE)) != NULL) {
647 (void) parse_newsrc_line(line, &sub);
648 fprintf(newfp, "%s%c\n", line, sub);
649 }
650 fclose(fp);
651 }
652 if ((sub = ferror(newfp)) || fclose(newfp)) {
654 if (sub) {
655 clearerr(newfp);
656 fclose(newfp);
657 }
659 } else
661 }
662
663 for (i = 0; i < selmenu.max; i++)
665}
666
667
668/*
669 * Rewrite the newsrc file, without the specified group
670 */
671void
673 char *group)
674{
675 FILE *fp;
676 FILE *newfp;
677 char *line;
678 char *seq;
679 int sub;
680
681 if (no_write)
682 return;
683
684 if ((newfp = fopen(newnewsrc, "w")) != NULL) {
685 if (newsrc_mode)
686#ifdef HAVE_FCHMOD
687 fchmod(fileno(newfp), newsrc_mode);
688#else
689# ifdef HAVE_CHMOD
690 chmod(newnewsrc, newsrc_mode);
691# endif /* HAVE_CHMOD */
692#endif /* HAVE_FCHMOD */
693
694 if ((fp = fopen(newsrc, "r")) != NULL) {
695 while ((line = tin_fgets(fp, FALSE)) != NULL) {
696 if ((seq = parse_newsrc_line(line, &sub))) {
697 if (!STRCMPEQ(line, group))
698 fprintf(newfp, "%s%c %s\n", line, sub, seq);
699 }
700 }
701 fclose(fp);
702 }
703
704 if ((sub = ferror(newfp)) || fclose(newfp)) {
706 if (sub) {
707 clearerr(newfp);
708 fclose(newfp);
709 }
711 } else
713 }
714}
715
716
717/*
718 * Mark a group as read
719 * If art != NULL then we explicitly process each article thus
720 * catching crossposts as well, otherwise we simply scrub the
721 * bitmap and adjust the highwater mark.
722 */
723void
725 struct t_group *group,
726 struct t_article *art)
727{
728 int i;
729
730#ifdef DEBUG
731 if (debug & DEBUG_NEWSRC)
732 debug_print_comment("c/C command");
733#endif /* DEBUG */
734
735 if (art != NULL) {
736 for_each_art(i)
737 art_mark(group, &art[i], ART_READ);
738 } else {
739 FreeAndNull(group->newsrc.xbitmap);
740 group->newsrc.xbitlen = 0;
741 if (group->xmax > group->newsrc.xmax)
742 group->newsrc.xmax = group->xmax;
743 group->newsrc.xmin = group->newsrc.xmax + 1;
744 group->newsrc.num_unread = 0;
745 }
746}
747
748
749void
751 struct t_group *group)
752{
753 t_artnum bitlength;
754 t_bitmap *newbitmap = (t_bitmap *) 0;
755
756#ifdef DEBUG
757 if (debug & DEBUG_NEWSRC)
758 debug_print_comment("Z command");
759#endif /* DEBUG */
760
761 group_get_art_info(group->spooldir, group->name, group->type, &group->count, &group->xmax, &group->xmin);
762
763 group->newsrc.num_unread = group->count;
764 if (group->xmax > group->newsrc.xmax)
765 group->newsrc.xmax = group->xmax;
766 if (group->xmin > 0)
767 group->newsrc.xmin = group->xmin;
768
769 bitlength = MAX(0, group->newsrc.xmax - group->newsrc.xmin + 1);
770
771 if (bitlength > 0)
772 newbitmap = my_malloc(BITS_TO_BYTES(bitlength));
773
775 group->newsrc.xbitmap = newbitmap;
776 group->newsrc.xbitlen = bitlength;
777
778 if (bitlength)
779 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), bitlength - T_ARTNUM_CONST(1));
780
781#ifdef DEBUG
782 if (debug & DEBUG_NEWSRC)
783 debug_print_bitmap(group, NULL);
784#endif /* DEBUG */
785}
786
787
788void
790 struct t_group *group,
791 long thread)
792{
793 int i;
794
795#ifdef DEBUG
796 if (debug & DEBUG_NEWSRC)
797 debug_print_comment("Mark thread read K command");
798#endif /* DEBUG */
799
800 for (i = (int) thread; i >= 0; i = arts[i].thread)
801 art_mark(group, &arts[i], ART_READ);
802}
803
804
805void
807 struct t_group *group,
808 long thread)
809{
810 int i;
811
812#ifdef DEBUG
813 if (debug & DEBUG_NEWSRC)
814 debug_print_comment("Mark thread unread Z command");
815#endif /* DEBUG */
816
817 for (i = (int) thread; i >= 0; i = arts[i].thread)
818 art_mark(group, &arts[i], ART_WILL_RETURN);
819}
820
821
822/*
823 * Parse the newsrc sequence for the specified group
824 */
825static void
827 struct t_group *group,
828 char *seq)
829{
830 char *ptr;
831 t_artnum sum = T_ARTNUM_CONST(0);
832 t_artnum low = T_ARTNUM_CONST(0);
833 t_artnum high = T_ARTNUM_CONST(0);
834 t_artnum min, max;
835 t_bool gotseq = FALSE;
836
837 /*
838 * Skip possible non-numeric prefix
839 */
840 ptr = seq;
841 while (ptr && *ptr && (*ptr < '0' || *ptr > '9'))
842 ptr++;
843
844#ifdef DEBUG
845 if (debug & DEBUG_NEWSRC) {
846 char buf[NEWSRC_LINE];
847
848 snprintf(buf, sizeof(buf), "Parsing [%s%c %.*s]", group->name, SUB_CHAR(group->subscribed), (int) (NEWSRC_LINE - strlen(group->name) - 14), BlankIfNull(ptr));
849 debug_print_comment(buf);
850 debug_print_bitmap(group, NULL);
851 }
852#endif /* DEBUG */
853
854 if (ptr) {
855 gotseq = TRUE;
856 ptr = parse_get_seq(ptr, &low, &high);
857
858 if (high < group->xmin - 1)
859 high = group->xmin - 1;
860
861 min = ((low <= 1) ? (high + 1) : 1);
862
863 if (group->xmin > min)
864 min = group->xmin;
865
866 if (group->xmax > high)
867 max = group->xmax;
868 else
869 max = high; /* trust newsrc's max */
870
871 FreeAndNull(group->newsrc.xbitmap);
872 group->newsrc.xmax = max;
873 group->newsrc.xmin = min;
874 group->newsrc.xbitlen = (max - min) + 1;
875 if (group->newsrc.xbitlen > 0) {
878 }
879
880 if (min <= high) {
881 if (low > min)
882 sum = low - min;
883 else
884 low = min;
885 NSETRNG0(group->newsrc.xbitmap, low - min, high - min);
886 }
887
888 /*
889 * Pick up any additional articles/ranges after the first
890 */
891 while (*ptr)
892 ptr = parse_subseq(group, ptr, &low, &high, &sum);
893 } else {
894 FreeAndNull(group->newsrc.xbitmap);
895 group->newsrc.xmax = group->xmax;
896 if (group->xmin > 0)
897 group->newsrc.xmin = group->xmin;
898 else
899 group->newsrc.xmin = 1;
900 group->newsrc.xbitlen = (group->newsrc.xmax - group->newsrc.xmin) + 1;
901 if (group->newsrc.xbitlen > 0) {
904 }
905/*
906wait_message(2, "BITMAP Grp=[%s] MinMax=[%"T_ARTNUM_PFMT"-%"T_ARTNUM_PFMT"] Len=[%"T_ARTNUM_PFMT"]\n",
907 group->name, group->xmin, group->xmax, group->newsrc.xbitlen);
908*/
909 }
910
911 group->newsrc.present = TRUE;
912
913 if (gotseq) {
914 if (group->newsrc.xmax > high)
915 sum += group->newsrc.xmax - high;
916 } else
917 sum = (group->count >= 0) ? group->count : ((group->newsrc.xmax - group->newsrc.xmin) + 1);
918
919 group->newsrc.num_unread = sum;
920#ifdef DEBUG
921 if (debug & DEBUG_NEWSRC)
922 debug_print_bitmap(group, NULL);
923#endif /* DEBUG */
924}
925
926
927/*
928 * Parse a subsection of the newsrc sequencer ie., 1-34,23-90,93,97-99
929 * would parse the sequence if called in a loop in the following way:
930 * 1st call would parse 1-34 and return 23-90,93,97-99
931 * 2nd call would parse 23-90 and return 93,97-99
932 * 3rd call would parse 93 and return 97-99
933 * 4th call would parse 97-99 and return NULL
934 */
935static char *
937 struct t_group *group,
938 char *seq,
939 t_artnum *low,
940 t_artnum *high,
941 t_artnum *sum)
942{
943 t_artnum bitmin;
944 t_artnum bitmax;
945 t_artnum last_high = *high;
946
947 seq = parse_get_seq(seq, low, high);
948
949 /*
950 * Bitmap index
951 */
952 bitmin = *low - group->newsrc.xmin;
953
954 /*
955 * check that seq is not out of order
956 */
957 if (*low > last_high)
958 *sum += (*low - last_high) - 1;
959
960 if (*high == *low) {
961 if (bitmin >= 0) {
962 if (*high > group->newsrc.xmax) {
963 /* We trust .newsrc's max. */
964 t_artnum bitlen;
965 t_bitmap *newbitmap;
966
967 group->newsrc.xmax = *high;
968 bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
969 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
970
971 /* Copy over old bitmap */
972 memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
973
974 /* Mark high numbered articles as unread */
975 NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
976
977 free(group->newsrc.xbitmap);
978 group->newsrc.xbitmap = newbitmap;
979 group->newsrc.xbitlen = bitlen;
980 }
981 NSET0(group->newsrc.xbitmap, bitmin);
982 }
983 } else if ((*low < *high) && (*high >= group->newsrc.xmin)) {
984 /*
985 * Restrict the range to min..max
986 */
987 if (bitmin < 0)
988 bitmin = 0;
989
990 bitmax = *high;
991
992 if (bitmax > group->newsrc.xmax) {
993 /* We trust .newsrc's max. */
994 t_artnum bitlen;
995 t_bitmap *newbitmap;
996
997 group->newsrc.xmax = bitmax;
998 bitlen = group->newsrc.xmax - group->newsrc.xmin + 1;
999 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1000
1001 /* Copy over old bitmap */
1002 memcpy(newbitmap, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
1003
1004 /* Mark high numbered articles as unread */
1005 NSETRNG1(newbitmap, group->newsrc.xbitlen, bitlen - 1);
1006
1007 free(group->newsrc.xbitmap);
1008 group->newsrc.xbitmap = newbitmap;
1009 group->newsrc.xbitlen = bitlen;
1010 }
1011
1012 bitmax -= group->newsrc.xmin;
1013
1014 /*
1015 * Fill in the whole range as read
1016 */
1017 NSETRNG0(group->newsrc.xbitmap, bitmin, bitmax);
1018 }
1019 return seq;
1020}
1021
1022
1023static char *
1025 char *seq,
1026 t_artnum *low,
1027 t_artnum *high)
1028{
1029 *low = strtoartnum(seq, &seq, 10);
1030
1031 if (*seq == '-') { /* Range of articles */
1032 seq++;
1033 *high = strtoartnum(seq, &seq, 10);
1034 } else /* Single article */
1035 *high = *low;
1036
1037 while (*seq && (*seq < '0' || *seq > '9'))
1038 seq++;
1039
1040 return seq;
1041}
1042
1043
1044/*
1045 * Loop through arts[] array marking state of each article READ/UNREAD
1046 */
1047void
1049 struct t_group *group,
1050 t_artnum min)
1051{
1052 int i;
1053 t_artnum unread = T_ARTNUM_CONST(0);
1054 t_artnum bitmin, bitmax;
1055 t_bitmap *newbitmap = (t_bitmap *) 0;
1056
1057 bitmin = group->newsrc.xmin;
1058 bitmax = group->newsrc.xmax;
1059
1060 /*
1061 * TODO
1062 * what about group->newsrc.xmax > group->xmax?
1063 * that should indicate an artnum 'reset' on the server
1064 * (or using the "wrong" newsrc for that server)
1065 */
1066
1067 if (group->xmax > group->newsrc.xmax)
1068 group->newsrc.xmax = group->xmax;
1069
1070 if (group->newsrc.xmax >= bitmin) {
1071 newbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xmax - bitmin + 1));
1072 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmax - bitmin);
1073 }
1074
1075 /*
1076 * if getart_limit > 0 preserve read/unread state
1077 * of all articles below the new minimum
1078 */
1079 if (min > 0 && newbitmap) {
1080 t_artnum j, tmp_bitmax;
1081
1082 tmp_bitmax = (bitmax < min) ? bitmax : min;
1083 for (j = bitmin; j < tmp_bitmax; j++) {
1084 if (NTEST(group->newsrc.xbitmap, j - bitmin) != ART_READ)
1085 NSET1(newbitmap, j - bitmin);
1086 }
1087 while (j < min) {
1088 NSET1(newbitmap, j - bitmin);
1089 j++;
1090 }
1091 }
1092
1093 for_each_art(i) {
1094 if (arts[i].artnum < bitmin)
1095 arts[i].status = ART_READ;
1096 else if (arts[i].artnum > bitmax)
1097 arts[i].status = ART_UNREAD;
1098 else if (NTEST(group->newsrc.xbitmap, arts[i].artnum - bitmin) == ART_READ)
1099 arts[i].status = ART_READ;
1100 else
1101 arts[i].status = ART_UNREAD;
1102
1103 /* TODO: logic correct? */
1104 if (newbitmap != NULL && arts[i].status == ART_UNREAD && arts[i].artnum >= bitmin) {
1105#if 0
1106 /*
1107 * check for wrong article numbers in the overview
1108 *
1109 * TODO: check disabled as we currently catch the artnum > high_mark
1110 * case in read_overview() where we might be able to
1111 * fix the broken artnum (via xref:-parsing). currently
1112 * we just skip the art there.
1113 */
1114 if (arts[i].artnum <= group->xmax)
1115#endif /* 0 */
1116 NSET1(newbitmap, arts[i].artnum - bitmin);
1117 unread++;
1118 }
1119 }
1120
1121 group->newsrc.xbitlen = group->newsrc.xmax - bitmin + 1;
1122
1123 FreeIfNeeded(group->newsrc.xbitmap);
1124
1125 group->newsrc.xbitmap = newbitmap;
1126 group->newsrc.num_unread = unread;
1127}
1128
1129
1130static void
1132 FILE *fp,
1133 struct t_group *group)
1134{
1135 t_artnum artnum;
1136 t_artnum i;
1137 t_bool flag = FALSE;
1138
1139#ifdef DEBUG
1140 if (debug & DEBUG_NEWSRC) {
1141 debug_print_comment("print_bitmap_seq()");
1142 debug_print_bitmap(group, NULL);
1143 }
1144#endif /* DEBUG */
1145
1146 if (group->count == 0 || group->xmin > group->xmax) {
1147 if (group->newsrc.xmax > 1)
1148 fprintf(fp, "1-%"T_ARTNUM_PFMT, group->newsrc.xmax);
1149
1150 fprintf(fp, "\n");
1151 fflush(fp);
1152#ifdef DEBUG
1153 if (debug & DEBUG_NEWSRC)
1154 debug_print_comment("print_bitmap_seq(): group->count == 0");
1155#endif /* DEBUG */
1156 return;
1157 }
1158
1159 i = group->newsrc.xmin;
1160 if (i <= group->newsrc.xmax) {
1161 forever {
1162 if (group->newsrc.xbitmap && NTEST(group->newsrc.xbitmap, i - group->newsrc.xmin) == ART_READ) {
1163 if (flag) {
1164 artnum = i;
1165 fprintf(fp, ",%"T_ARTNUM_PFMT, i);
1166 } else {
1167 artnum = 1;
1168 flag = TRUE;
1169 fprintf(fp, "1");
1170 }
1171 while (i < group->newsrc.xmax && NTEST(group->newsrc.xbitmap, (i + 1) - group->newsrc.xmin) == ART_READ)
1172 i++;
1173
1174 if (artnum != i)
1175 fprintf(fp, "-%"T_ARTNUM_PFMT, i);
1176
1177 } else if (!flag) {
1178 flag = TRUE;
1179 if (group->newsrc.xmin > 1) {
1180 fprintf(fp, "1");
1181
1182 if (group->newsrc.xmin > 2)
1183 fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
1184
1185 }
1186 }
1187 if (group->newsrc.xmax == i)
1188 break;
1189
1190 i++;
1191 }
1192 }
1193
1194 if (!flag && group->newsrc.xmin > 1) {
1195 fprintf(fp, "1");
1196
1197 if (group->newsrc.xmin > 2)
1198 fprintf(fp, "-%"T_ARTNUM_PFMT, group->newsrc.xmin - 1);
1199
1200#ifdef DEBUG
1201 if (debug & DEBUG_NEWSRC)
1202 debug_print_comment("print_bitmap_seq(): !flag && group->newsrc.xmin > 1");
1203#endif /* DEBUG */
1204 }
1205
1206 fprintf(fp, "\n");
1207 fflush(fp);
1208}
1209
1210
1211/*
1212 * rewrite .newsrc and position group at specified position
1213 */
1214t_bool
1216 struct t_group *group,
1217 int pos)
1218{
1219 FILE *fp_in = NULL, *fp_out = NULL;
1220 FILE *fp_sub = NULL, *fp_unsub = NULL;
1221 char *newsgroup = NULL;
1222 char *line;
1223 char filename[PATH_LEN];
1224 char sub[PATH_LEN];
1225 char unsub[PATH_LEN];
1226 int subscribed_pos = 1;
1227 int err;
1228 size_t group_len;
1229 t_bool found = FALSE;
1230 t_bool newnewsrc_created = FALSE;
1231 t_bool option_line = FALSE;
1232 t_bool repositioned = FALSE;
1234 t_bool sub_created = FALSE;
1235 t_bool unsub_created = FALSE;
1236 t_bool fs_error = FALSE;
1237
1238 if (no_write)
1239 goto rewrite_group_done;
1240
1241 if ((fp_in = fopen(newsrc, "r")) == NULL)
1242 goto rewrite_group_done;
1243
1244 if ((fp_out = fopen(newnewsrc, "w")) == NULL)
1245 goto rewrite_group_done;
1246
1247 newnewsrc_created = TRUE;
1248
1249 if (newsrc_mode)
1250#ifdef HAVE_FCHMOD
1251 fchmod(fileno(fp_out), newsrc_mode);
1252#else
1253# ifdef HAVE_CHMOD
1254 chmod(newnewsrc, newsrc_mode);
1255# endif /* HAVE_CHMOD */
1256#endif /* HAVE_FCHMOD */
1257
1258 joinpath(filename, sizeof(filename), TMPDIR, ".subrc");
1259 snprintf(sub, sizeof(sub), "%s.%ld", filename, (long) process_id);
1260
1261 joinpath(filename, sizeof(filename), TMPDIR, ".unsubrc");
1262 snprintf(unsub, sizeof(unsub), "%s.%ld", filename, (long) process_id);
1263
1264 if ((fp_sub = fopen(sub, "w")) == NULL)
1265 goto rewrite_group_done;
1266
1267 sub_created = TRUE;
1268
1269 if ((fp_unsub = fopen(unsub, "w")) == NULL)
1270 goto rewrite_group_done;
1271
1272 unsub_created = TRUE;
1273
1274 /*
1275 * split newsrc into subscribed and unsubscribed to files
1276 */
1277 group_len = strlen(group->name);
1278
1279 while ((line = tin_fgets(fp_in, FALSE)) != NULL) {
1280 if (STRNCMPEQ(group->name, line, group_len) && line[group_len] == SUBSCRIBED) {
1281 FreeIfNeeded(newsgroup);
1282 newsgroup = my_strdup(line); /* Take a copy of this line */
1283 found = TRUE;
1284 continue;
1285 } else if (strchr(line, SUBSCRIBED) != NULL) {
1286 write_newsrc_line(fp_sub, line);
1287 } else if (strchr(line, UNSUBSCRIBED) != NULL) {
1288 write_newsrc_line(fp_unsub, line);
1289 } else { /* options line at beginning of .newsrc */
1290 fprintf(fp_sub, "%s\n", line);
1291 option_line = TRUE;
1292 }
1293 }
1294
1295 if ((err = ferror(fp_sub)) || fclose(fp_sub)) {
1297 if (err) {
1298 clearerr(fp_sub);
1299 fclose(fp_sub);
1300 }
1301 fs_error = TRUE;
1302 }
1303 if ((err = ferror(fp_unsub)) || fclose(fp_unsub)) {
1304 if (!fs_error) /* avoid repeatd error message */
1306 if (err) {
1307 clearerr(fp_unsub);
1308 fclose(fp_unsub);
1309 }
1310 fs_error = TRUE;
1311 }
1312 fp_sub = fp_unsub = NULL;
1313
1314 if (fs_error)
1315 goto rewrite_group_done;
1316
1317 fclose(fp_in);
1318 fp_in = NULL;
1319
1320 /*
1321 * The group to be moved cannot be found, so give up now
1322 */
1323 if (!found)
1324 goto rewrite_group_done;
1325
1326 /*
1327 * write subscribed groups & repositioned group to newnewsrc
1328 */
1329 if ((fp_sub = fopen(sub, "r")) == NULL)
1330 goto rewrite_group_done;
1331
1332 while ((line = tin_fgets(fp_sub, FALSE)) != NULL) {
1333 if (option_line) {
1334 if (strchr(line, SUBSCRIBED) == NULL && strchr(line, UNSUBSCRIBED) == NULL) {
1335 fprintf(fp_out, "%s\n", line);
1336 continue;
1337 } else
1338 option_line = FALSE;
1339 }
1340
1341 if (pos == subscribed_pos) {
1342 write_newsrc_line(fp_out, newsgroup);
1343 repositioned = TRUE;
1344 }
1345
1346 fprintf(fp_out, "%s\n", line);
1347
1348 subscribed_pos++;
1349 }
1350
1351 if (!repositioned)
1352 write_newsrc_line(fp_out, newsgroup);
1353
1354 /*
1355 * append unsubscribed groups file to newnewsrc
1356 */
1357 if ((fp_unsub = fopen(unsub, "r")) == NULL)
1358 goto rewrite_group_done;
1359
1360 while ((line = tin_fgets(fp_unsub, FALSE)) != NULL)
1361 fprintf(fp_out, "%s\n", line);
1362
1363 /*
1364 * Try and cleanly close out the newnewsrc file
1365 */
1366 if ((err = ferror(fp_out)) || fclose(fp_out)) {
1368 if (err) {
1369 clearerr(fp_out);
1370 fclose(fp_out);
1371 }
1372 } else {
1374 ret_code = TRUE;
1375 }
1376 fp_out = NULL;
1377 newnewsrc_created = FALSE;
1378
1379rewrite_group_done:
1380 if (fp_in != NULL)
1381 fclose(fp_in);
1382
1383 if (fp_out != NULL)
1384 fclose(fp_out);
1385
1386 if (fp_sub != NULL)
1387 fclose(fp_sub);
1388
1389 if (fp_unsub != NULL)
1390 fclose(fp_unsub);
1391
1392 if (newnewsrc_created)
1394
1395 if (sub_created)
1396 unlink(sub);
1397
1398 if (unsub_created)
1399 unlink(unsub);
1400
1401 FreeIfNeeded(newsgroup);
1402
1403 return ret_code;
1404}
1405
1406
1407/*
1408 * catchup all groups in .newsrc
1409 */
1410void
1412 void)
1413{
1414 int i;
1415 struct t_group *group;
1416
1417 for (i = 0; i < selmenu.max; i++) {
1418 group = &active[my_group[i]];
1419 group->newsrc.present = TRUE;
1420 FreeAndNull(group->newsrc.xbitmap);
1421 if (group->xmax > group->newsrc.xmax)
1422 group->newsrc.xmax = group->xmax;
1423 group->newsrc.xmin = group->newsrc.xmax + 1;
1424 group->newsrc.num_unread = 0;
1425 group->newsrc.xbitlen = 0;
1426 }
1427}
1428
1429
1430/*
1431 * Break down a line of .newsrc file
1432 * The sequence information [ eg; 1-3,10,12 ] is returned, line is truncated to
1433 * just the group name and the subscription flag is copied to sub.
1434 */
1435static char *
1437 char *line,
1438 int *sub)
1439{
1440 char *ptr, *tmp;
1441
1442 *sub = UNSUBSCRIBED; /* Default to no entry */
1443
1444 if ((ptr = strpbrk(line, "!:")) == NULL) /* space|SUBSCRIBED|UNSUBSCRIBED */
1445 return NULL;
1446
1447 *sub = *ptr; /* Save the subscription status */
1448 tmp = ptr; /* Keep this blank for later */
1449 *(ptr++) = '\0'; /* Terminate the group name */
1450
1451#if 0
1452 if (ptr == NULL) /* No seq info, so return a blank */
1453 return tmp;
1454#endif /* 0 */
1455
1456 if ((ptr = strpbrk(ptr, " \t")) == NULL)
1457 return tmp;
1458
1459 return (ptr + 1); /* Return pointer to sequence info. At worst this will be \0 */
1460}
1461
1462
1463/*
1464 * expand group->newsrc information if group->xmax is larger than
1465 * group->newsrc.xmax or min is smaller than group->newsrc.xmin.
1466 */
1467void
1469 struct t_group *group,
1470 t_artnum min)
1471{
1472 t_artnum bitlen;
1473 t_artnum first;
1474 t_artnum tmp;
1475 t_artnum max;
1476 t_bool need_full_copy = FALSE;
1477
1478 /* calculate new max */
1479 if (group->newsrc.xmax > group->xmax)
1480 max = group->newsrc.xmax;
1481 else
1482 max = group->xmax;
1483
1484 /* adjust min */
1485 if (!min)
1486 min = group->newsrc.xmin;
1487
1488 /* calculate first */
1489 if (min >= group->newsrc.xmin)
1490 first = group->newsrc.xmin;
1491 else
1492 first = group->newsrc.xmin - ((group->newsrc.xmin - min + (NBITS - 1)) & ~(NBITS - 1));
1493
1494 /* adjust first */
1495 if (group->newsrc.xmax < first - 1)
1496 first = first - ((first - (group->newsrc.xmax + 1) + (NBITS - 1)) & ~(NBITS - 1));
1497
1498 /* check first */
1499 if (first < 1) {
1500 need_full_copy = TRUE;
1501 first = 1;
1502 }
1503
1504 bitlen = max - first + 1;
1505
1506 if (bitlen <= 0) {
1507 bitlen = 0;
1508 FreeIfNeeded(group->newsrc.xbitmap);
1509 group->newsrc.xbitmap = (t_bitmap *) 0;
1510#ifdef DEBUG
1511 if (debug & DEBUG_NEWSRC)
1512 debug_print_comment("expand_bitmap: group->newsrc.bitlen == 0");
1513#endif /* DEBUG */
1514 } else if (group->newsrc.xbitmap == NULL) {
1515 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1516 if (group->newsrc.xmin > first)
1517 NSETRNG0(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1518 if (bitlen > group->newsrc.xmin - first)
1519 NSETRNG1(group->newsrc.xbitmap, group->newsrc.xmin - first, bitlen - 1);
1520#ifdef DEBUG
1521 if (debug & DEBUG_NEWSRC)
1522 debug_print_comment("expand_bitmap: group->newsrc.xbitmap == NULL");
1523#endif /* DEBUG */
1524 } else if (need_full_copy) {
1525 t_bitmap *newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1526
1527 /* Copy over old bitmap */
1528 /* TODO: change to use shift */
1529 for (tmp = group->newsrc.xmin; tmp <= group->newsrc.xmax; tmp++) {
1530 if (NTEST(group->newsrc.xbitmap, tmp - group->newsrc.xmin) == ART_READ)
1531 NSET0(newbitmap, tmp - first);
1532 else
1533 NSET1(newbitmap, tmp - first);
1534 }
1535
1536 /* Mark earlier articles as read, updating num_unread */
1537
1538 if (first < group->newsrc.xmin) {
1539 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1540 }
1541
1542 for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
1543 if (NTEST(newbitmap, tmp - first) != ART_READ) {
1544 NSET0(newbitmap, tmp - first);
1545 if (group->newsrc.num_unread)
1546 group->newsrc.num_unread--;
1547 }
1548 }
1549
1550 /* Mark high numbered articles as unread */
1551
1552 if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
1553 tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
1554 NSETRNG1(newbitmap, tmp, bitlen - 1);
1555 }
1556
1557 free(group->newsrc.xbitmap);
1558 group->newsrc.xbitmap = newbitmap;
1559#ifdef DEBUG
1560 if (debug & DEBUG_NEWSRC)
1561 debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1 and need full copy");
1562#endif /* DEBUG */
1563 } else if (max != group->newsrc.xmax || first != group->newsrc.xmin) {
1564 t_bitmap *newbitmap;
1565 newbitmap = my_malloc(BITS_TO_BYTES(bitlen));
1566
1567 /* Copy over old bitmap */
1568
1569 assert((group->newsrc.xmin - first) / NBITS + BITS_TO_BYTES(group->newsrc.xbitlen) <= BITS_TO_BYTES(bitlen));
1570
1571 memcpy(newbitmap + (group->newsrc.xmin - first) / NBITS, group->newsrc.xbitmap, BITS_TO_BYTES(group->newsrc.xbitlen));
1572
1573 /* Mark earlier articles as read, updating num_unread */
1574
1575 if (first < group->newsrc.xmin) {
1576 NSETRNG0(newbitmap, T_ARTNUM_CONST(0), group->newsrc.xmin - first - T_ARTNUM_CONST(1));
1577 }
1578
1579 for (tmp = group->newsrc.xmin; tmp < min; tmp++) {
1580 if (NTEST(newbitmap, tmp - first) != ART_READ) {
1581 NSET0(newbitmap, tmp - first);
1582 if (group->newsrc.num_unread)
1583 group->newsrc.num_unread--;
1584 }
1585 }
1586
1587 /* Mark high numbered articles as unread */
1588
1589 if (group->newsrc.xmin - first + group->newsrc.xbitlen < bitlen) {
1590 tmp = group->newsrc.xmin - first + group->newsrc.xbitlen;
1591 NSETRNG1(newbitmap, tmp, bitlen - 1);
1592 }
1593
1594 free(group->newsrc.xbitmap);
1595 group->newsrc.xbitmap = newbitmap;
1596#ifdef DEBUG
1597 if (debug & DEBUG_NEWSRC)
1598 debug_print_comment("expand_bitmap: group->newsrc.bitlen != (group->max-group->min)+1");
1599#endif /* DEBUG */
1600 }
1601 group->newsrc.xmin = first;
1602 if (group->newsrc.xmax < max)
1603 group->newsrc.num_unread += max - group->newsrc.xmax;
1604 group->newsrc.xmax = max;
1605 group->newsrc.xbitlen = bitlen;
1606 group->newsrc.present = TRUE;
1607}
1608
1609
1610void
1612 struct t_group *group,
1613 struct t_article *art,
1614 int flag)
1615{
1616 if (art == NULL)
1617 return;
1618
1619 switch (flag) {
1620 case ART_READ:
1621 if (group != NULL) {
1622 if (art->artnum >= group->newsrc.xmin && art->artnum <= group->newsrc.xmax)
1623 NSET0(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
1624#ifdef DEBUG
1625 if (debug & DEBUG_NEWSRC)
1626 debug_print_bitmap(group, art);
1627#endif /* DEBUG */
1628 }
1629 if ((art->status == ART_UNREAD) || (art->status == ART_WILL_RETURN)) {
1631
1632 if (group != NULL) {
1633 if (group->newsrc.num_unread)
1634 group->newsrc.num_unread--;
1635
1636 if (group->attribute->show_only_unread_arts)
1637 art->keep_in_base = TRUE;
1638 }
1639
1640 art->status = ART_READ;
1641 }
1642 break;
1643
1644 case ART_UNREAD:
1645 case ART_WILL_RETURN:
1646 if (art->status == ART_READ) {
1647 if (group != NULL) {
1648 group->newsrc.num_unread++;
1649
1650 if (group->attribute->show_only_unread_arts)
1651 art->keep_in_base = FALSE;
1652 }
1653
1654 art->status = flag;
1655 }
1656 if (group != NULL) {
1657 if (art->artnum < group->newsrc.xmin)
1658 expand_bitmap(group, art->artnum);
1659 else {
1660 NSET1(group->newsrc.xbitmap, art->artnum - group->newsrc.xmin);
1661#ifdef DEBUG
1662 if (debug & DEBUG_NEWSRC)
1663 debug_print_bitmap(group, art);
1664#endif /* DEBUG */
1665 }
1666 }
1667 break;
1668
1669 default:
1670 break;
1671 }
1672}
1673
1674
1675void
1677 struct t_group *group)
1678{
1679 if (group != NULL) {
1680 group->newsrc.num_unread = 0;
1681 group->newsrc.present = FALSE;
1682
1683 FreeIfNeeded(group->newsrc.xbitmap);
1684
1685 group->newsrc.xbitmap = (t_bitmap *) 0;
1686 group->newsrc.xbitlen = 0;
1687 if (group->xmin > 0)
1688 group->newsrc.xmin = group->xmin;
1689 else
1690 group->newsrc.xmin = 1;
1691 group->newsrc.xmax = group->newsrc.xmin - 1;
1692 }
1693}
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
static t_openartinfo * art
Definition: cook.c:78
#define DEBUG_NEWSRC
Definition: debug.h:50
#define DEBUG_NNTP
Definition: debug.h:47
int verbose
Definition: init.c:154
constext txt_subscribing[]
Definition: lang.c:864
constext txt_autosubscribing_groups[]
Definition: lang.c:107
constext txt_filesystem_full_backup[]
Definition: lang.c:276
int * my_group
Definition: memory.c:64
struct t_capabilities nntp_caps
Definition: init.c:513
char homedir[PATH_LEN]
Definition: init.c:78
char * nntp_server
Definition: nntplib.c:28
t_bool cmd_line
Definition: init.c:129
t_bool read_saved_news
Definition: init.c:152
t_menu selmenu
Definition: select.c:84
pid_t process_id
Definition: init.c:125
constext txt_remove_bogus[]
Definition: lang.c:788
struct t_article * arts
Definition: memory.c:69
char newnewsrc[PATH_LEN]
Definition: init.c:93
constext txt_filesystem_full[]
Definition: lang.c:275
constext txt_creating_newsrc[]
Definition: lang.c:159
char rcdir[PATH_LEN]
Definition: init.c:100
char newsrc[PATH_LEN]
Definition: init.c:96
constext txt_reading_newsrc[]
Definition: lang.c:783
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
char subscriptions_file[PATH_LEN]
Definition: init.c:104
t_bool no_write
Definition: init.c:145
t_bool read_news_via_nntp
Definition: init.c:151
constext txt_newsrc_nogroups[]
Definition: lang.c:674
struct t_group * active
Definition: memory.c:66
t_bool batch_mode
Definition: init.c:127
static int ret_code
Definition: group.c:86
static char buf[16]
Definition: langinfo.c:50
void grp_mark_read(struct t_group *group, struct t_article *art)
Definition: newsrc.c:724
t_bool pos_group_in_newsrc(struct t_group *group, int pos)
Definition: newsrc.c:1215
void parse_unread_arts(struct t_group *group, t_artnum min)
Definition: newsrc.c:1048
static FILE * open_subscription_fp(void)
Definition: newsrc.c:305
void backup_newsrc(void)
Definition: newsrc.c:388
static void get_subscribe_info(struct t_group *grp)
Definition: newsrc.c:517
void grp_mark_unread(struct t_group *group)
Definition: newsrc.c:750
void art_mark(struct t_group *group, struct t_article *art, int flag)
Definition: newsrc.c:1611
void delete_group(char *group)
Definition: newsrc.c:672
void expand_bitmap(struct t_group *group, t_artnum min)
Definition: newsrc.c:1468
static mode_t newsrc_mode
Definition: newsrc.c:54
void reset_newsrc(void)
Definition: newsrc.c:627
void catchup_newsrc_file(void)
Definition: newsrc.c:1411
void set_default_bitmap(struct t_group *group)
Definition: newsrc.c:1676
void subscribe(struct t_group *group, int sub_state, t_bool get_info)
Definition: newsrc.c:553
signed long int read_newsrc(char *newsrc_file, t_bool allgroups)
Definition: newsrc.c:83
static char * parse_get_seq(char *seq, t_artnum *low, t_artnum *high)
Definition: newsrc.c:1024
void thd_mark_unread(struct t_group *group, long thread)
Definition: newsrc.c:806
static void auto_subscribe_groups(char *newsrc_file)
Definition: newsrc.c:330
static char * parse_subseq(struct t_group *group, char *seq, t_artnum *low, t_artnum *high, t_artnum *sum)
Definition: newsrc.c:936
signed long int write_newsrc(void)
Definition: newsrc.c:201
void thd_mark_read(struct t_group *group, long thread)
Definition: newsrc.c:789
static int write_newsrc_line(FILE *fp, char *line)
Definition: newsrc.c:146
int group_get_art_info(char *tin_spooldir, char *groupname, int grouptype, t_artnum *art_count, t_artnum *art_max, t_artnum *art_min)
Definition: newsrc.c:423
static void parse_bitmap_seq(struct t_group *group, char *seq)
Definition: newsrc.c:826
static char * parse_newsrc_line(char *line, int *sub)
Definition: newsrc.c:1436
static t_bool create_newsrc(char *newsrc_file)
Definition: newsrc.c:275
static void print_bitmap_seq(FILE *fp, struct t_group *group)
Definition: newsrc.c:1131
#define NSET1(n, b)
Definition: newsrc.h:135
#define NSET0(n, b)
Definition: newsrc.h:136
#define BITS_TO_BYTES(n)
Definition: newsrc.h:141
#define NBITS
Definition: newsrc.h:112
#define NTEST(n, b)
Definition: newsrc.h:122
#define NNTP_STRLEN
Definition: nntplib.h:155
#define OK_GROUPS
Definition: nntplib.h:96
#define ERR_NOGROUP
Definition: nntplib.h:126
#define OK_GROUP
Definition: nntplib.h:95
@ CAPABILITIES
Definition: nntplib.h:171
#define ERR_ACCESS
Definition: nntplib.h:146
struct t_group * group_find(const char *group_name, t_bool ignore_case)
Definition: list.c:154
void make_base_group_path(const char *base_dir, const char *group_name, char *group_path, size_t group_path_len)
Definition: misc.c:2098
char * strpbrk(const char *str1, const char *str2)
Definition: string.c:312
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
_Noreturn void tin_done(int ret, const char *fmt,...)
Definition: misc.c:562
char * strip_line(char *line)
Definition: misc.c:3644
void art_mark_xref_read(struct t_article *art)
Definition: xref.c:332
void NSETRNG0(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:456
void joinpath(char *result, size_t result_size, const char *dir, const char *file)
Definition: joinpath.c:50
t_bool backup_file(const char *filename, const char *backupname)
Definition: misc.c:213
void NSETRNG1(t_bitmap *bitmap, t_artnum low, t_artnum high)
Definition: xref.c:422
char * quote_space_to_dash(char *str)
Definition: config.c:1729
char * my_strdup(const char *str)
Definition: string.c:139
void rename_file(const char *old_filename, const char *new_filename)
Definition: misc.c:742
int my_mkdir(char *path, mode_t mode)
Definition: misc.c:717
void clear_message(void)
Definition: screen.c:283
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
int skip_newgroups(void)
Definition: select.c:1045
t_bool process_bogus(char *name)
Definition: active.c:192
const char * name
Definition: signal.c:117
Definition: tin.h:1533
int thread
Definition: tin.h:1548
t_artnum artnum
Definition: tin.h:1534
unsigned int status
Definition: tin.h:1551
unsigned show_only_unread_arts
Definition: tin.h:1674
t_bool list_subscriptions
Definition: nntplib.h:201
enum extension_type type
Definition: nntplib.h:187
int strip_bogus
Definition: tinrc.h:163
t_bool strip_newsrc
Definition: tinrc.h:251
Definition: tin.h:1816
t_bool bogus
Definition: tin.h:1831
t_artnum xmin
Definition: tin.h:1824
t_artnum xmax
Definition: tin.h:1823
struct t_attribute * attribute
Definition: tin.h:1834
unsigned int type
Definition: tin.h:1825
t_bool subscribed
Definition: tin.h:1829
char * name
Definition: tin.h:1817
t_artnum count
Definition: tin.h:1822
char * spooldir
Definition: tin.h:1820
struct t_newsrc newsrc
Definition: tin.h:1833
int max
Definition: tin.h:2057
t_artnum xbitlen
Definition: tin.h:1809
t_bool present
Definition: tin.h:1805
t_bitmap * xbitmap
Definition: tin.h:1810
t_artnum xmax
Definition: tin.h:1807
t_artnum num_unread
Definition: tin.h:1806
t_artnum xmin
Definition: tin.h:1808
#define my_flush()
Definition: tcurses.h:177
#define my_fprintf
Definition: tcurses.h:176
#define cCRLF
Definition: tcurses.h:156
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
#define SUBSCRIBED
Definition: tin.h:1366
#define STRCMPEQ(s1, s2)
Definition: tin.h:822
long t_artnum
Definition: tin.h:229
#define TIN_FCLOSE(x)
Definition: tin.h:1048
#define SUB_CHAR(x)
Definition: tin.h:1369
#define TMPDIR
Definition: tin.h:2170
#define UNSUBSCRIBED
Definition: tin.h:1365
#define T_ARTNUM_SFMT
Definition: tin.h:231
#define for_each_group(x)
Definition: tin.h:2259
#define STRCPY(dst, src)
Definition: tin.h:820
#define for_each_art(x)
Definition: tin.h:2260
#define OLDNEWSRC_FILE
Definition: tin.h:738
#define my_malloc(size)
Definition: tin.h:2245
#define unlink(file)
Definition: tin.h:387
#define SUB_BOOL(x)
Definition: tin.h:1371
#define NNTP_ERROR_EXIT
Definition: tin.h:1306
#define atoartnum
Definition: tin.h:233
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define _(Text)
Definition: tin.h:94
#define forever
Definition: tin.h:816
#define PATH_LEN
Definition: tin.h:843
#define strtoartnum
Definition: tin.h:234
#define snprintf
Definition: tin.h:2464
#define FreeAndNull(p)
Definition: tin.h:2253
#define T_ARTNUM_CONST(v)
Definition: tin.h:232
#define ART_WILL_RETURN
Definition: tin.h:1347
#define GROUP_TYPE_NEWS
Definition: tin.h:1070
#define T_ARTNUM_PFMT
Definition: tin.h:230
unsigned char t_bitmap
Definition: tin.h:1475
#define my_group_add(x, y)
Definition: tin.h:2258
#define ART_READ
Definition: tin.h:1345
#define NEWSRC_FILE
Definition: tin.h:734
#define STRNCMPEQ(s1, s2, n)
Definition: tin.h:823
#define assert(p)
Definition: tin.h:1320
#define NEWSRC_LINE
Definition: tin.h:862
#define ART_UNREAD
Definition: tin.h:1346
#define DIR_BUF
Definition: tin.h:383
#define CLOSEDIR(DIR)
Definition: tin.h:2398
#define MAX(a, b)
Definition: tin.h:808
#define BlankIfNull(p)
Definition: tin.h:2255
#define S_IRWXU
Definition: tin.h:2184
#define BOGUS_REMOVE
Definition: tin.h:1211