"Fossies" - the Fresh Open Source Software Archive 
Member "tin-2.6.2/src/tags.c" (9 Dec 2022, 9693 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 "tags.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 : tags.c
4 * Author : Jason Faultless <jason@altarstone.com>
5 * Created : 1999-12-06
6 * Updated : 2020-08-04
7 * Notes : Split out from other modules
8 *
9 * Copyright (c) 1999-2023 Jason Faultless <jason@altarstone.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 #ifndef TIN_H
41 # include "tin.h"
42 #endif /* !TIN_H */
43
44 /* Local prototypes */
45 static t_bool parse_range(char *range, int min, int max, int curr, int *range_start, int *range_end);
46
47 int num_of_tagged_arts = 0;
48
49 /*
50 * Tags all parts of a multipart index if base_index points
51 * to a multipart message and all its parts can be found.
52 *
53 * @param base_index points to one message in a multipart message.
54 * @return number of messages tagged, or zero on failure
55 */
56 int
57 tag_multipart(
58 int arts_index)
59 {
60 MultiPartInfo *info = NULL;
61 int i;
62 int qty;
63 t_bool untagging = FALSE;
64
65 for_each_art(i) {
66 if (!global_look_for_multipart(i, '[', ']'))
67 global_look_for_multipart(i, '(', ')');
68 }
69
70 qty = global_get_multiparts(arts_index, &info, TRUE);
71
72 /* check for failure... */
73 if (qty == 0) {
74 info_message(_(txt_info_not_multipart_message));
75 return 0;
76 }
77 if (qty < 0) {
78 info_message(_(txt_info_missing_part), -qty);
79 return 0;
80 }
81
82 /*
83 * if any are already tagged, untag 'em
84 */
85 for (i = 0; i < qty; ++i) {
86 if (arts[info[i].arts_index].tagged != 0) {
87 untagging = TRUE;
88 while (i < qty)
89 untag_article(info[i++].arts_index);
90 }
91 }
92
93 /*
94 * get_multiparts() sorts info by part number,
95 * so a simple for loop tags in the right order
96 *
97 * only tag if we are not untagging
98 */
99 if (!untagging) {
100 for (i = 0; i < qty; ++i)
101 arts[info[i].arts_index].tagged = ++num_of_tagged_arts;
102 }
103
104 free(info);
105
106 return qty;
107 }
108
109
110 /*
111 * Return the highest tag number of any article in thread
112 * rooted at base[n]
113 */
114 int
115 line_is_tagged(
116 int n)
117 {
118 int code = 0;
119
120 if (curr_group->attribute->thread_articles) {
121 int i;
122 for (i = n; i >= 0; i = arts[i].thread) {
123 if (arts[i].tagged > code)
124 code = arts[i].tagged;
125 }
126 } else
127 code = arts[n].tagged;
128
129 return code;
130 }
131
132
133 /*
134 * Toggle tag status of an article. Returns TRUE if we tagged the article
135 * FALSE if we untagged it.
136 */
137 t_bool
138 tag_article(
139 int art)
140 {
141 if (arts[art].tagged != 0) {
142 untag_article(art);
143 info_message(_(txt_prefix_untagged), txt_article_singular);
144 return FALSE;
145 } else {
146 arts[art].tagged = ++num_of_tagged_arts;
147 info_message(_(txt_prefix_tagged), txt_article_singular);
148 return TRUE;
149 }
150 }
151
152
153 /*
154 * Remove the tag from an article
155 * Work through all the threads and decrement the tag counter on all arts
156 * greater than 'tag', fixup counters
157 */
158 void
159 untag_article(
160 long art)
161 {
162 int i, j;
163
164 for (i = 0; i < grpmenu.max; ++i) {
165 for_each_art_in_thread(j, i) {
166 if (arts[j].tagged > arts[art].tagged)
167 --arts[j].tagged;
168 }
169 }
170 arts[art].tagged = 0;
171 --num_of_tagged_arts;
172 }
173
174
175 /*
176 * Clear tag status of all articles. If articles were untagged, return TRUE
177 */
178 t_bool
179 untag_all_articles(
180 void)
181 {
182 int i;
183 t_bool untagged = FALSE;
184
185 for_each_art(i) {
186 if (arts[i].tagged != 0) {
187 arts[i].tagged = 0;
188 untagged = TRUE;
189 }
190 }
191 num_of_tagged_arts = 0;
192
193 return untagged;
194 }
195
196
197 /*
198 * RANGE CODE
199 */
200 /*
201 * Allows user to specify an group/article range that a followup
202 * command will operate on (eg. catchup articles 1-56) # 1-56 K
203 * min/max/curr are the lowest/highest and current positions on the
204 * menu from which this was called; used as defaults if needed
205 * Return TRUE if a range was successfully read, parsed and set
206 *
207 * Allowed syntax is 0123456789-.$ (blanks are ignored):
208 * 1-23 mark grp/art 1 through 23
209 * 1-. mark grp/art 1 through current
210 * 1-$ mark grp/art 1 through last
211 * .-$ mark grp/art current through last
212 */
213 t_bool
214 set_range(
215 int level,
216 int min,
217 int max,
218 int curr)
219 {
220 char *range;
221 char *prompt;
222 int artnum;
223 int i;
224 int range_min;
225 int range_max;
226
227 switch (level) {
228 case SELECT_LEVEL:
229 range = tinrc.default_range_select;
230 break;
231
232 case GROUP_LEVEL:
233 range = tinrc.default_range_group;
234 break;
235
236 case THREAD_LEVEL:
237 range = tinrc.default_range_thread;
238 break;
239
240 default: /* should no happen */
241 return FALSE;
242 }
243
244 #if 0
245 error_message(2, "Min=[%d] Max=[%d] Cur=[%d] DefRng=[%s]", min, max, curr, range);
246 #endif /* 0 */
247 prompt = fmt_string(_(txt_enter_range), range);
248
249 if (!(prompt_string_default(prompt, range, _(txt_range_invalid), HIST_OTHER))) {
250 free(prompt);
251 return FALSE;
252 }
253 free(prompt);
254
255 /*
256 * Parse range string
257 */
258 if (!parse_range(range, min, max, curr, &range_min, &range_max)) {
259 info_message(_(txt_range_invalid));
260 return FALSE;
261 }
262
263 switch (level) {
264 case SELECT_LEVEL:
265 for (i = 0; i < max; i++) /* Clear existing range */
266 active[my_group[i]].inrange = FALSE;
267
268 for (i = range_min - 1; i < range_max; i++)
269 active[my_group[i]].inrange = TRUE;
270 break;
271
272 case GROUP_LEVEL:
273 for (i = 0; i < max; i++) { /* Clear existing range */
274 for_each_art_in_thread(artnum, i)
275 arts[artnum].inrange = FALSE;
276 }
277
278 for (i = range_min - 1; i < range_max; i++) {
279 for_each_art_in_thread(artnum, i)
280 arts[artnum].inrange = TRUE;
281 }
282 break;
283
284 case THREAD_LEVEL:
285 /*
286 * Debatably should clear all of arts[] depending on how you
287 * interpret the (non)spec
288 */
289 for (i = 0; i < grpmenu.max; i++) { /* Clear existing range */
290 for_each_art_in_thread(artnum, i)
291 arts[artnum].inrange = FALSE;
292 }
293
294 i = 1;
295 for_each_art_in_thread(artnum, thread_basenote) {
296 if (i > range_max)
297 break;
298 if (i >= range_min)
299 arts[artnum].inrange = TRUE;
300 i++;
301 }
302 break;
303
304 default:
305 return FALSE;
306 /* NOTREACHED */
307 break;
308 }
309 return TRUE;
310 }
311
312
313 /*
314 * Parse 'range', return the range start and end values in range_start and range_end
315 * min/max/curr are used to select defaults when n explicit start/end are given
316 */
317 static t_bool
318 parse_range(
319 char *range,
320 int min,
321 int max,
322 int curr,
323 int *range_start,
324 int *range_end)
325 {
326 char *ptr = range;
327 enum states { FINDMIN, FINDMAX, DONE };
328 int state = FINDMIN;
329 t_bool ret = FALSE;
330
331 *range_start = -1;
332 *range_end = -1;
333
334 while (*ptr && state != DONE) {
335 if (isdigit((int) *ptr)) {
336 if (state == FINDMAX) {
337 *range_end = atoi(ptr);
338 state = DONE;
339 } else
340 *range_start = atoi(ptr);
341 while (isdigit((int) *ptr))
342 ptr++;
343 } else {
344 switch (*ptr) {
345 case '-':
346 state = FINDMAX;
347 break;
348
349 case '.':
350 if (state == FINDMAX) {
351 *range_end = curr;
352 state = DONE;
353 } else
354 *range_start = curr;
355 break;
356
357 case '$':
358 if (state == FINDMAX) {
359 *range_end = max;
360 state = DONE;
361 }
362 break;
363
364 default:
365 break;
366 }
367 ptr++;
368 }
369 }
370
371 if (*range_start >= min && *range_end >= *range_start && *range_end <= max)
372 ret = TRUE;
373
374 return ret;
375 }
376
377
378 /*
379 * SELECTED CODE
380 */
381 void
382 do_auto_select_arts(
383 void)
384 {
385 int i;
386
387 for_each_art(i) {
388 if (arts[i].status == ART_UNREAD && !arts[i].selected) {
389 #ifdef DEBUG
390 if (debug & DEBUG_NEWSRC)
391 debug_print_comment("group.c: X command");
392 #endif /* DEBUG */
393 art_mark(curr_group, &arts[i], ART_READ);
394 arts[i].zombie = TRUE;
395 }
396 if (curr_group->attribute->show_only_unread_arts)
397 arts[i].keep_in_base = FALSE;
398 }
399 if (curr_group->attribute->show_only_unread_arts)
400 find_base(curr_group);
401
402 grpmenu.curr = 0;
403 show_group_page();
404 }
405
406
407 /* selection already happened in filter_articles() */
408 void
409 undo_auto_select_arts(
410 void)
411 {
412 int i;
413
414 for_each_art(i) {
415 if (arts[i].status == ART_READ && arts[i].zombie) {
416 #ifdef DEBUG
417 if (debug & DEBUG_NEWSRC)
418 debug_print_comment("group.c: + command");
419 #endif /* DEBUG */
420 art_mark(curr_group, &arts[i], ART_UNREAD);
421 arts[i].zombie = FALSE;
422 }
423 }
424 if (curr_group->attribute->show_only_unread_arts)
425 find_base(curr_group);
426
427 grpmenu.curr = 0; /* do we want this? */
428 show_group_page();
429 }
430
431
432 void
433 undo_selections(
434 void)
435 {
436 int i;
437
438 for_each_art(i) {
439 arts[i].selected = FALSE;
440 arts[i].zombie = FALSE;
441 }
442 }
443
444
445 /*
446 * Return TRUE if there are any selected arts
447 */
448 t_bool
449 arts_selected(
450 void)
451 {
452 int i;
453
454 for_each_art(i) {
455 if (arts[i].selected)
456 return TRUE;
457 }
458
459 return FALSE;
460 }