"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.2/src/newsrc.c" (9 Dec 2022, 40501 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:
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 "newsrc.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.6.1_vs_2.6.2.
1 /*
2 * Project : tin - a Usenet reader
3 * Module : newsrc.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2022-02-19
7 * Notes : ArtCount = (ArtMax - ArtMin) + 1 [could have holes]
8 *
9 * Copyright (c) 1991-2023 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
54 static mode_t newsrc_mode = 0;
55
56 /*
57 * Local prototypes
58 */
59 static FILE *open_subscription_fp(void);
60 static char *parse_newsrc_line(char *line, int *sub);
61 static char *parse_subseq(struct t_group *group, char *seq, t_artnum *low, t_artnum *high, t_artnum *sum);
62 static char *parse_get_seq(char *seq, t_artnum *low, t_artnum *high);
63 static int write_newsrc_line(FILE *fp, char *line);
64 static t_bool create_newsrc(char *newsrc_file);
65 static void auto_subscribe_groups(char *newsrc_file);
66 static void get_subscribe_info(struct t_group *grp);
67 static void parse_bitmap_seq(struct t_group *group, char *seq);
68 static 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 */
82 signed long int
83 read_newsrc(
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)
94 selmenu.max = skip_newgroups();
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)
108 wait_message(0, _(txt_reading_newsrc));
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) {
121 active[my_group[i]].subscribed = SUB_BOOL(sub);
122 parse_bitmap_seq(&active[my_group[i]], seq);
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)
135 clear_message();
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 */
145 static int
146 write_newsrc_line(
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 */
160 if (tinrc.strip_bogus == BOGUS_REMOVE)
161 wait_message(2, _(txt_remove_bogus), line);
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
172 if (tinrc.strip_bogus == BOGUS_REMOVE) {
173 if (group == NULL || group->bogus) { /* group doesn't exist */
174 wait_message(2, _(txt_remove_bogus), line);
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 */
200 signed long int
201 write_newsrc(
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), ¬e_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)) {
246 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
247 unlink(newnewsrc);
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) {
259 error_message(2, _(txt_newsrc_nogroups));
260 unlink(newnewsrc);
261 return 0L; /* So we don't get prompted to try again */
262 }
263
264 if (write_ok)
265 rename_file(newnewsrc, newsrc);
266
267 return tot;
268 }
269
270
271 /*
272 * Create a newsrc from active[] groups. Subscribe to all groups.
273 */
274 static t_bool
275 create_newsrc(
276 char *newsrc_file)
277 {
278 FILE *fp;
279 int i;
280
281 if ((fp = fopen(newsrc_file, "w")) != NULL) {
282 wait_message(0, _(txt_creating_newsrc));
283
284 for_each_group(i)
285 fprintf(fp, "%s!\n", active[i].name);
286
287 if ((i = ferror(fp)) || fclose(fp)) {
288 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
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 */
304 static FILE *
305 open_subscription_fp(
306 void)
307 {
308 if (!read_saved_news) {
309 #ifdef NNTP_ABLE
310 if (read_news_via_nntp) {
311 /* RFC 6048 2.6 */
312 if (nntp_caps.type == CAPABILITIES && !nntp_caps.list_subscriptions)
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 */
329 static void
330 auto_subscribe_groups(
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)
346 wait_message(0, _(txt_autosubscribing_groups));
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)) {
373 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
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 */
387 void
388 backup_newsrc(
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 {
401 STRCPY(filebuf, quote_space_to_dash(nntp_server));
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))
413 error_message(2, _(txt_filesystem_full_backup), NEWSRC_FILE);
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 */
422 int
423 group_get_art_info(
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 */
516 static void
517 get_subscribe_info(
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 */
552 void
553 subscribe(
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) {
600 wait_message(0, _(txt_subscribing));
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)) {
615 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
616 if (sub) {
617 clearerr(newfp);
618 fclose(newfp);
619 }
620 unlink(newnewsrc);
621 } else
622 rename_file(newnewsrc, newsrc);
623 }
624
625
626 void
627 reset_newsrc(
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)) {
653 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
654 if (sub) {
655 clearerr(newfp);
656 fclose(newfp);
657 }
658 unlink(newnewsrc);
659 } else
660 rename_file(newnewsrc, newsrc);
661 }
662
663 for (i = 0; i < selmenu.max; i++)
664 set_default_bitmap(&active[my_group[i]]);
665 }
666
667
668 /*
669 * Rewrite the newsrc file, without the specified group
670 */
671 void
672 delete_group(
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)) {
705 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
706 if (sub) {
707 clearerr(newfp);
708 fclose(newfp);
709 }
710 unlink(newnewsrc);
711 } else
712 rename_file(newnewsrc, newsrc);
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 */
723 void
724 grp_mark_read(
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
749 void
750 grp_mark_unread(
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
774 FreeIfNeeded(group->newsrc.xbitmap);
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
788 void
789 thd_mark_read(
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
805 void
806 thd_mark_unread(
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 */
825 static void
826 parse_bitmap_seq(
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) {
876 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
877 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
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) {
902 group->newsrc.xbitmap = my_malloc(BITS_TO_BYTES(group->newsrc.xbitlen));
903 NSETRNG1(group->newsrc.xbitmap, T_ARTNUM_CONST(0), group->newsrc.xbitlen - T_ARTNUM_CONST(1));
904 }
905 /*
906 wait_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 */
935 static char *
936 parse_subseq(
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
1023 static char *
1024 parse_get_seq(
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 */
1047 void
1048 parse_unread_arts(
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
1130 static void
1131 print_bitmap_seq(
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 */
1214 t_bool
1215 pos_group_in_newsrc(
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;
1233 t_bool ret_code = 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)) {
1296 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
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 */
1305 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
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)) {
1367 error_message(2, _(txt_filesystem_full), NEWSRC_FILE);
1368 if (err) {
1369 clearerr(fp_out);
1370 fclose(fp_out);
1371 }
1372 } else {
1373 rename_file(newnewsrc, newsrc);
1374 ret_code = TRUE;
1375 }
1376 fp_out = NULL;
1377 newnewsrc_created = FALSE;
1378
1379 rewrite_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)
1393 unlink(newnewsrc);
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 */
1410 void
1411 catchup_newsrc_file(
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 */
1435 static char *
1436 parse_newsrc_line(
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 */
1467 void
1468 expand_bitmap(
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
1610 void
1611 art_mark(
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)) {
1630 art_mark_xref_read(art);
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
1675 void
1676 set_default_bitmap(
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 }