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