1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
5 /* ========================================================================
6 * Copyright 2013-2018 Eduardo Chappa
7 * Copyright 2006-2008 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
18 #include "../pith/headers.h"
19 #include "../pith/mailindx.h"
20 #include "../pith/mailview.h"
21 #include "../pith/flag.h"
22 #include "../pith/icache.h"
23 #include "../pith/msgno.h"
24 #include "../pith/thread.h"
25 #include "../pith/strlst.h"
26 #include "../pith/status.h"
27 #include "../pith/mailcmd.h"
28 #include "../pith/search.h"
29 #include "../pith/charset.h"
30 #include "../pith/reply.h"
31 #include "../pith/bldaddr.h"
32 #include "../pith/addrstring.h"
33 #include "../pith/news.h"
34 #include "../pith/util.h"
35 #include "../pith/pattern.h"
36 #include "../pith/sequence.h"
37 #include "../pith/color.h"
38 #include "../pith/stream.h"
39 #include "../pith/string.h"
40 #include "../pith/send.h"
41 #include "../pith/options.h"
42 #include "../pith/ablookup.h"
44 #include "../pico/osdep/mswin.h"
48 * pointers to formatting functions
50 ICE_S
*(*format_index_line
)(INDEXDATA_S
*);
51 void (*setup_header_widths
)(MAILSTREAM
*);
54 * pointer to optional load_overview functionality
56 void (*pith_opt_paint_index_hline
)(MAILSTREAM
*, long, ICE_S
*);
59 * pointer to hook for saving index format state
61 void (*pith_opt_save_index_state
)(int);
64 * hook to allow caller to insert cue that indicates a condensed
65 * thread relationship cue
67 int (*pith_opt_condense_thread_cue
)(PINETHRD_S
*, ICE_S
*, char **, size_t *, int, int);
68 int (*pith_opt_truncate_sfstr
)(void);
74 void setup_for_thread_index_screen(void);
75 ICE_S
*format_index_index_line(INDEXDATA_S
*);
76 ICE_S
*format_thread_index_line(INDEXDATA_S
*);
77 int set_index_addr(INDEXDATA_S
*, char *, ADDRESS
*, char *, int, char *);
78 int ctype_is_fixed_length(IndexColType
);
79 void setup_index_header_widths(MAILSTREAM
*);
80 void setup_thread_header_widths(MAILSTREAM
*);
81 int parse_index_format(char *, INDEX_COL_S
**);
82 int index_in_overview(MAILSTREAM
*);
83 ADDRESS
*fetch_from(INDEXDATA_S
*);
84 ADDRESS
*fetch_sender(INDEXDATA_S
*);
85 char *fetch_newsgroups(INDEXDATA_S
*);
86 char *fetch_subject(INDEXDATA_S
*);
87 char *fetch_date(INDEXDATA_S
*);
88 long fetch_size(INDEXDATA_S
*);
89 BODY
*fetch_body(INDEXDATA_S
*);
90 char *fetch_firsttext(INDEXDATA_S
*idata
, int);
91 char *fetch_header(INDEXDATA_S
*idata
, char *hdrname
);
92 void subj_str(INDEXDATA_S
*, char *, size_t, SubjKW
, int, int, ICE_S
*);
93 void key_str(INDEXDATA_S
*, SubjKW
, ICE_S
*);
94 void header_str(INDEXDATA_S
*, HEADER_TOK_S
*, ICE_S
*);
95 void prio_str(INDEXDATA_S
*, IndexColType
, ICE_S
*);
96 void from_str(IndexColType
, INDEXDATA_S
*, char *, size_t, ICE_S
*);
97 int day_of_week(struct date
*);
98 int day_of_year(struct date
*);
99 unsigned long ice_hash(ICE_S
*);
100 char *left_adjust(int);
101 char *right_adjust(int);
102 char *format_str(int, int);
103 char *copy_format_str(int, int, char *, int);
104 void set_print_format(IELEM_S
*, int, int);
105 void set_ielem_widths_in_field(IFIELD_S
*);
108 #define BIGWIDTH 2047
111 /*----------------------------------------------------------------------
112 Initialize the index_disp_format array in ps_global from this
115 Args: format -- the string containing the format tokens
116 answer -- put the answer here, free first if there was a previous
120 init_index_format(char *format
, INDEX_COL_S
**answer
)
123 int i
, w
, monabb_width
= 0, column
= 0;
126 * Record the fact that SCORE appears in some index format. This
127 * is a heavy-handed approach. It will stick at 1 if any format ever
128 * contains score during this session. This is ok since it will just
129 * cause recalculation if wrong and these things rarely change much.
131 if(!ps_global
->a_format_contains_score
&& format
132 && strstr(format
, "SCORE")){
133 ps_global
->a_format_contains_score
= 1;
134 /* recalculate need for scores */
135 scores_are_used(SCOREUSE_INVALID
);
138 set_need_format_setup(ps_global
->mail_stream
);
139 /* if custom format is specified, try it, else go with default */
140 if(!(format
&& *format
&& parse_index_format(format
, answer
))){
141 static INDEX_COL_S answer_default
[] = {
143 {iMessNo
, WeCalculate
},
144 {iSDateTime24
, WeCalculate
},
145 {iFromTo
, Percent
, 33}, /* percent of rest */
146 {iSizeNarrow
, WeCalculate
},
147 {iSubjKey
, Percent
, 67},
152 free_index_format(answer
);
154 *answer
= (INDEX_COL_S
*)fs_get(sizeof(answer_default
));
155 memcpy(*answer
, answer_default
, sizeof(answer_default
));
159 * Test to see how long the month abbreviations are.
161 for(i
= 1; i
<= 12; i
++){
162 p
= month_abbrev_locale(i
);
163 monabb_width
= MAX(utf8_width(p
), monabb_width
);
166 monabb_width
= MIN(MAX(2, monabb_width
), 5);
169 * Fill in req_width's for WeCalculate items.
171 for(column
= 0; (*answer
)[column
].ctype
!= iNothing
; column
++){
173 /* don't use strftime if we're not trying to use the LC_TIME stuff */
174 if(F_ON(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
175 switch((*answer
)[column
].ctype
){
177 (*answer
)[column
].ctype
= iS1Date
;
180 (*answer
)[column
].ctype
= iSDateTimeS1
;
183 (*answer
)[column
].ctype
= iSDateTimeS124
;
190 if((*answer
)[column
].wtype
== WeCalculate
){
191 switch((*answer
)[column
].ctype
){
195 (*answer
)[column
].req_width
= 1;
204 (*answer
)[column
].req_width
= 2;
209 (*answer
)[column
].req_width
= 3;
214 (*answer
)[column
].req_width
= 4;
220 (*answer
)[column
].req_width
= 5;
225 (*answer
)[column
].req_width
= 6;
232 (*answer
)[column
].req_width
= 7;
240 (*answer
)[column
].req_width
= 8;
243 (*answer
)[column
].req_width
= 9;
246 (*answer
)[column
].req_width
= monabb_width
;
247 (*answer
)[column
].monabb_width
= monabb_width
;
254 * Test to see how long it is.
256 for(i
= 0; i
< 7; i
++){
257 p
= day_abbrev_locale(i
);
258 w
= MAX(utf8_width(p
), w
);
261 (*answer
)[column
].req_width
= MIN(MAX(2, w
), 5);
265 (*answer
)[column
].req_width
= monabb_width
+ 3;
266 (*answer
)[column
].monabb_width
= monabb_width
;
273 * Test to see how long it is.
275 for(i
= 1; i
<= 12; i
++){
276 p
= month_name_locale(i
);
277 w
= MAX(utf8_width(p
), w
);
280 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
287 for(i
= 0; i
< 7; i
++){
288 p
= day_name_locale(i
);
289 w
= MAX(utf8_width(p
), w
);
292 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
303 * Format a date to see how long it is.
304 * Make it as least as long as "Yesterday".
305 * We should really use the width of the longest
306 * of the translated yesterdays and friends but...
312 memset(&tm
, 0, sizeof(tm
));
319 switch((*answer
)[column
].ctype
){
321 our_strftime(ss
, sizeof(ss
), "%X", &tm
);
324 our_strftime(ss
, sizeof(ss
), "%c", &tm
);
328 our_strftime(ss
, sizeof(ss
), "%x", &tm
);
331 (*answer
)[column
].req_width
= MIN(MAX(9, utf8_width(ss
)), len
);
334 (*answer
)[column
].monabb_width
= monabb_width
;
339 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
341 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
342 case iSDateTimeIsoS24
:
343 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
345 * These SDates are 8 wide but they need to be 9 for "Yesterday".
347 (*answer
)[column
].req_width
= 9;
350 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
351 (*answer
)[column
].req_width
= 10;
354 (*answer
)[column
].req_width
= 12;
355 (*answer
)[column
].monabb_width
= monabb_width
;
358 (*answer
)[column
].req_width
= 16;
368 (void) mail_parameters(NULL
, SET_IMAPEXTRAHEADERS
,
369 (void *) get_extra_hdrs());
374 reset_index_format(void)
376 long rflags
= ROLE_DO_OTHER
;
381 if(ps_global
->mail_stream
&& nonempty_patterns(rflags
, &pstate
)){
382 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
)){
383 if(match_pattern(pat
->patgrp
, ps_global
->mail_stream
, NULL
,
384 NULL
, NULL
, SE_NOSERVER
|SE_NOPREFETCH
))
388 if(pat
&& pat
->action
&& !pat
->action
->bogus
389 && pat
->action
->index_format
){
391 init_index_format(pat
->action
->index_format
,
392 &ps_global
->index_disp_format
);
397 init_index_format(ps_global
->VAR_INDEX_FORMAT
,
398 &ps_global
->index_disp_format
);
403 free_index_format(INDEX_COL_S
**disp_format
)
405 INDEX_COL_S
*cdesc
= NULL
;
407 if(disp_format
&& *disp_format
){
408 for(cdesc
= (*disp_format
); cdesc
->ctype
!= iNothing
; cdesc
++)
410 free_hdrtok(&cdesc
->hdrtok
);
412 fs_give((void **) disp_format
);
418 new_hdrtok(char *hdrname
)
420 HEADER_TOK_S
*hdrtok
;
422 hdrtok
= (HEADER_TOK_S
*) fs_get(sizeof(HEADER_TOK_S
));
423 memset(hdrtok
, 0, sizeof(HEADER_TOK_S
));
424 hdrtok
->hdrname
= hdrname
? cpystr(hdrname
) : NULL
;
425 hdrtok
->fieldnum
= 0;
426 hdrtok
->adjustment
= Left
;
427 hdrtok
->fieldsepcnt
= 1;
428 hdrtok
->fieldseps
= cpystr(" ");
435 free_hdrtok(HEADER_TOK_S
**hdrtok
)
437 if(hdrtok
&& *hdrtok
){
438 if((*hdrtok
)->hdrname
)
439 fs_give((void **) &(*hdrtok
)->hdrname
);
441 if((*hdrtok
)->fieldseps
)
442 fs_give((void **) &(*hdrtok
)->fieldseps
);
444 fs_give((void **) hdrtok
);
449 /* popular ones first to make it slightly faster */
450 static INDEX_PARSE_T itokens
[] = {
451 {"STATUS", iStatus
, FOR_INDEX
},
452 {"MSGNO", iMessNo
, FOR_INDEX
},
453 {"DATE", iDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
454 {"FROMORTO", iFromTo
, FOR_INDEX
},
455 {"FROMORTONOTNEWS", iFromToNotNews
, FOR_INDEX
},
456 {"SIZE", iSize
, FOR_INDEX
},
457 {"SIZECOMMA", iSizeComma
, FOR_INDEX
},
458 {"SIZENARROW", iSizeNarrow
, FOR_INDEX
},
459 {"KSIZE", iKSize
, FOR_INDEX
},
460 {"SUBJECT", iSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
461 {"SHORTSUBJECT", iShortSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
462 {"FULLSTATUS", iFStatus
, FOR_INDEX
},
463 {"IMAPSTATUS", iIStatus
, FOR_INDEX
},
464 {"SHORTIMAPSTATUS", iSIStatus
, FOR_INDEX
},
465 {"SUBJKEY", iSubjKey
, FOR_INDEX
},
466 {"SHORTSUBJKEY", iShortSubjKey
, FOR_INDEX
},
467 {"SUBJKEYINIT", iSubjKeyInit
, FOR_INDEX
},
468 {"SHORTSUBJKEYINIT",iShortSubjKeyInit
, FOR_INDEX
},
469 {"SUBJECTTEXT", iSubjectText
, FOR_INDEX
},
470 {"SUBJKEYTEXT", iSubjKeyText
, FOR_INDEX
},
471 {"SUBJKEYINITTEXT", iSubjKeyInitText
, FOR_INDEX
},
472 {"OPENINGTEXT", iOpeningText
, FOR_INDEX
},
473 {"OPENINGTEXTNQ", iOpeningTextNQ
, FOR_INDEX
},
474 {"KEY", iKey
, FOR_INDEX
},
475 {"KEYINIT", iKeyInit
, FOR_INDEX
},
476 {"DESCRIPSIZE", iDescripSize
, FOR_INDEX
},
477 {"ATT", iAtt
, FOR_INDEX
},
478 {"SCORE", iScore
, FOR_INDEX
},
479 {"PRIORITY", iPrio
, FOR_INDEX
},
480 {"PRIORITYALPHA", iPrioAlpha
, FOR_INDEX
},
481 {"PRIORITY!", iPrioBang
, FOR_INDEX
},
482 {"LONGDATE", iLDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
483 {"SHORTDATE1", iS1Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
484 {"SHORTDATE2", iS2Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
485 {"SHORTDATE3", iS3Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
486 {"SHORTDATE4", iS4Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
487 {"DATEISO", iDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
488 {"SHORTDATEISO", iDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
489 {"SMARTDATE", iSDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
490 {"SMARTTIME", iSTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
491 {"SMARTTIME24", iSTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
492 {"SMARTDATEISO", iSDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
493 {"SMARTDATESHORTISO",iSDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
494 {"SMARTDATES1", iSDateS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
495 {"SMARTDATES2", iSDateS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
496 {"SMARTDATES3", iSDateS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
497 {"SMARTDATES4", iSDateS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
498 {"SMARTDATETIME", iSDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
499 {"SMARTDATETIMEISO",iSDateTimeIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
500 {"SMARTDATETIMESHORTISO",iSDateTimeIsoS
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
501 {"SMARTDATETIMES1", iSDateTimeS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
502 {"SMARTDATETIMES2", iSDateTimeS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
503 {"SMARTDATETIMES3", iSDateTimeS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
504 {"SMARTDATETIMES4", iSDateTimeS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
505 {"SMARTDATETIME24", iSDateTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
506 {"SMARTDATETIMEISO24", iSDateTimeIso24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
507 {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
508 {"SMARTDATETIMES124", iSDateTimeS124
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
509 {"SMARTDATETIMES224", iSDateTimeS224
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
510 {"SMARTDATETIMES324", iSDateTimeS324
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
511 {"SMARTDATETIMES424", iSDateTimeS424
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
512 {"TIME24", iTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
513 {"TIME12", iTime12
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
514 {"TIMEZONE", iTimezone
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
515 {"MONTHABBREV", iMonAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
516 {"DAYOFWEEKABBREV", iDayOfWeekAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
517 {"DAYOFWEEK", iDayOfWeek
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
518 {"FROM", iFrom
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
519 {"TO", iTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
520 {"SENDER", iSender
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
521 {"CC", iCc
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
522 {"RECIPS", iRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
523 {"NEWS", iNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
524 {"TOANDNEWS", iToAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
525 {"NEWSANDTO", iNewsAndTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
526 {"RECIPSANDNEWS", iRecipsAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
527 {"NEWSANDRECIPS", iNewsAndRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
528 {"MSGID", iMsgID
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
529 {"CURNEWS", iCurNews
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
530 {"DAYDATE", iRDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
531 {"PREFDATE", iPrefDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
532 {"PREFTIME", iPrefTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
533 {"PREFDATETIME", iPrefDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
534 {"DAY", iDay
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
535 {"DAYORDINAL", iDayOrdinal
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
536 {"DAY2DIGIT", iDay2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
537 {"MONTHLONG", iMonLong
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
538 {"MONTH", iMon
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
539 {"MONTH2DIGIT", iMon2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
540 {"YEAR", iYear
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
541 {"YEAR2DIGIT", iYear2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
542 {"ADDRESS", iAddress
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
543 {"MAILBOX", iMailbox
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
544 {"ROLENICK", iRoleNick
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
545 {"INIT", iInit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
546 {"CURDATE", iCurDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
547 {"CURDATEISO", iCurDateIso
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
548 {"CURDATEISOS", iCurDateIsoS
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
549 {"CURTIME24", iCurTime24
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
550 {"CURTIME12", iCurTime12
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
551 {"CURDAY", iCurDay
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
552 {"CURDAY2DIGIT", iCurDay2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
553 {"CURDAYOFWEEK", iCurDayOfWeek
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
554 {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb
,
555 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
556 {"CURMONTH", iCurMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
557 {"CURMONTH2DIGIT", iCurMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
558 {"CURMONTHLONG", iCurMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
559 {"CURMONTHABBREV", iCurMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
560 {"CURYEAR", iCurYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
561 {"CURYEAR2DIGIT", iCurYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
562 {"CURPREFDATE", iCurPrefDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
563 {"CURPREFTIME", iCurPrefTime
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
564 {"CURPREFDATETIME", iCurPrefDateTime
,
565 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
566 {"LASTMONTH", iLstMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
567 {"LASTMONTH2DIGIT", iLstMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
568 {"LASTMONTHLONG", iLstMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
569 {"LASTMONTHABBREV", iLstMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
570 {"LASTMONTHYEAR", iLstMonYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
571 {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit
,
572 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
573 {"LASTYEAR", iLstYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
574 {"LASTYEAR2DIGIT", iLstYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
575 {"HEADER", iHeader
, FOR_INDEX
},
576 {"TEXT", iText
, FOR_INDEX
},
577 {"ARROW", iArrow
, FOR_INDEX
},
578 {"NEWLINE", iNewLine
, FOR_REPLY_INTRO
},
579 {"CURSORPOS", iCursorPos
, FOR_TEMPLATE
},
580 {NULL
, iNothing
, FOR_NOTHING
}
583 INDEX_PARSE_T itokensinv
[sizeof(itokens
)/sizeof(itokens
[0])];
586 inverse_itokens(void)
589 for (pt
= itokens
; pt
->name
; pt
++)
590 itokensinv
[pt
->ctype
].ctype
= pt
- itokens
;
597 return((i
< sizeof(itokens
) && itokens
[i
].name
) ? &itokens
[i
] : NULL
);
602 * Args txt -- The token being checked begins at the beginning
603 * of txt. The end of the token is delimited by a null, or
604 * white space, or an underscore if DELIM_USCORE is set,
605 * or a left paren if DELIM_PAREN is set.
606 * flags -- Flags contains the what_for value, and DELIM_ values.
608 * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
611 itoktype(char *txt
, int flags
)
618 * Separate a copy of the possible token out of txt.
622 while(w
< token
+sizeof(token
)-1 &&
624 !(!(*v
& 0x80) && isspace((unsigned char)*v
)) &&
625 !(flags
& DELIM_USCORE
&& *v
== '_') &&
626 !(flags
& DELIM_PAREN
&& *v
== '(') &&
627 !(flags
& DELIM_COLON
&& *v
== ':'))
632 for(pt
= itokens
; pt
->name
; pt
++)
633 if(pt
->what_for
& flags
&& !strucmp(pt
->name
, token
))
641 parse_index_format(char *format_str
, INDEX_COL_S
**answer
)
646 INDEX_COL_S cdesc
[200]; /* plenty of temp storage for answer */
648 memset((void *)cdesc
, 0, sizeof(cdesc
));
651 while(p
&& *p
&& column
< 200-1){
652 /* skip leading white space for next word */
653 p
= skip_white_space(p
);
654 pt
= itoktype(p
, FOR_INDEX
| DELIM_PAREN
| DELIM_COLON
);
656 /* ignore unrecognized word */
659 for(q
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
666 "parse_index_format: unrecognized token: %s\n",
668 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
669 _("Unrecognized word in index-format: %s"), q
);
675 cdesc
[column
].ctype
= pt
->ctype
;
677 if(pt
->ctype
== iHeader
|| pt
->ctype
== iText
){
679 * iHeader field has special syntax.
681 * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
683 * where width is the regular width or percentage width or
684 * left out for default width, fieldnum defaults to 0 for
685 * whole thing, 1 for first field, ...
686 * and field_separators is a list of characters which separate
687 * the fields. The whole parenthesized part is optional. If used
688 * the arguments can be dropped from the right, so
691 * HEADER:hdrname(10) or
692 * HEADER:hdrname(10%) or
693 * HEADER:hdrname(10,2) or
694 * HEADER:hdrname(,2) or
695 * HEADER:hdrname(10,2, ) or
696 * HEADER:hdrname(10,2,\,:) or
697 * HEADER:hdrname(10,2,\,:,R)
699 * iText field uses the hdrtok field for convenience. It has syntax
705 * and the literal text goes into the index line. It is also special
706 * because there is no 1 column space after this field.
710 p
+= strlen(pt
->name
);
712 /* look for header name */
714 char *w
, hdrname
[200];
719 if(*p
== '\"'){ /* quoted name */
721 while(w
< hdrname
+ sizeof(hdrname
)-1 && *p
!= '\"'){
733 while(w
< hdrname
+ sizeof(hdrname
)-1 &&
734 !(!(*p
& 0x80) && isspace((unsigned char)*p
)) &&
742 cdesc
[column
].hdrtok
= new_hdrtok(hdrname
);
745 if(pt
->ctype
== iHeader
){
746 dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
747 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
750 dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
751 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
756 if(pt
->ctype
== iHeader
){
757 dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p
));
758 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
761 dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p
));
762 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
765 /* skip over rest of bogus config */
766 while(!(!(*p
& 0x80) && isspace((unsigned char)*p
)) && *p
!= '(')
771 /* skip over name and look for parens */
772 p
+= strlen(pt
->name
);
778 while(p
&& *p
&& isdigit((unsigned char) *p
))
781 if(pt
->ctype
== iHeader
){
782 /* first argument is width or width percentage, like for others */
783 if(p
&& *p
&& (*p
== ')' || *p
== ',')){
785 cdesc
[column
].wtype
= Fixed
;
786 cdesc
[column
].req_width
= atoi(q
);
789 cdesc
[column
].wtype
= WeCalculate
;
790 cdesc
[column
].req_width
= 0;
793 else if(p
&& *p
&& *p
== '%' && p
> q
){
794 cdesc
[column
].wtype
= Percent
;
795 cdesc
[column
].req_width
= atoi(q
);
799 cdesc
[column
].wtype
= WeCalculate
;
800 cdesc
[column
].req_width
= 0;
803 /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
804 if(p
&& *p
&& *p
== ','){
806 /* no space allowed between arguments */
807 if(*p
&& isdigit((unsigned char) *p
)){
809 while(*p
&& isdigit((unsigned char) *p
))
812 cdesc
[column
].hdrtok
->fieldnum
= atoi(q
);
815 * Optional 3rd argument is field separators.
816 * Comma is \, and backslash is \\.
822 /* don't use default */
823 if(*p
&& *p
!= ')' && *p
!= ',' && cdesc
[column
].hdrtok
->fieldseps
)
824 cdesc
[column
].hdrtok
->fieldseps
[0] = '\0';
827 if(*p
== '\"' && strchr(p
+1, '\"')){
829 while(*p
&& *p
!= ')' && *p
!= '\"' && *p
!= ','){
830 if(cdesc
[column
].hdrtok
->fieldseps
)
831 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
833 if(*p
== '\\' && *(p
+1))
836 if(cdesc
[column
].hdrtok
->fieldseps
){
837 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
838 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
839 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
847 while(*p
&& *p
!= ')' && *p
!= ','){
848 if(cdesc
[column
].hdrtok
->fieldseps
)
849 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
850 if(*p
== '\\' && *(p
+1))
853 if(cdesc
[column
].hdrtok
->fieldseps
){
854 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
855 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
856 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
861 /* optional 4th argument, left or right adjust */
864 if(*p
== 'L' || *p
== 'l')
865 cdesc
[column
].hdrtok
->adjustment
= Left
;
866 else if(*p
== 'R' || *p
== 'r')
867 cdesc
[column
].hdrtok
->adjustment
= Right
;
869 dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p
? p
: "<null>"));
870 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 4th argument should be L or R");
876 dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p
? p
: "<null>"));
877 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
882 if(p
&& *p
&& *p
== ')' && p
> q
){
883 cdesc
[column
].wtype
= Fixed
;
884 cdesc
[column
].req_width
= atoi(q
);
886 else if(p
&& *p
&& *p
== '%' && p
> q
){
887 cdesc
[column
].wtype
= Percent
;
888 cdesc
[column
].req_width
= atoi(q
);
891 cdesc
[column
].wtype
= WeCalculate
;
892 cdesc
[column
].req_width
= 0;
897 /* if they left out width for iText we can figure it out */
898 if(pt
->ctype
== iText
&& cdesc
[column
].hdrtok
&& cdesc
[column
].hdrtok
->hdrname
){
899 cdesc
[column
].wtype
= Fixed
;
900 cdesc
[column
].req_width
= utf8_width(cdesc
[column
].hdrtok
->hdrname
);
903 cdesc
[column
].wtype
= WeCalculate
;
904 cdesc
[column
].req_width
= 0;
909 /* skip text at end of word */
910 while(p
&& *p
&& !isspace((unsigned char)*p
))
914 /* if, after all that, we didn't find anything recognizable, bitch */
916 dprint((1, "Completely unrecognizable index-format\n"));
917 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
918 _("Configured \"index-format\" unrecognizable. Using default."));
922 /* Finish with Nothing column */
923 cdesc
[column
].ctype
= iNothing
;
925 /* free up old answer */
927 free_index_format(answer
);
929 /* allocate space for new answer */
930 *answer
= (INDEX_COL_S
*)fs_get((column
+1)*sizeof(INDEX_COL_S
));
931 memset((void *)(*answer
), 0, (column
+1)*sizeof(INDEX_COL_S
));
932 /* copy answer to real place */
933 for(i
= 0; i
<= column
; i
++)
934 (*answer
)[i
] = cdesc
[i
];
941 * These types are basically fixed in width.
942 * The order is slightly significant. The ones towards the front of the
943 * list get space allocated sooner than the ones at the end of the list.
945 static IndexColType fixed_ctypes
[] = {
946 iMessNo
, iStatus
, iFStatus
, iIStatus
, iSIStatus
,
947 iDate
, iSDate
, iSDateTime
, iSDateTime24
,
948 iSTime
, iSTime24
, iLDate
,
949 iS1Date
, iS2Date
, iS3Date
, iS4Date
, iDateIso
, iDateIsoS
,
950 iSDateIso
, iSDateIsoS
,
951 iSDateS1
, iSDateS2
, iSDateS3
, iSDateS4
,
952 iSDateTimeIso
, iSDateTimeIsoS
,
953 iSDateTimeS1
, iSDateTimeS2
, iSDateTimeS3
, iSDateTimeS4
,
954 iSDateTimeIso24
, iSDateTimeIsoS24
,
955 iSDateTimeS124
, iSDateTimeS224
, iSDateTimeS324
, iSDateTimeS424
,
956 iSize
, iSizeComma
, iSizeNarrow
, iKSize
, iDescripSize
,
957 iPrio
, iPrioBang
, iPrioAlpha
, iInit
,
958 iAtt
, iTime24
, iTime12
, iTimezone
, iMonAbb
, iYear
, iYear2Digit
,
959 iDay2Digit
, iMon2Digit
, iDayOfWeekAbb
, iScore
, iMonLong
, iDayOfWeek
964 ctype_is_fixed_length(IndexColType ctype
)
969 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
972 if(ctype
== fixed_ctypes
[j
])
980 /*----------------------------------------------------------------------
981 Setup the widths of the various columns in the index display
984 setup_index_header_widths(MAILSTREAM
*stream
)
986 int colspace
; /* for reserving space between columns */
987 int j
, some_to_calculate
;
988 int space_left
, screen_width
, fix
;
989 int keep_going
, tot_pct
, was_sl
;
994 max_msgno
= mn_get_total(ps_global
->msgmap
);
996 dprint((8, "=== setup_index_header_widths() ===\n"));
998 clear_icache_flags(stream
);
999 screen_width
= ps_global
->ttyo
->screen_cols
;
1000 space_left
= screen_width
;
1001 some_to_calculate
= 0;
1005 * Calculate how many fields there are so we know how many spaces
1006 * between columns to reserve. Fill in Fixed widths now. Reserve
1007 * special case WeCalculate with non-zero req_widths before doing
1008 * Percent cases below.
1010 for(cdesc
= ps_global
->index_disp_format
;
1011 cdesc
->ctype
!= iNothing
;
1014 if(cdesc
->wtype
== Fixed
){
1015 cdesc
->width
= cdesc
->req_width
;
1016 if(cdesc
->width
> 0)
1019 else if(cdesc
->wtype
== Percent
){
1020 cdesc
->width
= 0; /* calculated later */
1023 else{ /* WeCalculate */
1024 cdesc
->width
= cdesc
->req_width
; /* reserve this for now */
1025 some_to_calculate
++;
1029 /* no space after iText */
1030 if(cdesc
->ctype
== iText
)
1033 space_left
-= cdesc
->width
;
1036 colspace
= MAX(colspace
, 0);
1038 space_left
-= colspace
; /* space between columns */
1040 ps_global
->display_keywords_in_subject
= 0;
1041 ps_global
->display_keywordinits_in_subject
= 0;
1044 * Set the actual lengths for the fixed width fields and set up
1045 * the left or right adjustment for everything.
1046 * There should be a case setting actual_length for all of the types
1049 for(cdesc
= ps_global
->index_disp_format
;
1050 cdesc
->ctype
!= iNothing
;
1053 wtype
= cdesc
->wtype
;
1055 if(cdesc
->ctype
== iSubjKey
|| cdesc
->ctype
== iSubjKeyText
)
1056 ps_global
->display_keywords_in_subject
= 1;
1057 else if(cdesc
->ctype
== iSubjKeyInit
|| cdesc
->ctype
== iSubjKeyInitText
)
1058 ps_global
->display_keywordinits_in_subject
= 1;
1060 if(wtype
== WeCalculate
|| wtype
== Percent
|| cdesc
->width
!= 0){
1062 switch(cdesc
->ctype
){
1063 case iSDate
: case iSDateIso
: case iSDateIsoS
:
1064 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1065 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
1066 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1067 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
1068 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1069 case iSTime
: case iSTime24
:
1070 set_format_includes_smartdate(stream
);
1077 if(ctype_is_fixed_length(cdesc
->ctype
)){
1078 switch(cdesc
->ctype
){
1082 cdesc
->actual_length
= 1;
1083 cdesc
->adjustment
= Left
;
1089 cdesc
->actual_length
= 2;
1090 cdesc
->adjustment
= Left
;
1094 cdesc
->actual_length
= 2;
1095 cdesc
->adjustment
= Right
;
1100 cdesc
->actual_length
= 3;
1101 cdesc
->adjustment
= Left
;
1105 set_format_includes_msgno(stream
);
1106 if(max_msgno
< 1000)
1107 cdesc
->actual_length
= 3;
1108 else if(max_msgno
< 10000)
1109 cdesc
->actual_length
= 4;
1110 else if(max_msgno
< 100000)
1111 cdesc
->actual_length
= 5;
1113 cdesc
->actual_length
= 6;
1115 cdesc
->adjustment
= Right
;
1120 cdesc
->actual_length
= 4;
1121 cdesc
->adjustment
= Left
;
1126 cdesc
->actual_length
= 5;
1127 cdesc
->adjustment
= Left
;
1131 cdesc
->actual_length
= 5;
1132 cdesc
->adjustment
= Right
;
1137 cdesc
->actual_length
= 6;
1138 cdesc
->adjustment
= Left
;
1142 cdesc
->actual_length
= 6;
1143 cdesc
->adjustment
= Right
;
1149 cdesc
->actual_length
= 7;
1150 cdesc
->adjustment
= Right
;
1154 cdesc
->actual_length
= 7;
1155 cdesc
->adjustment
= Left
;
1159 cdesc
->actual_length
= 7;
1160 cdesc
->adjustment
= Left
;
1169 cdesc
->actual_length
= 8;
1170 cdesc
->adjustment
= Left
;
1174 cdesc
->actual_length
= 8;
1175 cdesc
->adjustment
= Right
;
1186 cdesc
->actual_length
= cdesc
->req_width
;
1187 cdesc
->adjustment
= Left
;
1192 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1193 case iSDateTimeIsoS
:
1194 case iSDateTimeIsoS24
:
1195 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1196 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1197 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
1198 if(cdesc
->ctype
== iSDateIso
1199 || cdesc
->ctype
== iSDateTimeIso
1200 || cdesc
->ctype
== iSDateTimeIso24
)
1201 cdesc
->actual_length
= 10;
1203 cdesc
->actual_length
= 9;
1205 cdesc
->adjustment
= Left
;
1209 cdesc
->actual_length
= 9;
1210 cdesc
->adjustment
= Right
;
1214 cdesc
->actual_length
= 10;
1215 cdesc
->adjustment
= Left
;
1219 cdesc
->actual_length
= 12;
1220 cdesc
->adjustment
= Left
;
1224 alpine_panic("Unhandled fixed case in setup_index_header");
1228 else if(cdesc
->ctype
== iHeader
)
1229 cdesc
->adjustment
= cdesc
->hdrtok
? cdesc
->hdrtok
->adjustment
: Left
;
1231 cdesc
->adjustment
= Left
;
1235 if(ps_global
->display_keywords_in_subject
)
1236 ps_global
->display_keywordinits_in_subject
= 0;
1238 /* if have reserved unneeded space for size, give it back */
1239 for(cdesc
= ps_global
->index_disp_format
;
1240 cdesc
->ctype
!= iNothing
;
1242 if(cdesc
->ctype
== iSize
|| cdesc
->ctype
== iKSize
||
1243 cdesc
->ctype
== iSizeNarrow
||
1244 cdesc
->ctype
== iSizeComma
|| cdesc
->ctype
== iDescripSize
){
1245 if(cdesc
->actual_length
== 0){
1246 if((fix
=cdesc
->width
) > 0){ /* had this reserved */
1251 space_left
++; /* +1 for space between columns */
1256 * Calculate the field widths that are basically fixed in width.
1257 * Do them in this order in case we don't have enough space to go around.
1258 * The set of fixed_ctypes here is the same as the set where we
1259 * set the actual_lengths above.
1261 for(j
= 0; space_left
> 0 && some_to_calculate
; j
++){
1263 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
1266 for(cdesc
= ps_global
->index_disp_format
;
1267 cdesc
->ctype
!= iNothing
&& space_left
> 0 && some_to_calculate
;
1269 if(cdesc
->ctype
== fixed_ctypes
[j
] && cdesc
->wtype
== WeCalculate
){
1270 some_to_calculate
--;
1271 fix
= MIN(cdesc
->actual_length
- cdesc
->width
, space_left
);
1272 cdesc
->width
+= fix
;
1278 * Fill in widths for Percent cases. If there are no more to calculate,
1279 * use the percentages as relative numbers and use the rest of the space,
1280 * else treat them as absolute percentages of the original avail screen.
1283 if(some_to_calculate
){
1284 int tot_requested
= 0;
1287 * Requests are treated as percent of screen width. See if they
1288 * will all fit. If not, trim them back proportionately.
1290 for(cdesc
= ps_global
->index_disp_format
;
1291 cdesc
->ctype
!= iNothing
;
1293 if(cdesc
->wtype
== Percent
){
1294 /* The 2, 200, and +100 are because we're rounding */
1295 fix
= ((2*cdesc
->req_width
*
1296 (screen_width
-colspace
))+100) / 200;
1297 tot_requested
+= fix
;
1301 if(tot_requested
> space_left
){
1302 int multiplier
= (100 * space_left
) / tot_requested
;
1304 for(cdesc
= ps_global
->index_disp_format
;
1305 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1307 if(cdesc
->wtype
== Percent
){
1308 /* The 2, 200, and +100 are because we're rounding */
1309 fix
= ((2*cdesc
->req_width
*
1310 (screen_width
-colspace
))+100) / 200;
1311 fix
= (2 * fix
* multiplier
+ 100) / 200;
1312 fix
= MIN(fix
, space_left
);
1313 cdesc
->width
+= fix
;
1319 for(cdesc
= ps_global
->index_disp_format
;
1320 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1322 if(cdesc
->wtype
== Percent
){
1323 /* The 2, 200, and +100 are because we're rounding */
1324 fix
= ((2*cdesc
->req_width
*
1325 (screen_width
-colspace
))+100) / 200;
1326 fix
= MIN(fix
, space_left
);
1327 cdesc
->width
+= fix
;
1335 was_sl
= space_left
;
1336 /* add up total percentages requested */
1337 for(cdesc
= ps_global
->index_disp_format
;
1338 cdesc
->ctype
!= iNothing
;
1340 if(cdesc
->wtype
== Percent
)
1341 tot_pct
+= cdesc
->req_width
;
1343 /* give relative weight to requests */
1344 for(cdesc
= ps_global
->index_disp_format
;
1345 cdesc
->ctype
!= iNothing
&& space_left
> 0 && tot_pct
> 0;
1347 if(cdesc
->wtype
== Percent
){
1348 fix
= ((2*cdesc
->req_width
*was_sl
)+tot_pct
) / (2*tot_pct
);
1349 fix
= MIN(fix
, space_left
);
1350 cdesc
->width
+= fix
;
1357 /* split up rest, give twice as much to Subject */
1359 while(space_left
> 0 && keep_going
){
1361 for(cdesc
= ps_global
->index_disp_format
;
1362 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1364 if(cdesc
->wtype
== WeCalculate
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1368 if(space_left
> 0 && (cdesc
->ctype
== iSubject
1369 || cdesc
->ctype
== iShortSubject
1370 || cdesc
->ctype
== iSubjectText
1371 || cdesc
->ctype
== iSubjKey
1372 || cdesc
->ctype
== iShortSubjKey
1373 || cdesc
->ctype
== iSubjKeyText
1374 || cdesc
->ctype
== iSubjKeyInit
1375 || cdesc
->ctype
== iShortSubjKeyInit
1376 || cdesc
->ctype
== iSubjKeyInitText
)){
1384 /* if still more, pad out percent's */
1386 while(space_left
> 0 && keep_going
){
1388 for(cdesc
= ps_global
->index_disp_format
;
1389 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1391 if(cdesc
->wtype
== Percent
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1399 /* if user made Fixed fields too big, give back space */
1401 while(space_left
< 0 && keep_going
){
1403 for(cdesc
= ps_global
->index_disp_format
;
1404 cdesc
->ctype
!= iNothing
&& space_left
< 0;
1406 if(cdesc
->wtype
== Fixed
&& cdesc
->width
> 0){
1414 if(pith_opt_save_index_state
)
1415 (*pith_opt_save_index_state
)(FALSE
);
1420 setup_thread_header_widths(MAILSTREAM
*stream
)
1422 clear_icache_flags(stream
);
1423 if(pith_opt_save_index_state
)
1424 (*pith_opt_save_index_state
)(TRUE
);
1429 * load_overview - c-client call back to gather overview data
1431 * Note: if we never get called, UID represents a hole
1432 * if we're passed a zero UID, totally bogus overview data
1433 * if we're passed a zero obuf, mostly bogus overview data
1436 load_overview(MAILSTREAM
*stream
, imapuid_t uid
, OVERVIEW
*obuf
, long unsigned int rawno
)
1438 if(obuf
&& rawno
>= 1L && stream
&& rawno
<= stream
->nmsgs
){
1442 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1446 * Only really load the thing if we've got an NNTP stream
1447 * otherwise we're just using mail_fetch_overview to load the
1448 * IMAP envelope cache with the specific set of messages
1451 idata
.stream
= stream
;
1452 idata
.rawno
= rawno
;
1453 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), idata
.rawno
);
1454 idata
.size
= obuf
->optional
.octets
;
1455 idata
.from
= obuf
->from
;
1456 idata
.date
= obuf
->date
;
1457 idata
.subject
= obuf
->subject
;
1459 ice
= (*format_index_line
)(&idata
);
1460 if(idata
.bogus
&& ice
){
1463 clear_ice(&ice
->tice
);
1468 else if(F_OFF(F_QUELL_NEWS_ENV_CB
, ps_global
)
1469 && (!THRD_INDX() || (ice
&& ice
->tice
))
1470 && !msgline_hidden(stream
, sp_msgmap(stream
), idata
.msgno
, 0)
1471 && pith_opt_paint_index_hline
){
1472 (*pith_opt_paint_index_hline
)(stream
, idata
.msgno
, ice
);
1479 build_header_work(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
1480 long int msgno
, long int top_msgno
, int msgcount
, int *fetched
)
1484 long n
, i
, cnt
, rawno
, visible
, limit
= -1L;
1486 rawno
= mn_m2raw(msgmap
, msgno
);
1490 ice
= fetch_ice(stream
, rawno
);
1494 if(ice
->tice
&& ice
->tice
->ifield
1495 && ice
->tice
->color_lookup_done
&& ice
->tice
->widths_done
){
1497 char buf
[MAX_SCREEN_COLS
+1];
1498 simple_index_line(buf
, sizeof(buf
), ice
->tice
, msgno
);
1500 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1503 buf
[0] ? strlen(buf
) : 0));
1508 ice
= fetch_ice(stream
, rawno
);
1512 if(ice
->ifield
&& ice
->color_lookup_done
&& ice
->widths_done
){
1514 char buf
[MAX_SCREEN_COLS
+1];
1515 simple_index_line(buf
, sizeof(buf
), ice
, msgno
);
1517 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1520 buf
[0] ? strlen(buf
) : 0));
1526 * If we are in THRD_INDX() and the width changed we don't currently
1527 * have a method of fixing just the widths and print_format strings.
1528 * Instead, we clear the index cache entry and start over.
1530 if(THRD_INDX() && ice
&& ice
->tice
&& ice
->tice
->ifield
1531 && !ice
->tice
->widths_done
){
1532 clear_ice(&ice
->tice
);
1536 * Fetch everything we need to start filling in the index line
1537 * explicitly via mail_fetch_overview. On an nntp stream
1538 * this has the effect of building the index lines in the
1539 * load_overview callback. Under IMAP we're either getting
1540 * the envelope data via the imap_envelope callback or
1541 * preloading the cache. Either way, we're getting exactly
1542 * what we want rather than relying on linear lookahead sort
1545 if(!(fetched
&& *fetched
) && index_in_overview(stream
)
1546 && ((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1547 || (!THRD_INDX() && !ice
->ifield
))){
1556 /* clear sequence bits */
1557 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1558 if((mc
= mail_elt(stream
, n
)) != NULL
)
1562 * Light interesting bits
1563 * NOTE: not set above because m2raw's cheaper
1564 * than raw2m for every message
1568 * Unfortunately, it is expensive to calculate visible pages
1569 * in thread index if we are zoomed, so we don't try.
1571 if(THRD_INDX() && any_lflagged(msgmap
, MN_HIDE
))
1572 visible
= msgmap
->visible_threads
;
1573 else if(THREADING() && sp_viewing_a_thread(stream
)){
1575 * We know that all visible messages in the thread are marked
1578 for(visible
= 0L, n
= top_msgno
;
1579 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1582 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1585 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1591 visible
= mn_get_total(msgmap
)
1592 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1594 limit
= MIN(visible
, msgcount
);
1600 * First add the msgno we're asking for in case it
1603 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, msgno
));
1604 if(msgno
<= mn_get_total(msgmap
)
1605 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1606 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1609 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, top_msgno
));
1612 * Loop through visible threads, marking them for fetching.
1613 * Stop at end of screen or sooner if we run out of visible
1617 n
= mn_raw2m(msgmap
, thrd
->rawno
);
1619 && n
<= mn_get_total(msgmap
)
1620 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1621 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1627 /* find next thread which is visible */
1629 if(mn_get_revsort(msgmap
) && thrd
->prevthd
)
1630 thrd
= fetch_thread(stream
, thrd
->prevthd
);
1631 else if(!mn_get_revsort(msgmap
) && thrd
->nextthd
)
1632 thrd
= fetch_thread(stream
, thrd
->nextthd
);
1636 && msgline_hidden(stream
, msgmap
,
1637 mn_raw2m(msgmap
, thrd
->rawno
), 0));
1644 * First add the msgno we're asking for in case it
1647 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)
1648 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,msgno
)))) || !ic
->ifield
)){
1649 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1651 * If we're doing a MUTTLIKE display the index line
1652 * may depend on the thread parent, and grandparent,
1653 * and further back. So just fetch the whole thread
1657 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1659 thrd
= fetch_thread(stream
, thrd
->top
);
1661 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1663 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1664 && (mc
= mail_elt(stream
,rawno
))
1665 && !mc
->private.msg
.env
){
1674 && n
<= mn_get_total(msgmap
)
1675 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,n
)))) || !ic
->ifield
)){
1676 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1678 * If we're doing a MUTTLIKE display the index line
1679 * may depend on the thread parent, and grandparent,
1680 * and further back. So just fetch the whole thread
1684 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1686 thrd
= fetch_thread(stream
, thrd
->top
);
1688 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1690 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1691 && (mc
= mail_elt(stream
,rawno
))
1692 && !mc
->private.msg
.env
){
1701 /* find next n which is visible */
1702 while(++n
<= mn_get_total(msgmap
)
1703 && msgline_hidden(stream
, msgmap
, n
, 0))
1709 seq
= build_sequence(stream
, NULL
, NULL
);
1711 ps_global
->dont_count_flagchanges
= 1;
1712 mail_fetch_overview_sequence(stream
, seq
,
1713 (stream
->dtb
&& stream
->dtb
->name
1714 && !strcmp(stream
->dtb
->name
, "imap"))
1715 ? NULL
: load_overview
);
1716 ps_global
->dont_count_flagchanges
= 0;
1717 fs_give((void **) &seq
);
1722 * reassign ice from the cache as it may've been built
1723 * within the overview callback or it may have become stale
1724 * in the prior sequence bit setting loop ...
1726 rawno
= mn_m2raw(msgmap
, msgno
);
1727 ice
= fetch_ice(stream
, rawno
);
1732 if((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1733 || (!THRD_INDX() && !ice
->ifield
)){
1737 * With pre-fetching/callback-formatting done and no success,
1738 * fall into formatting the requested line...
1740 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1741 idata
.stream
= stream
;
1742 idata
.msgno
= msgno
;
1743 idata
.rawno
= mn_m2raw(msgmap
, msgno
);
1744 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
1745 && (mc
= mail_elt(stream
, idata
.rawno
))){
1746 idata
.size
= mc
->rfc822_size
;
1747 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
1752 ice
= (*format_index_line
)(&idata
);
1758 * If needed, reset the print_format strings so that they add up to
1759 * the right total width. The reset width functionality isn't implemented
1760 * for THRD_INDX() so we are just doing a complete rebuild in that
1761 * case. This is driven by the clear_ice() call in clear_index_cache_ent()
1762 * so it should never be the case that THRD_INDX() is true and only
1763 * widths_done needs to be fixed.
1765 if((!THRD_INDX() && ice
->ifield
&& !ice
->widths_done
)){
1770 if(need_format_setup(stream
))
1771 setup_header_widths(stream
);
1774 working_ice
= ice
? ice
->tice
: NULL
;
1780 * First fix the ifield widths. The cdescs with nonzero widths
1781 * should correspond to the ifields that are defined.
1783 ifield
= working_ice
->ifield
;
1784 for(cdesc
= ps_global
->index_disp_format
;
1785 cdesc
->ctype
!= iNothing
&& ifield
; cdesc
++){
1787 if(cdesc
->ctype
!= ifield
->ctype
){
1788 dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno
, (int) cdesc
->ctype
, (int) ifield
->ctype
));
1792 ifield
->width
= cdesc
->width
;
1793 ifield
= ifield
->next
;
1797 /* fix the print_format strings and widths */
1798 for(ifield
= working_ice
->ifield
; ifield
; ifield
= ifield
->next
)
1799 set_ielem_widths_in_field(ifield
);
1801 working_ice
->widths_done
= 1;
1805 if(THRD_INDX() && ice
->tice
)
1806 ice
->tice
->color_lookup_done
= 1;
1809 * Look for a color for this line (and other lines in the current
1810 * view). This does a SEARCH for each role which has a color until
1811 * it finds a match. This will be satisfied by the c-client
1812 * cache created by the mail_fetch_overview above if it is a header
1815 if(!THRD_INDX() && !ice
->color_lookup_done
){
1816 COLOR_PAIR
*linecolor
;
1819 PAT_STATE
*pstate
= NULL
;
1821 if(pico_usingcolor()){
1823 if(THREADING() && sp_viewing_a_thread(stream
)){
1824 for(visible
= 0L, n
= top_msgno
;
1825 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1828 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1831 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1837 visible
= mn_get_total(msgmap
)
1838 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1840 limit
= MIN(visible
, msgcount
);
1842 /* clear sequence bits */
1843 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1844 if((mc
= mail_elt(stream
, n
)) != NULL
)
1851 && n
<= mn_get_total(msgmap
)
1852 && (!(ic
=fetch_ice(stream
,(rawno
= mn_m2raw(msgmap
, n
)))) || !ic
->color_lookup_done
)){
1854 if(rawno
>= 1L && rawno
<= stream
->nmsgs
1855 && (mc
= mail_elt(stream
, rawno
))){
1864 /* find next n which is visible */
1865 while(++n
<= mn_get_total(msgmap
)
1866 && msgline_hidden(stream
, msgmap
, n
, 0))
1871 * Why is there a loop here? The first call to get_index_line_color
1872 * will return a set of messages which match one of the roles.
1873 * Then, we eliminate those messages from the search set and try
1874 * again. This time we'd get past that role and into a different
1875 * role. Because of that, we hang onto the state and don't reset
1876 * to the first_pattern on the second and subsequent times
1877 * through the loop, avoiding fruitless match_pattern calls in
1878 * get_index_line_color.
1879 * Before the first call, pstate should be set to NULL.
1882 ss
= build_searchset(stream
);
1887 colormatch
= get_index_line_color(stream
, ss
, &pstate
,
1891 * Assign this color to all matched msgno's and
1892 * turn off the sequence bit so we won't check
1896 for(s
= ss
; s
; s
= s
->next
){
1897 for(n
= s
->first
; n
<= s
->last
; n
++){
1898 if(n
>= 1L && n
<= stream
->nmsgs
1899 && (mc
= mail_elt(stream
, n
))
1903 ic
= fetch_ice(stream
, n
);
1905 ic
->color_lookup_done
= 1;
1907 ic
->linecolor
= new_color_pair(linecolor
->fg
,
1915 free_color_pair(&linecolor
);
1918 /* have to mark the rest of the lookups done */
1919 for(s
= ss
; s
&& cnt
> 0; s
= s
->next
){
1920 for(n
= s
->first
; n
<= s
->last
&& cnt
> 0; n
++){
1921 if(n
>= 1L && n
<= stream
->nmsgs
1922 && (mc
= mail_elt(stream
, n
))
1925 ic
= fetch_ice(stream
, n
);
1927 ic
->color_lookup_done
= 1;
1932 /* just making sure */
1936 mail_free_searchset(&ss
);
1942 ice
= fetch_ice(stream
, mn_m2raw(msgmap
, msgno
));
1945 ice
->color_lookup_done
= 1;
1948 return(ice
); /* Return formatted index data */
1953 day_of_week(struct date
*d
)
1964 m
-= 3; /* March is month 0 */
1966 return((d
->day
+2+((7+31*m
)/12)+y
+(y
/4)+(y
/400)-(y
/100))%7);
1970 static int daytab
[2][13] = {
1971 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1972 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1976 day_of_year(struct date
*d
)
1980 if(d
->year
<= 0 || d
->month
< 1 || d
->month
> 12)
1984 leap
= (d
->year
%4 == 0 && d
->year
%100 != 0) || d
->year
%400 == 0;
1985 for(i
= 1; i
< d
->month
; i
++)
1986 doy
+= daytab
[leap
][i
];
1993 /*----------------------------------------------------------------------
1994 Format a string summarizing the message header for index on screen
1996 Args: buffer -- buffer to place formatted line
1997 idata -- snot it takes to format the line
1999 Result: returns pointer given buffer IF entry formatted
2000 else NULL if there was a problem (but the buffer is
2001 still suitable for display)
2004 format_index_index_line(INDEXDATA_S
*idata
)
2006 char str
[BIGWIDTH
+1], to_us
, status
, *field
,
2008 int i
, collapsed
= 0, start
, fromfield
;
2012 ADDRESS
*addr
, *toaddr
, *ccaddr
, *last_to
;
2013 PINETHRD_S
*thrd
= NULL
;
2014 INDEX_COL_S
*cdesc
= NULL
;
2018 COLOR_PAIR
*color
= NULL
;
2019 struct variable
*vars
= ps_global
->vars
;
2021 dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
2022 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
2025 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
2029 free_ifield(&ice
->ifield
);
2032 * Operate on a temporary copy of ice. The reason for this
2033 * is that we may end up causing a pine_mail_fetchenvelope() call
2034 * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
2035 * and mm_flags may do a clear_ice(), freeing the ice we are working
2036 * on out from under us. We try to fetch everything we need in
2037 * build_header_work() but c-client will short-circuit our request
2038 * if we already got the raw header for some reason. One possible
2039 * reason is a categorizer command in a filter. In that case
2040 * we still need a fetch fast to get the rest of the envelope data.
2042 ice
= copy_ice(ice
);
2044 /* is this a collapsed thread index line? */
2045 if(!idata
->bogus
&& THREADING()){
2046 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2047 collapsed
= thrd
&& thrd
->next
2048 && get_lflag(idata
->stream
, NULL
,
2049 idata
->rawno
, MN_COLL
);
2052 /* calculate contents of the required fields */
2053 for(cdesc
= ps_global
->index_disp_format
; cdesc
->ctype
!= iNothing
; cdesc
++)
2055 memset(str
, 0, sizeof(str
));
2056 ifield
= new_ifield(&ice
->ifield
);
2057 ifield
->ctype
= cdesc
->ctype
;
2058 ifield
->width
= cdesc
->width
;
2062 if(cdesc
->ctype
== iMessNo
)
2063 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2064 else if(idata
->bogus
< 2 && (cdesc
->ctype
== iSubject
2065 || cdesc
->ctype
== iShortSubject
2066 || cdesc
->ctype
== iSubjectText
2067 || cdesc
->ctype
== iSubjKey
2068 || cdesc
->ctype
== iShortSubjKey
2069 || cdesc
->ctype
== iSubjKeyText
2070 || cdesc
->ctype
== iSubjKeyInit
2071 || cdesc
->ctype
== iShortSubjKeyInit
2072 || cdesc
->ctype
== iSubjKeyInitText
))
2073 snprintf(str
, sizeof(str
), "%s", _("[ No Message Text Available ]"));
2076 switch(cdesc
->ctype
){
2078 to_us
= status
= ' ';
2080 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2081 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
2082 status
= status_symbol_for_thread(idata
->stream
, thrd
,
2086 if(idata
->rawno
> 0L && idata
->rawno
<= idata
->stream
->nmsgs
2087 && (mc
=mail_elt(idata
->stream
,idata
->rawno
)) && mc
->flagged
)
2088 to_us
= '*'; /* simple */
2089 else if(!IS_NEWS(idata
->stream
)){
2090 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2091 if(address_is_us(addr
, ps_global
)){
2099 if(to_us
!= '+' && resent_to_us(idata
)){
2105 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2106 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2107 if(address_is_us(addr
, ps_global
)){
2114 status
= (!idata
->stream
|| !IS_NEWS(idata
->stream
)
2115 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))
2121 if(user_flag_is_set(idata
->stream
, idata
->rawno
, FORWARDED_FLAG
))
2131 snprintf(str
, sizeof(str
), "%c %c", to_us
, status
);
2133 ifield
->leftadj
= 1;
2134 for(i
= 0; i
< 3; i
++){
2135 ielem
= new_ielem(&ifield
->ielem
);
2136 ielem
->freedata
= 1;
2137 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2138 ielem
->data
[0] = str
[i
];
2139 ielem
->data
[1] = '\0';
2141 set_print_format(ielem
, 1, ifield
->leftadj
);
2144 if(pico_usingcolor()){
2147 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2148 ielem
= ifield
->ielem
;
2149 ielem
->freecolor
= 1;
2150 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2153 else if(str
[0] == '+' || str
[0] == '-'){
2154 if(VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
2155 ielem
= ifield
->ielem
;
2156 ielem
->freecolor
= 1;
2157 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2162 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2163 ielem
= ifield
->ielem
->next
->next
;
2164 ielem
->freecolor
= 1;
2165 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2168 else if(str
[2] == 'A'){
2169 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2170 ielem
= ifield
->ielem
->next
->next
;
2171 ielem
->freecolor
= 1;
2172 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2175 else if(str
[2] == 'F'){
2176 if(VAR_IND_FWD_FORE_COLOR
&& VAR_IND_FWD_BACK_COLOR
){
2177 ielem
= ifield
->ielem
->next
->next
;
2178 ielem
->freecolor
= 1;
2179 ielem
->color
= new_color_pair(VAR_IND_FWD_FORE_COLOR
, VAR_IND_FWD_BACK_COLOR
);
2182 else if(str
[2] == 'N'){
2183 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2184 ielem
= ifield
->ielem
->next
->next
;
2185 ielem
->freecolor
= 1;
2186 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2197 char new, answered
, deleted
, flagged
;
2200 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2201 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 0);
2205 if(!IS_NEWS(idata
->stream
)){
2206 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2207 if(address_is_us(addr
, ps_global
)){
2212 if(to_us
== ' ' && resent_to_us(idata
))
2215 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2216 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2217 if(address_is_us(addr
, ps_global
)){
2224 new = answered
= deleted
= flagged
= ' ';
2227 unsigned long save_branch
, cnt
, tot_in_thrd
;
2230 * Branch is a sibling, not part of the thread, so
2231 * don't consider it when displaying this line.
2233 save_branch
= thrd
->branch
;
2236 tot_in_thrd
= count_flags_in_thread(idata
->stream
, thrd
,
2239 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_DEL
);
2241 deleted
= (cnt
== tot_in_thrd
) ? 'D' : 'd';
2243 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_ANS
);
2245 answered
= (cnt
== tot_in_thrd
) ? 'A' : 'a';
2247 /* no lower case *, same thing for some or all */
2248 if(count_flags_in_thread(idata
->stream
, thrd
, F_FLAG
))
2251 new = status_symbol_for_thread(idata
->stream
, thrd
,
2254 thrd
->branch
= save_branch
;
2257 mc
= (idata
->rawno
> 0L && idata
->stream
2258 && idata
->rawno
<= idata
->stream
->nmsgs
)
2259 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2260 if(mc
&& mc
->valid
){
2261 if(cdesc
->ctype
== iIStatus
|| cdesc
->ctype
== iSIStatus
){
2263 new = mc
->seen
? 'R' : 'N';
2268 && (!IS_NEWS(idata
->stream
)
2269 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)))
2283 snprintf(str
, sizeof(str
), "%c %c%c%c%c", to_us
, flagged
, new,
2286 if(cdesc
->ctype
== iSIStatus
)
2291 ifield
->leftadj
= 1;
2292 for(i
= start
; i
< 6; i
++){
2293 ielem
= new_ielem(&ifield
->ielem
);
2294 ielem
->freedata
= 1;
2295 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2296 ielem
->data
[0] = str
[i
];
2297 ielem
->data
[1] = '\0';
2299 set_print_format(ielem
, 1, ifield
->leftadj
);
2302 if(pico_usingcolor()){
2304 if(str
[0] == '+' || str
[0] == '-'){
2306 && VAR_IND_PLUS_FORE_COLOR
2307 && VAR_IND_PLUS_BACK_COLOR
){
2308 ielem
= ifield
->ielem
;
2309 ielem
->freecolor
= 1;
2310 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2315 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2317 ielem
= ifield
->ielem
;
2319 ielem
= ifield
->ielem
->next
->next
;
2321 ielem
->freecolor
= 1;
2322 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2326 if(str
[3] == 'N' || str
[3] == 'n'){
2327 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2329 ielem
= ifield
->ielem
->next
;
2331 ielem
= ifield
->ielem
->next
->next
->next
;
2333 ielem
->freecolor
= 1;
2334 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2337 else if(str
[3] == 'R' || str
[3] == 'r'){
2338 if(VAR_IND_REC_FORE_COLOR
&& VAR_IND_REC_BACK_COLOR
){
2340 ielem
= ifield
->ielem
->next
;
2342 ielem
= ifield
->ielem
->next
->next
->next
;
2344 ielem
->freecolor
= 1;
2345 ielem
->color
= new_color_pair(VAR_IND_REC_FORE_COLOR
, VAR_IND_REC_BACK_COLOR
);
2348 else if(str
[3] == 'U' || str
[3] == 'u'){
2349 if(VAR_IND_UNS_FORE_COLOR
&& VAR_IND_UNS_BACK_COLOR
){
2351 ielem
= ifield
->ielem
->next
;
2353 ielem
= ifield
->ielem
->next
->next
->next
;
2355 ielem
->freecolor
= 1;
2356 ielem
->color
= new_color_pair(VAR_IND_UNS_FORE_COLOR
, VAR_IND_UNS_BACK_COLOR
);
2360 if(str
[4] == 'A' || str
[4] == 'a'){
2361 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2363 ielem
= ifield
->ielem
->next
->next
;
2365 ielem
= ifield
->ielem
->next
->next
->next
->next
;
2367 ielem
->freecolor
= 1;
2368 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2372 if(str
[5] == 'D' || str
[5] == 'd'){
2373 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2375 ielem
= ifield
->ielem
->next
->next
->next
;
2377 ielem
= ifield
->ielem
->next
->next
->next
->next
->next
;
2379 ielem
->freecolor
= 1;
2380 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2390 * This is a special case. The message number is
2391 * generated on the fly in the painting routine.
2392 * But the data array is allocated here in case it
2393 * is useful for the paint routine.
2395 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2399 snprintf(str
, sizeof(str
), "%-*.*s", ifield
->width
, ifield
->width
, " ");
2400 if(VAR_IND_ARR_FORE_COLOR
&& VAR_IND_ARR_BACK_COLOR
){
2401 ifield
->leftadj
= 1;
2402 ielem
= new_ielem(&ifield
->ielem
);
2403 ielem
->freedata
= 1;
2404 ielem
->data
= cpystr(str
);
2405 ielem
->datalen
= strlen(str
);
2406 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
2407 ielem
->freecolor
= 1;
2408 ielem
->color
= new_color_pair(VAR_IND_ARR_FORE_COLOR
,
2409 VAR_IND_ARR_BACK_COLOR
);
2415 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2416 if(score
== SCORE_UNDEF
){
2417 SEARCHSET
*ss
= NULL
;
2419 ss
= mail_newsearchset();
2420 ss
->first
= ss
->last
= (unsigned long) idata
->rawno
;
2423 * This looks like it might be expensive to get the
2424 * score for each message when needed but it shouldn't
2425 * be too bad because we know we have the envelope
2426 * data cached. We can't calculate all of the scores
2427 * we need for the visible messages right here in
2428 * one fell swoop because we don't have the other
2429 * envelopes yet. And we can't get the other
2430 * envelopes at this point because we may be in
2431 * the middle of a c-client callback (pine_imap_env).
2432 * (Actually we could, because we know whether or
2433 * not we're in the callback because of the no_fetch
2435 * We have another problem if the score rules depend
2436 * on something other than envelope data. I guess they
2437 * only do that if they have an alltext (search the
2438 * text of the message) definition. So, we're going
2439 * to pass no_fetch to calculate_scores so that it
2440 * can return an error if we need the text data but
2441 * can't get it because of no_fetch. Setting bogus
2442 * will cause us to do the scores calculation later
2443 * when we are no longer in the callback.
2446 (calculate_some_scores(idata
->stream
,
2447 ss
, idata
->no_fetch
) == 0)
2449 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2450 mail_free_searchset(&ss
);
2454 snprintf(str
, sizeof(str
), "%ld", score
!= SCORE_UNDEF
? score
: 0L);
2457 case iDate
: case iMonAbb
: case iLDate
:
2458 case iSDate
: case iSTime
: case iSTime24
:
2459 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
2460 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
2461 case iSDateIsoS
: case iSDateIso
:
2462 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
2464 case iSDateTimeIsoS
: case iSDateTimeIso
:
2465 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
2467 case iSDateTimeIsoS24
: case iSDateTimeIso24
:
2468 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
2469 case iTimezone
: case iYear
: case iYear2Digit
:
2470 case iRDate
: case iDay
: case iDay2Digit
: case iMon2Digit
:
2471 case iDayOrdinal
: case iMon
: case iMonLong
:
2472 case iDayOfWeekAbb
: case iDayOfWeek
:
2473 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
2474 date_str(fetch_date(idata
), cdesc
->ctype
, 0, str
, sizeof(str
), cdesc
->monabb_width
);
2478 case iFromToNotNews
:
2483 from_str(cdesc
->ctype
, idata
, str
, sizeof(str
), ice
);
2487 if(((field
= ((addr
= fetch_to(idata
))
2489 : (addr
= fetch_cc(idata
))
2492 && !set_index_addr(idata
, field
, addr
, NULL
, BIGWIDTH
, str
))
2494 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2495 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, newsgroups
);
2500 set_index_addr(idata
, "Cc", fetch_cc(idata
), NULL
, BIGWIDTH
, str
);
2504 toaddr
= fetch_to(idata
);
2505 ccaddr
= fetch_cc(idata
);
2506 for(last_to
= toaddr
;
2507 last_to
&& last_to
->next
;
2508 last_to
= last_to
->next
)
2511 /* point end of to list temporarily at cc list */
2513 last_to
->next
= ccaddr
;
2515 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2518 last_to
->next
= NULL
;
2524 if((addr
= fetch_sender(idata
)) != NULL
)
2525 set_index_addr(idata
, "Sender", addr
, NULL
, BIGWIDTH
, str
);
2532 if((addr
= fetch_from(idata
)) && addr
->personal
){
2533 char *name
, *initials
= NULL
;
2535 name
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
2536 SIZEOF_20KBUF
, addr
->personal
);
2537 if(name
== addr
->personal
){
2538 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
2539 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
2540 name
= (char *) tmp_20k_buf
;
2544 initials
= reply_quote_initials(name
);
2545 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, initials
);
2554 if((l
= fetch_size(idata
)) < 10*1000L)
2555 snprintf(str
, sizeof(str
), "(%lu)", l
);
2557 else if(l
< 1000L*1000L - 1000L/2){
2558 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2559 snprintf(str
, sizeof(str
), "(%luK)", l
);
2561 /* 1.0M ... 99.9M */
2562 else if(l
< 1000L*100L*1000L - 100L*1000L/2){
2563 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2565 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2567 /* 100M ... 2000M */
2568 else if(l
<= 2*1000L*1000L*1000L){
2569 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2571 snprintf(str
, sizeof(str
), "(%luM)", l
);
2574 snprintf(str
, sizeof(str
), "(HUGE!)");
2580 if((l
= fetch_size(idata
)) < 100*1000L)
2581 snprintf(str
, sizeof(str
), "(%s)", comatose(l
));
2582 /* 100K ... 9,999K */
2583 else if(l
< 10L*1000L*1000L - 1000L/2){
2584 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2585 snprintf(str
, sizeof(str
), "(%sK)", comatose(l
));
2587 /* 10.0M ... 999.9M */
2588 else if(l
< 1000L*1000L*1000L - 100L*1000L/2){
2589 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2591 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2593 /* 1,000M ... 2,000M */
2594 else if(l
<= 2*1000L*1000L*1000L){
2595 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2597 snprintf(str
, sizeof(str
), "(%sM)", comatose(l
));
2600 snprintf(str
, sizeof(str
), "(HUGE!)");
2606 if((l
= fetch_size(idata
)) < 1000L)
2607 snprintf(str
, sizeof(str
), "(%lu)", l
);
2609 else if(l
< 100L*1000L - 1000L/2){
2610 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2611 snprintf(str
, sizeof(str
), "(%luK)", l
);
2614 else if(l
< 1000L*1000L - 100L*1000L/2){
2615 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= 100L*1000L/2
2617 snprintf(str
, sizeof(str
), "(.%luM)", l
);
2620 else if(l
< 1000L*100L*1000L - 1000L*1000L/2){
2621 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2623 snprintf(str
, sizeof(str
), "(%luM)", l
);
2626 else if(l
< 1000L*1000L*1000L - 100L*1000L*1000L/2){
2627 l
= l
/(100L*1000L*1000L) + (l
%(100L*1000L*1000L) >=
2628 (100L*1000L*1000L/2) ? 1L : 0L);
2629 snprintf(str
, sizeof(str
), "(.%luG)", l
);
2632 else if(l
<= 2*1000L*1000L*1000L){
2633 l
= l
/(1000L*1000L*1000L) + (l
%(1000L*1000L*1000L) >=
2634 (1000L*1000L*1000L/2) ? 1L : 0L);
2635 snprintf(str
, sizeof(str
), "(%luG)", l
);
2638 snprintf(str
, sizeof(str
), "(HUGE!)");
2642 /* From Carl Jacobsen <carl@ucsd.edu> */
2644 l
= fetch_size(idata
);
2645 l
= (l
/ 1024L) + (l
% 1024L != 0 ? 1 : 0);
2647 if(l
< 1024L) { /* 0k .. 1023k */
2648 snprintf(str
, sizeof(str
), "(%luk)", l
);
2650 } else if (l
< 100L * 1024L){ /* 1.0M .. 99.9M */
2651 snprintf(str
, sizeof(str
), "(%lu.M)", (l
* 10L) / 1024L);
2652 if ((p
= strchr(str
, '.')) != NULL
) {
2653 p
--; p
[1] = p
[0]; p
[0] = '.'; /* swap last digit & . */
2655 } else if (l
<= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2656 snprintf(str
, sizeof(str
), "(%luM)", l
/ 1024L);
2658 snprintf(str
, sizeof(str
), "(HUGE!)");
2664 if((body
= fetch_body(idata
)) != NULL
)
2668 mc
= (idata
->rawno
> 0L && idata
->stream
2669 && idata
->rawno
<= idata
->stream
->nmsgs
)
2670 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2671 if(mc
&& mc
->rfc822_size
< 6000)
2672 snprintf(str
, sizeof(str
), "(short )");
2673 else if(mc
&& mc
->rfc822_size
< 25000)
2674 snprintf(str
, sizeof(str
), "(medium )");
2675 else if(mc
&& mc
->rfc822_size
< 100000)
2676 snprintf(str
, sizeof(str
), "(long )");
2678 snprintf(str
, sizeof(str
), "(huge )");
2684 if(strucmp(body
->subtype
, "MIXED") == 0){
2687 x
= body
->nested
.part
2688 ? body
->nested
.part
->body
.type
2692 if(body
->nested
.part
->body
.size
.bytes
< 6000)
2693 snprintf(str
, sizeof(str
), "(short+ )");
2694 else if(body
->nested
.part
->body
.size
.bytes
2696 snprintf(str
, sizeof(str
), "(medium+)");
2697 else if(body
->nested
.part
->body
.size
.bytes
2699 snprintf(str
, sizeof(str
), "(long+ )");
2701 snprintf(str
, sizeof(str
), "(huge+ )");
2705 snprintf(str
, sizeof(str
), "(multi )");
2709 else if(strucmp(body
->subtype
, "DIGEST") == 0)
2710 snprintf(str
, sizeof(str
), "(digest )");
2711 else if(strucmp(body
->subtype
, "ALTERNATIVE") == 0)
2712 snprintf(str
, sizeof(str
), "(mul/alt)");
2713 else if(strucmp(body
->subtype
, "PARALLEL") == 0)
2714 snprintf(str
, sizeof(str
), "(mul/par)");
2716 snprintf(str
, sizeof(str
), "(multi )");
2721 snprintf(str
, sizeof(str
), "(message)");
2724 case TYPEAPPLICATION
:
2725 snprintf(str
, sizeof(str
), "(applica)");
2729 snprintf(str
, sizeof(str
), "(audio )");
2733 snprintf(str
, sizeof(str
), "(image )");
2737 snprintf(str
, sizeof(str
), "(video )");
2741 snprintf(str
, sizeof(str
), "(other )");
2750 if((body
= fetch_body(idata
)) &&
2751 body
->type
== TYPEMULTIPART
&&
2752 strucmp(body
->subtype
, "ALTERNATIVE") != 0){
2756 part
= body
->nested
.part
; /* 1st part, don't count */
2757 while(part
&& part
->next
&& atts
< 10){
2765 str
[0] = '0' + atts
;
2771 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 0, ice
);
2775 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 1, ice
);
2779 subj_str(idata
, str
, sizeof(str
), NoKW
, 1, 0, ice
);
2783 subj_str(idata
, str
, sizeof(str
), KW
, 0, 0, ice
);
2787 subj_str(idata
, str
, sizeof(str
), KW
, 0, 1, ice
);
2791 subj_str(idata
, str
, sizeof(str
), KW
, 1, 0, ice
);
2795 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 0, ice
);
2798 case iShortSubjKeyInit
:
2799 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 1, ice
);
2802 case iSubjKeyInitText
:
2803 subj_str(idata
, str
, sizeof(str
), KWInit
, 1, 0, ice
);
2807 case iOpeningTextNQ
:
2813 first_text
= fetch_firsttext(idata
, cdesc
->ctype
== iOpeningTextNQ
);
2816 strncpy(str
, first_text
, BIGWIDTH
);
2817 str
[BIGWIDTH
] = '\0';
2818 fs_give((void **) &first_text
);
2825 key_str(idata
, KW
, ice
);
2829 key_str(idata
, KWInit
, ice
);
2833 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
){
2834 strncpy(str
, newsgroups
, BIGWIDTH
);
2835 str
[BIGWIDTH
] = '\0';
2841 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2842 strncpy(str
, newsgroups
, sizeof(str
));
2844 if((l
= strlen(str
)) < sizeof(str
)){
2845 if(sizeof(str
) - l
< 6)
2846 strncpy(str
+l
, "...", sizeof(str
)-l
);
2849 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2850 set_index_addr(idata
, "To", fetch_to(idata
),
2851 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2856 set_index_addr(idata
, "To", fetch_to(idata
),
2857 NULL
, BIGWIDTH
, str
);
2864 set_index_addr(idata
, "To", fetch_to(idata
),
2865 NULL
, BIGWIDTH
, str
);
2866 if((l
= strlen(str
)) < sizeof(str
) &&
2867 (newsgroups
= fetch_newsgroups(idata
))){
2868 if(sizeof(str
) - l
< 6)
2869 strncpy(str
+l
, "...", sizeof(str
)-l
);
2872 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2875 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2877 strncpy(str
, newsgroups
, BIGWIDTH
);
2883 case iNewsAndRecips
:
2884 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2885 strncpy(str
, newsgroups
, BIGWIDTH
);
2887 if((l
= strlen(str
)) < BIGWIDTH
){
2888 if(BIGWIDTH
- l
< 6)
2889 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2891 toaddr
= fetch_to(idata
);
2892 ccaddr
= fetch_cc(idata
);
2893 for(last_to
= toaddr
;
2894 last_to
&& last_to
->next
;
2895 last_to
= last_to
->next
)
2898 /* point end of to list temporarily at cc list */
2900 last_to
->next
= ccaddr
;
2903 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2904 set_index_addr(idata
, "To", toaddr
,
2905 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2910 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2913 last_to
->next
= NULL
;
2919 case iRecipsAndNews
:
2920 toaddr
= fetch_to(idata
);
2921 ccaddr
= fetch_cc(idata
);
2922 for(last_to
= toaddr
;
2923 last_to
&& last_to
->next
;
2924 last_to
= last_to
->next
)
2927 /* point end of to list temporarily at cc list */
2929 last_to
->next
= ccaddr
;
2931 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2934 last_to
->next
= NULL
;
2936 if((l
= strlen(str
)) < BIGWIDTH
&&
2937 (newsgroups
= fetch_newsgroups(idata
))){
2938 if(BIGWIDTH
- l
< 6)
2939 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2942 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2945 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2947 strncpy(str
, newsgroups
, BIGWIDTH
);
2956 prio_str(idata
, cdesc
->ctype
, ice
);
2960 header_str(idata
, cdesc
->hdrtok
, ice
);
2964 strncpy(str
, (cdesc
->hdrtok
&& cdesc
->hdrtok
->hdrname
) ? cdesc
->hdrtok
->hdrname
: "", sizeof(str
));
2965 str
[sizeof(str
)-1] = '\0';
2973 * If the element wasn't already filled in above, do it here.
2976 ielem
= new_ielem(&ifield
->ielem
);
2978 if((color
= hdr_color(itokens
[itokensinv
[cdesc
->ctype
].ctype
].name
, NULL
, ps_global
->index_token_colors
)) != NULL
){
2979 if(pico_usingcolor()){
2980 ielem
->color
= new_color_pair(color
->fg
, color
->bg
);
2981 ielem
->type
= eTypeCol
;
2983 free_color_pair(&color
);
2986 ielem
->freedata
= 1;
2987 ielem
->data
= cpystr(str
);
2988 ielem
->datalen
= strlen(str
);
2990 if(fromfield
&& pico_usingcolor()
2991 && ps_global
->VAR_IND_FROM_FORE_COLOR
2992 && ps_global
->VAR_IND_FROM_BACK_COLOR
){
2993 ielem
->type
= eTypeCol
;
2994 ielem
->freecolor
= 1;
2995 ielem
->color
= new_color_pair(ps_global
->VAR_IND_FROM_FORE_COLOR
,
2996 ps_global
->VAR_IND_FROM_BACK_COLOR
);
2998 * This space is here so that if the text does
2999 * not extend all the way to the end of the field then
3000 * we'll switch the color back and paint the rest of the
3001 * field in the Normal color or the index line color.
3003 ielem
= new_ielem(&ielem
);
3004 ielem
->freedata
= 1;
3005 ielem
->data
= cpystr(" ");
3008 else if((cdesc
->ctype
== iOpeningText
|| cdesc
->ctype
== iOpeningTextNQ
)
3009 && pico_usingcolor()
3010 && ps_global
->VAR_IND_OP_FORE_COLOR
3011 && ps_global
->VAR_IND_OP_BACK_COLOR
){
3012 ielem
->type
= eTypeCol
;
3013 ielem
->freecolor
= 1;
3014 ielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
,
3015 ps_global
->VAR_IND_OP_BACK_COLOR
);
3017 * This space is here so that if the text does
3018 * not extend all the way to the end of the field then
3019 * we'll switch the color back and paint the rest of the
3020 * field in the Normal color or the index line color.
3022 ielem
= new_ielem(&ielem
);
3023 ielem
->freedata
= 1;
3024 ielem
->data
= cpystr(" ");
3028 ifield
->leftadj
= (cdesc
->adjustment
== Left
) ? 1 : 0;
3029 set_ielem_widths_in_field(ifield
);
3033 ice
->widths_done
= 1;
3034 ice
->id
= ice_hash(ice
);
3037 * Now we have to put the temporary copy of ice back as the
3040 icep
= fetch_ice_ptr(idata
->stream
, idata
->rawno
);
3042 free_ice(icep
); /* free what is already there */
3051 format_thread_index_line(INDEXDATA_S
*idata
)
3053 char *p
, buffer
[BIGWIDTH
+1];
3054 int thdlen
, space_left
, i
;
3055 PINETHRD_S
*thrd
= NULL
;
3056 ICE_S
*ice
, *tice
= NULL
, **ticep
= NULL
;
3059 int (*save_sfstr_func
)(void);
3060 struct variable
*vars
= ps_global
->vars
;
3062 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3063 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
3065 space_left
= ps_global
->ttyo
->screen_cols
;
3067 if(ps_global
->msgmap
->max_thrdno
< 1000)
3069 else if(ps_global
->msgmap
->max_thrdno
< 10000)
3071 else if(ps_global
->msgmap
->max_thrdno
< 100000)
3076 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
3078 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
3080 if(!thrd
|| !ice
) /* can't happen? */
3084 tice
= (ICE_S
*) fs_get(sizeof(*tice
));
3085 memset(tice
, 0, sizeof(*tice
));
3094 free_ifield(&tice
->ifield
);
3097 tice
= copy_ice(tice
);
3099 if(space_left
>= 3){
3103 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
3104 status
= status_symbol_for_thread(idata
->stream
, thrd
, iStatus
);
3106 if((p
-buffer
)+3 < sizeof(buffer
)){
3115 ifield
= new_ifield(&tice
->ifield
);
3116 ifield
->ctype
= iStatus
;
3118 ifield
->leftadj
= 1;
3119 for(i
= 0; i
< 3; i
++){
3120 ielem
= new_ielem(&ifield
->ielem
);
3121 ielem
->freedata
= 1;
3122 ielem
->data
= (char *) fs_get(2 * sizeof(char));
3123 ielem
->data
[0] = p
[i
];
3124 ielem
->data
[1] = '\0';
3126 set_print_format(ielem
, 1, ifield
->leftadj
);
3129 if(pico_usingcolor()){
3131 && VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
3132 ielem
= ifield
->ielem
;
3133 ielem
->freecolor
= 1;
3134 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3135 VAR_IND_IMP_BACK_COLOR
);
3136 if(F_ON(F_COLOR_LINE_IMPORTANT
, ps_global
))
3137 tice
->linecolor
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3138 VAR_IND_IMP_BACK_COLOR
);
3140 else if((to_us
== '+' || to_us
== '-')
3141 && VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
3142 ielem
= ifield
->ielem
;
3143 ielem
->freecolor
= 1;
3144 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
,
3145 VAR_IND_PLUS_BACK_COLOR
);
3149 && VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
3150 ielem
= ifield
->ielem
->next
->next
;
3151 ielem
->freecolor
= 1;
3152 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
,
3153 VAR_IND_DEL_BACK_COLOR
);
3155 else if(status
== 'N'
3156 && VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
3157 ielem
= ifield
->ielem
->next
->next
;
3158 ielem
->freecolor
= 1;
3159 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
,
3160 VAR_IND_NEW_BACK_COLOR
);
3165 if(space_left
>= thdlen
+1){
3169 snprintf(p
, sizeof(buffer
), "%*.*s", thdlen
, thdlen
, "");
3170 space_left
-= thdlen
;
3172 ifield
= new_ifield(&tice
->ifield
);
3173 ifield
->ctype
= iMessNo
;
3174 ifield
->width
= thdlen
;
3175 ifield
->leftadj
= 0;
3176 ielem
= new_ielem(&ifield
->ielem
);
3177 ielem
->freedata
= 1;
3178 ielem
->data
= cpystr(p
);
3179 ielem
->datalen
= strlen(p
);
3180 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3183 if(space_left
>= 7){
3188 date_str(fetch_date(idata
), iDate
, 0, p
, sizeof(buffer
), 0);
3189 if(sizeof(buffer
) > 6)
3192 if(strlen(p
) < 6 && (sizeof(buffer
)) > 6){
3195 for(q
= p
+ strlen(p
); q
< p
+ 6; q
++)
3201 ifield
= new_ifield(&tice
->ifield
);
3202 ifield
->ctype
= iDate
;
3204 ifield
->leftadj
= 1;
3205 ielem
= new_ielem(&ifield
->ielem
);
3206 ielem
->freedata
= 1;
3207 ielem
->data
= cpystr(p
);
3208 ielem
->datalen
= ifield
->width
;
3209 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3214 int from_width
, subj_width
, bigthread_adjust
;
3216 char from
[BIGWIDTH
+1];
3221 in_thread
= count_lflags_in_thread(idata
->stream
, thrd
,
3222 ps_global
->msgmap
, MN_NONE
);
3225 if(in_thread
== 1 && THRD_AUTO_VIEW())
3226 snprintf(tcnt
, sizeof(tcnt
), " ");
3228 snprintf(tcnt
, sizeof(tcnt
), "(%ld)", in_thread
);
3230 bigthread_adjust
= MAX(0, strlen(tcnt
) - 3);
3232 /* third of the rest */
3233 from_width
= MAX((space_left
-1)/3 - bigthread_adjust
, 1);
3236 subj_width
= space_left
- from_width
- 1;
3238 if(strlen(tcnt
) > subj_width
)
3239 tcnt
[subj_width
] = '\0';
3242 save_sfstr_func
= pith_opt_truncate_sfstr
;
3243 pith_opt_truncate_sfstr
= NULL
;
3244 from_str(iFromTo
, idata
, from
, sizeof(from
), tice
);
3245 pith_opt_truncate_sfstr
= save_sfstr_func
;
3247 ifield
= new_ifield(&tice
->ifield
);
3248 ifield
->leftadj
= 1;
3249 ielem
= new_ielem(&ifield
->ielem
);
3250 ielem
->freedata
= 1;
3251 ielem
->type
= eTypeCol
;
3252 ielem
->data
= cpystr(from
);
3253 ielem
->datalen
= strlen(from
);
3254 ifield
->width
= from_width
;
3255 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3256 ifield
->ctype
= iFrom
;
3257 if(from_width
> 0 && pico_usingcolor()
3258 && VAR_IND_FROM_FORE_COLOR
&& VAR_IND_FROM_BACK_COLOR
){
3259 ielem
->freecolor
= 1;
3260 ielem
->color
= new_color_pair(VAR_IND_FROM_FORE_COLOR
,
3261 VAR_IND_FROM_BACK_COLOR
);
3264 ifield
= new_ifield(&tice
->ifield
);
3265 ifield
->leftadj
= 0;
3266 ielem
= new_ielem(&ifield
->ielem
);
3267 ielem
->freedata
= 1;
3268 ielem
->data
= cpystr(tcnt
);
3269 ielem
->datalen
= strlen(tcnt
);
3270 ifield
->width
= ielem
->datalen
;
3271 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3272 ifield
->ctype
= iAtt
; /* not used, except that it isn't special */
3274 subj_width
-= strlen(tcnt
);
3281 if(idata
->bogus
< 2)
3282 snprintf(buffer
, sizeof(buffer
), "%-.*s", BIGWIDTH
,
3283 _("[ No Message Text Available ]"));
3287 save_sfstr_func
= pith_opt_truncate_sfstr
;
3288 pith_opt_truncate_sfstr
= NULL
;
3289 subj_str(idata
, buffer
, sizeof(buffer
), NoKW
, 0, 0, NULL
);
3290 pith_opt_truncate_sfstr
= save_sfstr_func
;
3293 ifield
= new_ifield(&tice
->ifield
);
3294 ifield
->leftadj
= 1;
3295 ielem
= new_ielem(&ifield
->ielem
);
3296 ielem
->freedata
= 1;
3297 ielem
->type
= eTypeCol
;
3298 ielem
->data
= cpystr(buffer
);
3299 ielem
->datalen
= strlen(buffer
);
3300 ifield
->width
= subj_width
;
3301 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3302 ifield
->ctype
= iSubject
;
3303 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR
&& VAR_IND_SUBJ_BACK_COLOR
){
3304 ielem
->freecolor
= 1;
3305 ielem
->color
= new_color_pair(VAR_IND_SUBJ_FORE_COLOR
,
3306 VAR_IND_SUBJ_BACK_COLOR
);
3310 else if(space_left
> 1){
3311 snprintf(p
, sizeof(buffer
)-(p
-buffer
), "%-.*s", space_left
-1, " ");
3312 ifield
= new_ifield(&tice
->ifield
);
3313 ifield
->leftadj
= 1;
3314 ielem
= new_ielem(&ifield
->ielem
);
3315 ielem
->freedata
= 1;
3316 ielem
->data
= cpystr(p
);
3317 ielem
->datalen
= strlen(p
);
3318 ifield
->width
= space_left
-1;
3319 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3320 ifield
->ctype
= iSubject
;
3323 tice
->widths_done
= 1;
3324 tice
->id
= ice_hash(tice
);
3327 free_ice(ticep
); /* free what is already there */
3336 * Print the fields of ice in buf with a single space between fields.
3338 * Args buf -- place to put the line
3339 * ice -- the data for the line
3340 * msgno -- this is the msgno to be used, blanks if <= 0
3342 * Returns a pointer to buf.
3345 simple_index_line(char *buf
, size_t buflen
, ICE_S
*ice
, long int msgno
)
3348 IFIELD_S
*ifield
, *previfield
= NULL
;
3352 alpine_panic("NULL buf in simple_index_line()");
3361 for(ifield
= ice
->ifield
; ifield
&& p
-buf
< buflen
; ifield
= ifield
->next
){
3363 /* space between fields */
3364 if(ifield
!= ice
->ifield
&& !(previfield
&& previfield
->ctype
== iText
))
3367 /* message number string is generated on the fly */
3368 if(ifield
->ctype
== iMessNo
){
3369 ielem
= ifield
->ielem
;
3370 if(ielem
&& ielem
->datalen
>= ifield
->width
){
3372 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.ld", ifield
->width
, msgno
);
3374 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.*s", ifield
->width
, ifield
->width
, "");
3378 for(ielem
= ifield
->ielem
;
3379 ielem
&& ielem
->print_format
&& p
-buf
< buflen
;
3380 ielem
= ielem
->next
){
3385 bytes_added
= utf8_pad_to_width(p
, src
,
3386 buflen
-(p
-buf
) * sizeof(char),
3387 ielem
->wid
, ifield
->leftadj
);
3391 previfield
= ifield
;
3398 buf
[buflen
-1] = '\0';
3405 * Look in current mail_stream for matches for messages in the searchset
3406 * which match a color rule pattern. Return the color.
3407 * The searched bit will be set for all of the messages which match the
3408 * first pattern which has a match.
3410 * Args stream -- the mail stream
3411 * searchset -- restrict attention to this set of messages
3412 * pstate -- The pattern state. On the first call it will be Null.
3413 * Null means start over with a new first_pattern.
3414 * After that it will be pointing to our local PAT_STATE
3415 * so that next_pattern goes to the next one after the
3416 * ones we've already checked.
3418 * Returns 0 if no match, 1 if a match.
3419 * The color that goes with the matched rule in returned_color.
3420 * It may be NULL, which indicates default.
3423 get_index_line_color(MAILSTREAM
*stream
, SEARCHSET
*searchset
,
3424 PAT_STATE
**pstate
, COLOR_PAIR
**returned_color
)
3427 long rflags
= ROLE_INCOL
;
3428 COLOR_PAIR
*color
= NULL
;
3430 static PAT_STATE localpstate
;
3432 dprint((7, "get_index_line_color\n"));
3435 *returned_color
= NULL
;
3438 pat
= next_pattern(*pstate
);
3440 *pstate
= &localpstate
;
3441 if(!nonempty_patterns(rflags
, *pstate
))
3445 pat
= first_pattern(*pstate
);
3450 /* Go through the possible roles one at a time until we get a match. */
3451 while(!match
&& pat
){
3452 if(match_pattern(pat
->patgrp
, stream
, searchset
, NULL
,
3453 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
3454 if(!pat
->action
|| pat
->action
->bogus
)
3458 if(pat
->action
&& pat
->action
->incol
)
3459 color
= new_color_pair(pat
->action
->incol
->fg
,
3460 pat
->action
->incol
->bg
);
3463 pat
= next_pattern(*pstate
);
3467 if(match
&& returned_color
)
3468 *returned_color
= color
;
3478 index_in_overview(MAILSTREAM
*stream
)
3480 INDEX_COL_S
*cdesc
= NULL
;
3482 if(!(stream
->mailbox
&& IS_REMOTE(stream
->mailbox
)))
3483 return(FALSE
); /* no point! */
3485 if(stream
->dtb
&& stream
->dtb
->name
&& !strcmp(stream
->dtb
->name
, "nntp")){
3490 for(cdesc
= ps_global
->index_disp_format
;
3491 cdesc
->ctype
!= iNothing
;
3493 switch(cdesc
->ctype
){
3494 case iTo
: /* can't be satisfied by XOVER */
3495 case iSender
: /* ... or specifically handled */
3496 case iDescripSize
: /* ... in news case */
3511 * fetch_from - called to get a the index entry's "From:" field
3514 resent_to_us(INDEXDATA_S
*idata
)
3516 if(!idata
->valid_resent_to
){
3517 static char *fields
[] = {"Resent-To", NULL
};
3520 if(idata
->no_fetch
){
3521 idata
->bogus
= 1; /* don't do this */
3525 if((h
= pine_fetchheader_lines(idata
->stream
,idata
->rawno
,NULL
,fields
)) != NULL
){
3526 idata
->resent_to_us
= parsed_resent_to_us(h
);
3527 fs_give((void **) &h
);
3530 idata
->valid_resent_to
= 1;
3533 return(idata
->resent_to_us
);
3538 parsed_resent_to_us(char *h
)
3541 ADDRESS
*addr
= NULL
;
3544 if((p
= strindex(h
, ':')) != NULL
){
3545 for(q
= ++p
; (q
= strpbrk(q
, "\015\012")) != NULL
; q
++)
3546 *q
= ' '; /* quash junk */
3548 rfc822_parse_adrlist(&addr
, p
, ps_global
->maildomain
);
3550 rv
= address_is_us(addr
, ps_global
);
3551 mail_free_address(&addr
);
3561 * fetch_from - called to get a the index entry's "From:" field
3564 fetch_from(INDEXDATA_S
*idata
)
3566 if(idata
->no_fetch
) /* implies from is valid */
3567 return(idata
->from
);
3568 else if(idata
->bogus
)
3573 /* c-client call's just cache access at this point */
3574 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3585 * fetch_to - called to get a the index entry's "To:" field
3588 fetch_to(INDEXDATA_S
*idata
)
3590 if(idata
->no_fetch
){ /* check for specific validity */
3594 idata
->bogus
= 1; /* can't give 'em what they want */
3596 else if(idata
->bogus
){
3597 idata
->bogus
= 2; /* elevate bogosity */
3602 /* c-client call's just cache access at this point */
3603 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3614 * fetch_cc - called to get a the index entry's "Cc:" field
3617 fetch_cc(INDEXDATA_S
*idata
)
3619 if(idata
->no_fetch
){ /* check for specific validity */
3623 idata
->bogus
= 1; /* can't give 'em what they want */
3625 else if(idata
->bogus
){
3626 idata
->bogus
= 2; /* elevate bogosity */
3631 /* c-client call's just cache access at this point */
3632 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3644 * fetch_sender - called to get a the index entry's "Sender:" field
3647 fetch_sender(INDEXDATA_S
*idata
)
3649 if(idata
->no_fetch
){ /* check for specific validity */
3650 if(idata
->valid_sender
)
3651 return(idata
->sender
);
3653 idata
->bogus
= 1; /* can't give 'em what they want */
3655 else if(idata
->bogus
){
3656 idata
->bogus
= 2; /* elevate bogosity */
3661 /* c-client call's just cache access at this point */
3662 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3663 return(env
->sender
);
3673 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3676 fetch_newsgroups(INDEXDATA_S
*idata
)
3678 if(idata
->no_fetch
){ /* check for specific validity */
3679 if(idata
->valid_news
)
3680 return(idata
->newsgroups
);
3682 idata
->bogus
= 1; /* can't give 'em what they want */
3684 else if(idata
->bogus
){
3685 idata
->bogus
= 2; /* elevate bogosity */
3690 /* c-client call's just cache access at this point */
3691 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3692 return(env
->newsgroups
);
3702 * fetch_subject - called to get at the index entry's "Subject:" field
3705 fetch_subject(INDEXDATA_S
*idata
)
3707 if(idata
->no_fetch
) /* implies subject is valid */
3708 return(idata
->subject
);
3709 else if(idata
->bogus
)
3714 /* c-client call's just cache access at this point */
3715 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3716 return(env
->subject
);
3726 * Return an allocated copy of the first few characters from the body
3727 * of the message for possible use in the index screen.
3729 * Maybe we could figure out some way to do aggregate calls to get
3730 * this info for all the lines in view instead of all the one at a
3731 * time calls we're doing now.
3734 fetch_firsttext(INDEXDATA_S
*idata
, int delete_quotes
)
3738 char *firsttext
= NULL
;
3741 long partial_fetch_len
= 0L;
3742 SEARCHSET
*ss
, **sset
;
3747 * Prevent wild prefetch, just get the one we're after.
3748 * Can we get this somehow in the overview call in build_header_work?
3750 ss
= mail_newsearchset();
3751 ss
->first
= idata
->rawno
;
3752 sset
= (SEARCHSET
**) mail_parameters(idata
->stream
,
3754 (void *) idata
->stream
);
3758 if((env
= pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
)) != NULL
){
3760 char *subtype
= NULL
;
3763 if((body
->type
== TYPETEXT
3764 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3766 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3767 && body
->nested
.part
->body
.type
== TYPETEXT
3768 && (subtype
=body
->nested
.part
->body
.subtype
)
3769 && ALLOWED_SUBTYPE(subtype
))
3771 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3772 && body
->nested
.part
->body
.type
== TYPEMULTIPART
3773 && body
->nested
.part
->body
.nested
.part
3774 && body
->nested
.part
->body
.nested
.part
->body
.type
== TYPETEXT
3775 && (subtype
=body
->nested
.part
->body
.nested
.part
->body
.subtype
)
3776 && ALLOWED_SUBTYPE(subtype
))){
3778 if((so
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
3782 int one_space_done
= 0;
3784 if(partial_fetch_len
== 0L){
3785 if(subtype
&& !strucmp(subtype
, "html"))
3786 partial_fetch_len
= 1024L;
3787 else if(subtype
&& !strucmp(subtype
, "plain"))
3788 partial_fetch_len
= delete_quotes
? 128L : 64L;
3790 partial_fetch_len
= 256L;
3793 if((body
->type
== TYPETEXT
3794 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3796 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3797 && body
->nested
.part
->body
.type
== TYPETEXT
3798 && (subtype
=body
->nested
.part
->body
.subtype
)
3799 && ALLOWED_SUBTYPE(subtype
)))
3804 gf_set_so_writec(&pc
, so
);
3805 success
= get_body_part_text(idata
->stream
, body
, idata
->rawno
,
3806 partno
, partial_fetch_len
, pc
,
3808 GBPT_NOINTR
| GBPT_PEEK
|
3809 (delete_quotes
? GBPT_DELQUOTES
: 0));
3810 gf_clear_so_writec(so
);
3815 while(p
-buf
< sizeof(buf
)-1 && so_readc(&c
, so
)){
3816 /* delete leading whitespace */
3817 if(p
== buf
&& isspace(c
))
3819 /* and include just one space per run of whitespace */
3820 else if(isspace(c
)){
3821 if(!one_space_done
){
3839 firsttext
= fs_get((l
+1) * sizeof(char));
3840 firsttext
[0] = '\0';
3841 iutf8ncpy(firsttext
, buf
, l
);
3842 firsttext
[l
] = '\0';
3843 removing_trailing_white_space(firsttext
);
3849 /* first if means we didn't fetch all of the data */
3850 if(!(success
> 1 && success
< partial_fetch_len
)){
3851 if(partial_fetch_len
< 4096L
3852 && (!firsttext
|| utf8_width(firsttext
) < 50)){
3854 fs_give((void **) &firsttext
);
3856 partial_fetch_len
= 4096L;
3866 mail_free_searchset(&ss
);
3873 * fetch_date - called to get at the index entry's "Date:" field
3876 fetch_date(INDEXDATA_S
*idata
)
3878 if(idata
->no_fetch
) /* implies date is valid */
3879 return(idata
->date
);
3880 else if(idata
->bogus
)
3885 /* c-client call's just cache access at this point */
3886 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3887 return((char *) env
->date
);
3897 * fetch_header - called to get at the index entry's "Hdrname:" field
3900 fetch_header(INDEXDATA_S
*idata
, char *hdrname
)
3904 else if(idata
->bogus
)
3907 char *h
, *p
, *q
, *decoded
, *fields
[2];
3908 size_t retsize
, decsize
;
3910 unsigned char *decode_buf
= NULL
;
3912 fields
[0] = hdrname
;
3914 if(hdrname
&& hdrname
[0]
3915 && (h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
3918 if(strlen(h
) < strlen(hdrname
) + 1){
3919 fs_give((void **) &h
);
3923 /* skip "hdrname:" */
3924 for(p
= h
+ strlen(hdrname
) + 1;
3925 *p
&& isspace((unsigned char)*p
); p
++)
3928 decsize
= (4 * strlen(p
)) + 1;
3929 decode_buf
= (unsigned char *) fs_get(decsize
* sizeof(unsigned char));
3930 decoded
= (char *) rfc1522_decode_to_utf8(decode_buf
, decsize
, p
);
3933 retsize
= strlen(decoded
);
3934 q
= ret
= (char *) fs_get((retsize
+1) * sizeof(char));
3937 while(q
-ret
< retsize
&& *p
){
3938 if(*p
== '\015' || *p
== '\012')
3940 else if(*p
== '\t'){
3950 fs_give((void **) &h
);
3952 fs_give((void **) &decode_buf
);
3965 * fetch_size - called to get at the index entry's "size" field
3968 fetch_size(INDEXDATA_S
*idata
)
3970 if(idata
->no_fetch
) /* implies size is valid */
3971 return(idata
->size
);
3972 else if(idata
->bogus
)
3977 if(idata
->stream
&& idata
->rawno
> 0L
3978 && idata
->rawno
<= idata
->stream
->nmsgs
3979 && (mc
= mail_elt(idata
->stream
, idata
->rawno
)))
3980 return(mc
->rfc822_size
);
3990 * fetch_body - called to get a the index entry's body structure
3993 fetch_body(INDEXDATA_S
*idata
)
3997 if(idata
->bogus
|| idata
->no_fetch
){
4002 if(pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
))
4011 * s is at least size width+1
4014 set_index_addr(INDEXDATA_S
*idata
,
4016 struct mail_address
*addr
,
4022 char *p
, *stmp
= NULL
, *sptr
;
4023 char *save_personal
= NULL
;
4028 for(atmp
= addr
; idata
->stream
&& atmp
; atmp
= atmp
->next
)
4029 if(atmp
->host
&& atmp
->host
[0] == '.'){
4030 char *pref
, *h
, *fields
[2];
4032 if(idata
->no_fetch
){
4039 if((h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
4040 NULL
, fields
)) != NULL
){
4041 if(strlen(h
) < strlen(field
) + 1){
4046 for(p
= h
+ strlen(field
) + 1;
4047 *p
&& isspace((unsigned char)*p
); p
++)
4052 sptr
= stmp
= (char *) fs_get((orig_width
+1) * sizeof(char));
4055 for(pref
= prefix
; pref
&& *pref
; pref
++)
4064 if(*p
== '\015' || *p
== '\012')
4065 p
++; /* skip CR LF */
4068 else if(*p
== '\t'){
4075 *sptr
= '\0'; /* tie off return string */
4078 iutf8ncpy(s
, stmp
, orig_width
+1);
4079 s
[orig_width
] = '\0';
4080 fs_give((void **) &stmp
);
4083 fs_give((void **) &h
);
4086 /* else fall thru and display what c-client gave us */
4089 if(addr
&& !addr
->next
/* only one address */
4090 && addr
->host
/* not group syntax */
4091 && addr
->personal
&& addr
->personal
[0]){ /* there is a personal name */
4092 char buftmp
[MAILTMPLEN
];
4095 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4096 strncpy(s
, prefix
, width
+1);
4098 snprintf(buftmp
, sizeof(buftmp
), "%s", addr
->personal
);
4099 p
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
4100 SIZEOF_20KBUF
, buftmp
);
4101 removing_leading_and_trailing_white_space(p
);
4103 iutf8ncpy(s
+ l
, p
, width
- l
);
4110 save_personal
= addr
->personal
;
4111 addr
->personal
= NULL
;
4119 a_string
= addr_list_string(addr
, NULL
, 0);
4121 addr
->personal
= save_personal
;
4123 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4124 strncpy(s
, prefix
, width
+1);
4126 iutf8ncpy(s
+ l
, a_string
, width
- l
);
4129 fs_give((void **)&a_string
);
4135 addr
->personal
= save_personal
;
4142 index_data_env(INDEXDATA_S
*idata
, ENVELOPE
*env
)
4149 idata
->from
= env
->from
;
4150 idata
->to
= env
->to
;
4151 idata
->cc
= env
->cc
;
4152 idata
->sender
= env
->sender
;
4153 idata
->subject
= env
->subject
;
4154 idata
->date
= (char *) env
->date
;
4155 idata
->newsgroups
= env
->newsgroups
;
4157 idata
->valid_to
= 1; /* signal that everythings here */
4158 idata
->valid_cc
= 1;
4159 idata
->valid_sender
= 1;
4160 idata
->valid_news
= 1;
4165 * Put a string representing the date into str. The source date is
4166 * in the string datesrc. The format to be used is in type.
4167 * Notice that type is an IndexColType, but really only a subset of
4168 * IndexColType types are allowed.
4170 * Args datesrc -- The source date string
4171 * type -- What type of output we want
4172 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4173 * str -- Put the answer here.
4174 * str_len -- Length of str
4175 * monabb_width -- This is a hack to get dates to line up right. For
4176 * example, in French (but without accents here)
4180 * For this monabb_width would be 5.
4183 date_str(char *datesrc
, IndexColType type
, int v
, char *str
, size_t str_len
,
4186 char year4
[5], /* 4 digit year */
4187 yearzero
[3], /* zero padded, 2-digit year */
4188 monzero
[3], /* zero padded, 2-digit month */
4189 mon
[3], /* 1 or 2-digit month, no pad */
4190 dayzero
[3], /* zero padded, 2-digit day */
4191 day
[3], /* 1 or 2-digit day, no pad */
4192 dayord
[3], /* 2-letter ordinal label */
4193 monabb
[10], /* 3-letter month abbrev */
4194 /* actually maybe not 3 if localized */
4195 hour24
[3], /* 2-digit, 24 hour clock hour */
4196 hour12
[3], /* 12 hour clock hour, no pad */
4197 minzero
[3], /* zero padded, 2-digit minutes */
4198 timezone
[6]; /* timezone, like -0800 or +... */
4200 int curtype
, lastmonthtype
, lastyeartype
, preftype
;
4201 int sdatetimetype
, sdatetime24type
;
4203 #define TODAYSTR N_("Today")
4205 curtype
= (type
== iCurDate
||
4206 type
== iCurDateIso
||
4207 type
== iCurDateIsoS
||
4208 type
== iCurPrefDate
||
4209 type
== iCurPrefDateTime
||
4210 type
== iCurPrefTime
||
4211 type
== iCurTime24
||
4212 type
== iCurTime12
||
4214 type
== iCurDay2Digit
||
4215 type
== iCurDayOfWeek
||
4216 type
== iCurDayOfWeekAbb
||
4218 type
== iCurMon2Digit
||
4219 type
== iCurMonLong
||
4220 type
== iCurMonAbb
||
4222 type
== iCurYear2Digit
);
4223 lastmonthtype
= (type
== iLstMon
||
4224 type
== iLstMon2Digit
||
4225 type
== iLstMonLong
||
4226 type
== iLstMonAbb
||
4227 type
== iLstMonYear
||
4228 type
== iLstMonYear2Digit
);
4229 lastyeartype
= (type
== iLstYear
||
4230 type
== iLstYear2Digit
);
4231 sdatetimetype
= (type
== iSDateTime
||
4232 type
== iSDateTimeIso
||
4233 type
== iSDateTimeIsoS
||
4234 type
== iSDateTimeS1
||
4235 type
== iSDateTimeS2
||
4236 type
== iSDateTimeS3
||
4237 type
== iSDateTimeS4
||
4238 type
== iSDateTime24
||
4239 type
== iSDateTimeIso24
||
4240 type
== iSDateTimeIsoS24
||
4241 type
== iSDateTimeS124
||
4242 type
== iSDateTimeS224
||
4243 type
== iSDateTimeS324
||
4244 type
== iSDateTimeS424
);
4245 sdatetime24type
= (type
== iSDateTime24
||
4246 type
== iSDateTimeIso24
||
4247 type
== iSDateTimeIsoS24
||
4248 type
== iSDateTimeS124
||
4249 type
== iSDateTimeS224
||
4250 type
== iSDateTimeS324
||
4251 type
== iSDateTimeS424
);
4252 preftype
= (type
== iPrefDate
||
4253 type
== iPrefDateTime
||
4254 type
== iPrefTime
||
4255 type
== iCurPrefDate
||
4256 type
== iCurPrefDateTime
||
4257 type
== iCurPrefTime
);
4261 if(!(datesrc
&& datesrc
[0]) && !(curtype
|| lastmonthtype
|| lastyeartype
))
4264 if(curtype
|| lastmonthtype
|| lastyeartype
){
4268 parse_date(dbuf
, &d
);
4272 else if(lastmonthtype
){
4281 parse_date(F_ON(F_DATES_TO_LOCAL
,ps_global
)
4282 ? convert_date_to_local(datesrc
) : datesrc
, &d
);
4283 if(d
.year
== -1 || d
.month
== -1 || d
.day
== -1){
4285 sdatetime24type
= 0;
4288 case iSDate
: case iSDateTime
: case iSDateTime24
:
4292 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4293 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
4297 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4301 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4305 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4309 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4313 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4323 /* some special ones to start with */
4325 struct tm tm
, *tmptr
= NULL
;
4329 * Make sure we get the right one if we're using current time.
4332 now
= time((time_t *) 0);
4333 if(now
!= (time_t) -1)
4334 tmptr
= localtime(&now
);
4338 memset(&tm
, 0, sizeof(tm
));
4339 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4340 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4341 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4342 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4343 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4344 tm
.tm_wday
= MIN(MAX(d
.wkday
, 0), 6);
4351 our_strftime(str
, str_len
, "%x", tmptr
);
4355 our_strftime(str
, str_len
, "%X", tmptr
);
4358 case iCurPrefDateTime
:
4359 our_strftime(str
, str_len
, "%c", tmptr
);
4369 strncpy(monabb
, (d
.month
> 0 && d
.month
< 13)
4370 ? month_abbrev_locale(d
.month
) : "", sizeof(monabb
));
4371 monabb
[sizeof(monabb
)-1] = '\0';
4373 strncpy(mon
, (d
.month
> 0 && d
.month
< 13)
4374 ? int2string(d
.month
) : "", sizeof(mon
));
4375 mon
[sizeof(mon
)-1] = '\0';
4377 strncpy(day
, (d
.day
> 0 && d
.day
< 32)
4378 ? int2string(d
.day
) : "", sizeof(day
));
4379 day
[sizeof(day
)-1] = '\0';
4382 (d
.day
<= 0 || d
.day
> 31) ? "" :
4383 (d
.day
== 1 || d
.day
== 21 || d
.day
== 31) ? "st" :
4384 (d
.day
== 2 || d
.day
== 22 ) ? "nd" :
4385 (d
.day
== 3 || d
.day
== 23 ) ? "rd" : "th", sizeof(dayord
));
4387 dayord
[sizeof(dayord
)-1] = '\0';
4389 strncpy(year4
, (d
.year
>= 1000 && d
.year
< 10000)
4390 ? int2string(d
.year
) : "????", sizeof(year4
));
4391 year4
[sizeof(year4
)-1] = '\0';
4394 if((d
.year
% 100) < 10){
4396 strncpy(yearzero
+1, int2string(d
.year
% 100), sizeof(yearzero
)-1);
4399 strncpy(yearzero
, int2string(d
.year
% 100), sizeof(yearzero
));
4402 strncpy(yearzero
, "??", sizeof(yearzero
));
4404 yearzero
[sizeof(yearzero
)-1] = '\0';
4406 if(d
.month
> 0 && d
.month
< 10){
4408 strncpy(monzero
+1, int2string(d
.month
), sizeof(monzero
)-1);
4410 else if(d
.month
>= 10 && d
.month
<= 12)
4411 strncpy(monzero
, int2string(d
.month
), sizeof(monzero
));
4413 strncpy(monzero
, "??", sizeof(monzero
));
4415 monzero
[sizeof(monzero
)-1] = '\0';
4417 if(d
.day
> 0 && d
.day
< 10){
4419 strncpy(dayzero
+1, int2string(d
.day
), sizeof(dayzero
)-1);
4421 else if(d
.day
>= 10 && d
.day
<= 31)
4422 strncpy(dayzero
, int2string(d
.day
), sizeof(dayzero
));
4424 strncpy(dayzero
, "??", sizeof(dayzero
));
4426 dayzero
[sizeof(dayzero
)-1] = '\0';
4428 hr12
= (d
.hour
== 0) ? 12 :
4429 (d
.hour
> 12) ? (d
.hour
- 12) : d
.hour
;
4431 if(hr12
> 0 && hr12
<= 12)
4432 strncpy(hour12
, int2string(hr12
), sizeof(hour12
));
4434 hour12
[sizeof(hour12
)-1] = '\0';
4437 if(d
.hour
>= 0 && d
.hour
< 10){
4439 strncpy(hour24
+1, int2string(d
.hour
), sizeof(hour24
)-1);
4441 else if(d
.hour
>= 10 && d
.hour
< 24)
4442 strncpy(hour24
, int2string(d
.hour
), sizeof(hour24
));
4444 hour24
[sizeof(hour24
)-1] = '\0';
4447 if(d
.minute
>= 0 && d
.minute
< 10){
4449 strncpy(minzero
+1, int2string(d
.minute
), sizeof(minzero
)-1);
4451 else if(d
.minute
>= 10 && d
.minute
<= 60)
4452 strncpy(minzero
, int2string(d
.minute
), sizeof(minzero
));
4454 minzero
[sizeof(minzero
)-1] = '\0';
4456 if(sizeof(timezone
) > 5){
4457 if(d
.hours_off_gmt
<= 0){
4459 d
.hours_off_gmt
*= -1;
4460 d
.min_off_gmt
*= -1;
4466 if(d
.hours_off_gmt
>= 0 && d
.hours_off_gmt
< 10){
4468 strncpy(timezone
+2, int2string(d
.hours_off_gmt
), sizeof(timezone
)-2);
4470 else if(d
.hours_off_gmt
>= 10 && d
.hours_off_gmt
< 24)
4471 strncpy(timezone
+1, int2string(d
.hours_off_gmt
), sizeof(timezone
)-1);
4478 if(d
.min_off_gmt
>= 0 && d
.min_off_gmt
< 10){
4480 strncpy(timezone
+4, int2string(d
.min_off_gmt
), sizeof(timezone
)-4);
4482 else if(d
.min_off_gmt
>= 10 && d
.min_off_gmt
<= 60)
4483 strncpy(timezone
+3, int2string(d
.min_off_gmt
), sizeof(timezone
)-3);
4490 timezone
[sizeof(timezone
)-1] = '\0';
4495 /* this one is not locale-specific */
4496 snprintf(str
, str_len
, "%s%s%s %s %s",
4497 (d
.wkday
!= -1) ? day_abbrev(d
.wkday
) : "",
4498 (d
.wkday
!= -1) ? ", " : "",
4500 (d
.month
> 0 && d
.month
< 13) ? month_abbrev(d
.month
) : "",
4504 case iCurDayOfWeekAbb
:
4505 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_abbrev_locale(d
.wkday
) : "", str_len
);
4506 str
[str_len
-1] = '\0';
4510 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_name_locale(d
.wkday
) : "", str_len
);
4511 str
[str_len
-1] = '\0';
4517 strncpy(str
, year4
, str_len
);
4521 strncpy(str
, dayzero
, str_len
);
4526 strncpy(str
, monzero
, str_len
);
4529 case iCurYear2Digit
:
4530 case iLstYear2Digit
:
4531 case iLstMonYear2Digit
:
4532 strncpy(str
, yearzero
, str_len
);
4535 strncpy(str
, timezone
, str_len
);
4539 strncpy(str
, day
, str_len
);
4542 snprintf(str
, str_len
, "%s%s", day
, dayord
);
4547 if(d
.month
> 0 && d
.month
<= 12)
4548 strncpy(str
, int2string(d
.month
), str_len
);
4554 strncpy(str
, monabb
, str_len
);
4559 strncpy(str
, (d
.month
> 0 && d
.month
< 13)
4560 ? month_name_locale(d
.month
) : "", str_len
);
4565 snprintf(str
, str_len
, "%s%s%s", monabb
, (monabb
[0] && day
[0]) ? " " : "", day
);
4567 if(monabb_width
> 0)
4568 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4569 monabb_width
, monabb_width
, monabb
, day
);
4571 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4577 snprintf(str
, str_len
, "%s%s%s%s%s", monabb
,
4578 (monabb
[0] && day
[0]) ? " " : "", day
,
4579 ((monabb
[0] || day
[0]) && year4
[0]) ? ", " : "",
4582 if(monabb_width
> 0)
4583 utf8_snprintf(str
, str_len
, "%-*.*w %2s%c %4s",
4584 monabb_width
, monabb_width
,
4586 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ', year4
);
4588 snprintf(str
, str_len
, "%s %2s%c %4s", monabb
, day
,
4589 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ',
4602 if(monzero
[0] == '?' && dayzero
[0] == '?' &&
4604 snprintf(str
, str_len
, "%8s", "");
4608 snprintf(str
, str_len
, "%2s/%2s/%2s",
4609 monzero
, dayzero
, yearzero
);
4612 snprintf(str
, str_len
, "%2s/%2s/%2s",
4613 dayzero
, monzero
, yearzero
);
4616 snprintf(str
, str_len
, "%2s.%2s.%2s",
4617 dayzero
, monzero
, yearzero
);
4620 snprintf(str
, str_len
, "%2s.%2s.%2s",
4621 yearzero
, monzero
, dayzero
);
4625 snprintf(str
, str_len
, "%2s-%2s-%2s",
4626 yearzero
, monzero
, dayzero
);
4630 snprintf(str
, str_len
, "%4s-%2s-%2s",
4631 year4
, monzero
, dayzero
);
4641 snprintf(str
, str_len
, "%2s%c%2s",
4642 (hour24
[0] && minzero
[0]) ? hour24
: "",
4643 (hour24
[0] && minzero
[0]) ? ':' : ' ',
4644 (hour24
[0] && minzero
[0]) ? minzero
: "");
4648 snprintf(str
, str_len
, "%s%c%2s%s",
4649 (hour12
[0] && minzero
[0]) ? hour12
: "",
4650 (hour12
[0] && minzero
[0]) ? ':' : ' ',
4651 (hour12
[0] && minzero
[0]) ? minzero
: "",
4652 (hour12
[0] && minzero
[0] && d
.hour
< 12) ? "am" :
4653 (hour12
[0] && minzero
[0] && d
.hour
>= 12) ? "pm" :
4656 case iSDate
: case iSDateIso
: case iSDateIsoS
:
4657 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
4658 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
4659 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
4660 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
4661 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
4662 { struct date now
, last_day
;
4664 int msg_day_of_year
, now_day_of_year
, today
;
4665 int diff
, ydiff
, last_day_of_year
;
4668 parse_date(dbuf
, &now
);
4669 today
= day_of_week(&now
) + 7;
4671 if(today
>= 0+7 && today
<= 6+7){
4672 now_day_of_year
= day_of_year(&now
);
4673 msg_day_of_year
= day_of_year(&d
);
4674 ydiff
= now
.year
- d
.year
;
4676 if(msg_day_of_year
== -1)
4679 diff
= now_day_of_year
- msg_day_of_year
;
4680 else if(ydiff
== 1){
4682 last_day
.month
= 12;
4684 last_day_of_year
= day_of_year(&last_day
);
4686 diff
= now_day_of_year
+
4687 (last_day_of_year
- msg_day_of_year
);
4689 else if(ydiff
== -1){
4691 last_day
.month
= 12;
4693 last_day_of_year
= day_of_year(&last_day
);
4695 diff
= -1 * (msg_day_of_year
+
4696 (last_day_of_year
- now_day_of_year
));
4704 strncpy(str
, _(TODAYSTR
), str_len
);
4706 strncpy(str
, _("Yesterday"), str_len
);
4707 else if(diff
> 1 && diff
< 7)
4708 snprintf(str
, str_len
, "%s", day_name_locale((today
- diff
) % 7));
4710 strncpy(str
, _("Tomorrow"), str_len
);
4711 else if(diff
< -1 && diff
> -7)
4712 snprintf(str
, str_len
, _("Next %.3s!"),
4713 day_name_locale((today
- diff
) % 7));
4716 || (ydiff
== 1 && 12 + now
.month
- d
.month
< 6))){
4718 snprintf(str
, str_len
, "%s%s%s", monabb
,
4719 (monabb
[0] && day
[0]) ? " " : "", day
);
4721 if(monabb_width
> 0)
4722 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4723 monabb_width
, monabb_width
, monabb
, day
);
4725 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4729 if(msg_day_of_year
== -1 && (type
== iSDate
|| type
== iSDateTime
))
4730 type
= iSDateTimeIsoS
;
4733 case iSDate
: case iSDateTime
: case iSDateTime24
:
4737 memset(&tm
, 0, sizeof(tm
));
4738 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4739 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4740 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4741 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4742 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4743 our_strftime(str
, str_len
, "%x", &tm
);
4747 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4749 snprintf(str
, str_len
, "%s/%s/%s%s", mon
, day
, yearzero
,
4750 diff
< 0 ? "!" : "");
4752 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4753 (mon
[0] && mon
[1]) ? "" : " ",
4754 mon
, dayzero
, yearzero
,
4755 diff
< 0 ? "!" : "");
4757 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4759 snprintf(str
, str_len
, "%s/%s/%s%s", day
, mon
, yearzero
,
4760 diff
< 0 ? "!" : "");
4762 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4763 (day
[0] && day
[1]) ? "" : " ",
4764 day
, monzero
, yearzero
,
4765 diff
< 0 ? "!" : "");
4767 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4769 snprintf(str
, str_len
, "%s.%s.%s%s", day
, mon
, yearzero
,
4770 diff
< 0 ? "!" : "");
4772 snprintf(str
, str_len
, "%s%s.%s.%s%s",
4773 (day
[0] && day
[1]) ? "" : " ",
4774 day
, monzero
, yearzero
,
4775 diff
< 0 ? "!" : "");
4777 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4779 snprintf(str
, str_len
, "%s.%s.%s%s",
4780 yearzero
, monzero
, dayzero
,
4781 diff
< 0 ? "!" : "");
4783 snprintf(str
, str_len
, "%s.%s.%s%s",
4784 yearzero
, monzero
, dayzero
,
4785 diff
< 0 ? "!" : "");
4787 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4788 snprintf(str
, str_len
, "%2s-%2s-%2s",
4789 yearzero
, monzero
, dayzero
);
4791 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4792 snprintf(str
, str_len
, "%4s-%2s-%2s",
4793 year4
, monzero
, dayzero
);
4803 snprintf(str
, str_len
, "%s%s%s", monabb
,
4804 (monabb
[0] && day
[0]) ? " " : "", day
);
4806 if(monabb_width
> 0)
4807 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4808 monabb_width
, monabb_width
, monabb
, day
);
4810 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4821 str
[str_len
-1] = '\0';
4823 if(type
== iSTime
||
4824 (sdatetimetype
&& !strcmp(str
, _(TODAYSTR
)))){
4825 struct date now
, last_day
;
4826 char dbuf
[200], *Ddd
, *ampm
;
4831 parse_date(dbuf
, &now
);
4833 /* Figure out if message date lands in the past week */
4835 /* (if message dated this month or last month...) */
4836 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 1) ||
4837 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 1)){
4839 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4842 * If msg in end of last year (and we're in first bit of "this"
4843 * year), diff will be backwards; fix up by adding number of days
4844 * in last year (usually 365, but occasionally 366)...
4846 if(d
.year
== now
.year
- 1){
4848 last_day
.month
= 12;
4851 daydiff
+= day_of_year(&last_day
);
4855 daydiff
= -100; /* comfortably out of range (of past week) */
4857 /* Build 2-digit hour and am/pm indicator, used below */
4859 if(d
.hour
>= 0 && d
.hour
< 24){
4860 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4861 ampm
= (d
.hour
< 12) ? "am" : "pm";
4862 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4865 strncpy(hour12
, "??", sizeof(hour12
));
4866 hour12
[sizeof(hour12
)-1] = '\0';
4868 strncpy(hour24
, "??", sizeof(hour24
));
4869 hour24
[sizeof(hour24
)-1] = '\0';
4872 /* Build date/time in str, in format similar to that used by w(1) */
4874 if(daydiff
== 0){ /* If date is today, "HH:MMap" */
4875 if(d
.minute
>= 0 && d
.minute
< 60)
4876 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4878 strncpy(minzero
, "??", sizeof(minzero
));
4879 minzero
[sizeof(minzero
)-1] = '\0';
4882 snprintf(str
, str_len
, "%s:%s%s", sdatetime24type
? hour24
: hour12
,
4883 minzero
, sdatetime24type
? "" : ampm
);
4885 else if(daydiff
>= 1 && daydiff
< 6){ /* If <1wk ago, "DddHHap" */
4887 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4888 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4889 Ddd
= day_abbrev_locale(day_of_week(&d
));
4893 snprintf(str
, str_len
, "%s%s%s", Ddd
, hour12
, ampm
);
4895 else{ /* date is old or future, "ddMmmyy" */
4896 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4897 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4898 monabb
[sizeof(monabb
)-1] = '\0';
4900 if(d
.day
>= 1 && d
.day
<= 31)
4901 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
4903 strncpy(dayzero
, "??", sizeof(dayzero
));
4904 dayzero
[sizeof(dayzero
)-1] = '\0';
4907 if(d
.year
>= 0 && d
.year
<= 9999)
4908 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
4910 strncpy(yearzero
, "??", sizeof(yearzero
));
4911 yearzero
[sizeof(yearzero
)-1] = '\0';
4914 snprintf(str
, str_len
, "%s%s%s", dayzero
, monabb
, yearzero
);
4917 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4919 memmove(str
, str
+ 1, strlen(str
));
4925 /* This is like iSTime, but the format is different */
4926 if(type
== iSTime24
){
4927 struct date now
, last_day
;
4928 char dbuf
[200], *Ddd
, *ampm
;
4933 parse_date(dbuf
, &now
);
4935 /* (if message dated this month or last month...) */
4936 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 6) ||
4937 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 6)){
4939 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4942 * If msg in end of last year (and we're in first bit of "this"
4943 * year), diff will be backwards; fix up by adding number of days
4944 * in last year (usually 365, but occasionally 366)...
4946 if(d
.year
== now
.year
- 1){
4948 last_day
.month
= 12;
4951 daydiff
+= day_of_year(&last_day
);
4955 daydiff
= 181; /* comfortably out of range (of past week) */
4957 /* Build 2-digit hour and am/pm indicator, used below */
4959 if(d
.hour
>= 0 && d
.hour
< 24){
4960 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4961 ampm
= (d
.hour
< 12) ? "am" : "pm";
4962 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4965 strncpy(hour12
, "??", sizeof(hour12
));
4966 hour12
[sizeof(hour12
)-1] = '\0';
4968 strncpy(hour24
, "??", sizeof(hour24
));
4969 hour24
[sizeof(hour24
)-1] = '\0';
4972 /* Build date/time in str, in format similar to that used by w(1) */
4974 if(daydiff
>= 0 && daydiff
<= 6){ /* If <1wk ago, "Ddd HH:mm" */
4976 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4977 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4978 Ddd
= day_abbrev_locale(day_of_week(&d
));
4982 if(d
.minute
>= 0 && d
.minute
< 60)
4983 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4985 strncpy(minzero
, "??", sizeof(minzero
));
4986 minzero
[sizeof(minzero
)-1] = '\0';
4989 snprintf(str
, str_len
, "%s %s:%s", Ddd
, hour24
, minzero
);
4991 else if(daydiff
< 180){ /* date is "Mmm dd" */
4992 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4993 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4994 monabb
[sizeof(monabb
)-1] = '\0';
4996 if(d
.day
>= 1 && d
.day
<= 31)
4997 snprintf(dayzero
, sizeof(dayzero
), "%*d", 2, d
.day
);
4999 strncpy(dayzero
, "??", sizeof(dayzero
));
5000 dayzero
[sizeof(dayzero
)-1] = '\0';
5003 snprintf(str
, str_len
, "%s %s", monabb
, dayzero
);
5005 else { /* date is old or future, "dd/Mmm/yy" */
5006 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
5007 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
5008 monabb
[sizeof(monabb
)-1] = '\0';
5010 if(d
.day
>= 1 && d
.day
<= 31)
5011 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
5013 strncpy(dayzero
, "??", sizeof(dayzero
));
5014 dayzero
[sizeof(dayzero
)-1] = '\0';
5017 if(d
.year
>= 0 && d
.year
<= 9999)
5018 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
5020 strncpy(yearzero
, "??", sizeof(yearzero
));
5021 yearzero
[sizeof(yearzero
)-1] = '\0';
5024 snprintf(str
, str_len
, "%s/%s/%s", dayzero
, monabb
, yearzero
);
5027 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
5029 memmove(str
, str
+ 1, strlen(str
));
5038 * Format a string representing the keywords into ice.
5040 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
5042 * Args idata -- which message?
5043 * kwtype -- keywords or kw initials
5044 * ice -- index cache entry for message
5047 key_str(INDEXDATA_S
*idata
, SubjKW kwtype
, ICE_S
*ice
)
5052 COLOR_PAIR
*color
= NULL
;
5053 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5054 IELEM_S
*ielem
= NULL
;
5055 IFIELD_S
*ourifield
= NULL
;
5057 if(ice
&& ice
->ifield
){
5058 /* move to last ifield, the one we're working */
5059 for(ourifield
= ice
->ifield
;
5060 ourifield
&& ourifield
->next
;
5061 ourifield
= ourifield
->next
)
5068 if(kwtype
== KWInit
){
5069 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5070 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
5071 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
5072 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
5075 * Pick off the first initial. Since word is UTF-8 it may
5076 * take more than one byte for the first initial.
5079 if(word
&& word
[0]){
5081 unsigned long remaining_octets
;
5082 unsigned char *inputp
;
5084 remaining_octets
= strlen(word
);
5085 inputp
= (unsigned char *) word
;
5086 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5087 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5088 ielem
= new_ielem(&ourifield
->ielem
);
5089 ielem
->freedata
= 1;
5090 ielem
->datalen
= (unsigned) (inputp
- (unsigned char *) word
);
5091 ielem
->data
= (char *) fs_get((ielem
->datalen
+ 1) * sizeof(char));
5092 strncpy(ielem
->data
, word
, ielem
->datalen
);
5093 ielem
->data
[ielem
->datalen
] = '\0';
5095 if(pico_usingcolor()
5096 && ((kw
->nick
&& kw
->nick
[0]
5097 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5098 || (kw
->kw
&& kw
->kw
[0]
5099 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5100 ielem
->color
= color
;
5107 free_color_pair(&color
);
5111 else if(kwtype
== KW
){
5112 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5113 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
5116 ielem
= new_ielem(&ourifield
->ielem
);
5117 ielem
->freedata
= 1;
5118 ielem
->data
= cpystr(" ");
5124 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
5125 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
5128 ielem
= new_ielem(&ourifield
->ielem
);
5129 ielem
->freedata
= 1;
5130 ielem
->data
= cpystr(word
);
5131 ielem
->datalen
= strlen(word
);
5133 if(pico_usingcolor()
5134 && ((kw
->nick
&& kw
->nick
[0]
5135 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5136 || (kw
->kw
&& kw
->kw
[0]
5137 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5138 ielem
->color
= color
;
5144 free_color_pair(&color
);
5150 * If we're coloring some of the fields then add a dummy field
5151 * at the end that can soak up the rest of the space after the last
5152 * colored keyword. Otherwise, the last one's color will extend to
5153 * the end of the field.
5155 if(pico_usingcolor()){
5156 ielem
= new_ielem(&ourifield
->ielem
);
5157 ielem
->freedata
= 1;
5158 ielem
->data
= cpystr(" ");
5162 ourifield
->leftadj
= 1;
5163 set_ielem_widths_in_field(ourifield
);
5168 prio_str(INDEXDATA_S
*idata
, IndexColType ctype
, ICE_S
*ice
)
5170 IFIELD_S
*ourifield
= NULL
;
5171 IELEM_S
*ielem
= NULL
;
5176 if(ice
&& ice
->ifield
){
5177 /* move to last ifield, the one we're working */
5178 for(ourifield
= ice
->ifield
;
5179 ourifield
&& ourifield
->next
;
5180 ourifield
= ourifield
->next
)
5187 hdrval
= fetch_header(idata
, PRIORITYNAME
);
5189 if(hdrval
&& hdrval
[0] && isdigit(hdrval
[0])){
5191 if(v
>= 1 && v
<= 5 && v
!= 3){
5193 ielem
= new_ielem(&ourifield
->ielem
);
5194 ielem
->freedata
= 1;
5198 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5199 ielem
->data
[0] = hdrval
[0];
5200 ielem
->data
[1] = '\0';
5204 for(p
= priorities
; p
&& p
->desc
; p
++)
5209 ielem
->data
= cpystr(p
->desc
);
5214 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5215 ielem
->data
[0] = '\0';
5218 ielem
->data
[0] = '!';
5223 * We could put a Unicode downarrow in here but
5224 * we have no way of knowing if the user's font
5225 * will have it (I think).
5227 ielem
->data
[0] = 'v';
5231 ielem
->data
[1] = '\0';
5236 alpine_panic("Unhandled case in prio_str");
5241 ielem
->data
= cpystr("");
5243 if(ielem
&& ielem
->data
)
5244 ielem
->datalen
= strlen(ielem
->data
);
5246 if((v
== 1 || v
== 2) && pico_usingcolor()
5247 && ps_global
->VAR_IND_HIPRI_FORE_COLOR
5248 && ps_global
->VAR_IND_HIPRI_BACK_COLOR
){
5249 ielem
->type
= eTypeCol
;
5250 ielem
->freecolor
= 1;
5251 ielem
->color
= new_color_pair(ps_global
->VAR_IND_HIPRI_FORE_COLOR
, ps_global
->VAR_IND_HIPRI_BACK_COLOR
);
5253 else if((v
== 4 || v
== 5) && pico_usingcolor()
5254 && ps_global
->VAR_IND_LOPRI_FORE_COLOR
5255 && ps_global
->VAR_IND_LOPRI_BACK_COLOR
){
5256 ielem
->type
= eTypeCol
;
5257 ielem
->freecolor
= 1;
5258 ielem
->color
= new_color_pair(ps_global
->VAR_IND_LOPRI_FORE_COLOR
, ps_global
->VAR_IND_LOPRI_BACK_COLOR
);
5261 ourifield
->leftadj
= 1;
5262 set_ielem_widths_in_field(ourifield
);
5265 fs_give((void **) &hdrval
);
5271 header_str(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
, ICE_S
*ice
)
5273 IFIELD_S
*ourifield
= NULL
;
5274 IELEM_S
*ielem
= NULL
;
5275 char *fieldval
= NULL
;
5277 if(ice
&& ice
->ifield
){
5278 /* move to last ifield, the one we're working */
5279 for(ourifield
= ice
->ifield
;
5280 ourifield
&& ourifield
->next
;
5281 ourifield
= ourifield
->next
)
5288 fieldval
= get_fieldval(idata
, hdrtok
);
5291 ielem
= new_ielem(&ourifield
->ielem
);
5292 ielem
->freedata
= 1;
5293 ielem
->data
= fieldval
;
5294 ielem
->datalen
= strlen(fieldval
);
5296 ourifield
->leftadj
= (hdrtok
->adjustment
== Left
) ? 1 : 0;
5299 set_ielem_widths_in_field(ourifield
);
5304 get_fieldval(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
)
5307 char *hdrval
= NULL
, *testval
;
5308 char *fieldval
= NULL
, *firstval
;
5309 char *retfieldval
= NULL
;
5312 return(retfieldval
);
5314 if(hdrtok
&& hdrtok
->hdrname
&& hdrtok
->hdrname
[0])
5315 hdrval
= fetch_header(idata
, hdrtok
? hdrtok
->hdrname
: "");
5317 /* find start of fieldnum'th field */
5319 for(fieldnum
= MAX(hdrtok
->fieldnum
-1, 0);
5320 fieldnum
> 0 && fieldval
&& *fieldval
; fieldnum
--){
5323 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5324 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5325 if(testval
&& (!firstval
|| testval
< firstval
))
5329 fieldval
= firstval
;
5330 if(fieldval
&& *fieldval
)
5334 /* tie off end of field */
5335 if(fieldval
&& *fieldval
&& hdrtok
->fieldnum
> 0){
5337 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5338 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5339 if(testval
&& (!firstval
|| testval
< firstval
))
5350 retfieldval
= cpystr(fieldval
);
5353 fs_give((void **) &hdrval
);
5355 return(retfieldval
);
5360 scorevalfrommsg(MAILSTREAM
*stream
, MsgNo rawno
, HEADER_TOK_S
*hdrtok
, int no_fetch
)
5364 char *fieldval
= NULL
;
5367 memset(&idata
, 0, sizeof(INDEXDATA_S
));
5368 idata
.stream
= stream
;
5369 idata
.no_fetch
= no_fetch
;
5370 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), rawno
);
5371 idata
.rawno
= rawno
;
5372 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
5373 && (mc
= mail_elt(stream
, idata
.rawno
))){
5374 idata
.size
= mc
->rfc822_size
;
5375 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
5380 fieldval
= get_fieldval(&idata
, hdrtok
);
5383 retval
= atol(fieldval
);
5384 fs_give((void **) &fieldval
);
5391 * Put a string representing the subject into str. Idata tells us which
5392 * message we are referring to.
5394 * This means we should ensure that all data ends up being UTF-8 data.
5395 * That covers the data in ice ielems and str.
5397 * Args idata -- which message?
5398 * str -- destination buffer
5399 * strsize -- size of str buffer
5400 * kwtype -- prepend keywords or kw initials before the subject
5401 * opening -- add first text from body of message if there's room
5402 * shorten -- if on, shorten the subject.
5403 * ice -- index cache entry for message
5406 subj_str(INDEXDATA_S
*idata
, char *str
, size_t strsize
, SubjKW kwtype
, int opening
, int shorten
, ICE_S
*ice
)
5408 char *subject
, *origsubj
, *origstr
, *rawsubj
, *sptr
= NULL
;
5409 char *p
, *border
, *q
= NULL
, *free_subj
= NULL
;
5413 int depth
= 0, mult
= 2;
5415 int do_subj
= 0, truncated_tree
= 0;
5416 PINETHRD_S
*thd
, *thdorig
;
5417 IELEM_S
*ielem
= NULL
, *subjielem
= NULL
;
5418 IFIELD_S
*ourifield
= NULL
;
5424 * If we need the data at the start of the message and we're in
5425 * a c-client callback, defer the data lookup until later.
5427 if(opening
&& idata
->no_fetch
){
5432 if(ice
&& ice
->ifield
){
5433 /* move to last ifield, the one we're working on */
5434 for(ourifield
= ice
->ifield
;
5435 ourifield
&& ourifield
->next
;
5436 ourifield
= ourifield
->next
)
5440 str
[0] = str
[strsize
-1] = '\0';
5442 rawsubj
= fetch_subject(idata
);
5447 * Before we do anything else, decode the character set in the subject and
5448 * work with the result.
5450 sp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5451 SIZEOF_20KBUF
, rawsubj
);
5454 len
+= 100; /* for possible charset, escaped characters */
5455 origsubj
= fs_get((len
+1) * sizeof(unsigned char));
5458 iutf8ncpy(origsubj
, sp
, len
);
5460 origsubj
[len
] = '\0';
5461 replace_tabs_by_space(origsubj
);
5462 removing_trailing_white_space(origsubj
);
5465 * origsubj is the original subject but it has been decoded. We need
5466 * to free it at the end of this routine.
5470 shorten_subject(origsubj
);
5473 * prepend_keyword will put the keyword stuff before the subject
5474 * and split the subject up into its colored parts in subjielem.
5475 * Subjielem is a local ielem which will have to be fit into the
5476 * real ifield->ielem later. The print_format strings in subjielem will
5477 * not be filled in by prepend_keyword because of the fact that we
5478 * may have to adjust things for threading below.
5479 * We use subjielem in case we want to insert some threading information
5480 * at the front of the subject.
5482 if(kwtype
== KW
|| kwtype
== KWInit
){
5483 subject
= prepend_keyword_subject(idata
->stream
, idata
->rawno
,
5485 ourifield
? &subjielem
: NULL
,
5486 ps_global
->VAR_KW_BRACES
);
5487 free_subj
= subject
;
5492 subjielem
= new_ielem(&subjielem
);
5493 subjielem
->type
= eTypeCol
;
5494 subjielem
->freedata
= 1;
5495 subjielem
->data
= cpystr(subject
);
5496 subjielem
->datalen
= strlen(subject
);
5497 if(pico_usingcolor()
5498 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5499 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5500 subjielem
->freecolor
= 1;
5501 subjielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5507 * This space is here so that if the subject does
5508 * not extend all the way to the end of the field then
5509 * we'll switch the color back and paint the rest of the
5510 * field in the Normal color or the index line color.
5513 ielem
= new_ielem(&subjielem
);
5514 ielem
->freedata
= 1;
5515 ielem
->data
= cpystr(" ");
5523 && (ps_global
->thread_disp_style
== THREAD_STRUCT
5524 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
5525 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
5526 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ2
)){
5529 * Why do we want to truncate the subject and from strs?
5530 * It's so we can put the [5] thread count things in below when
5531 * we are threading and the thread structure runs off the right
5532 * hand edge of the screen. This routine doesn't know that it
5533 * is running off the edge unless it knows the actual width
5534 * that we have to draw in.
5536 if(pith_opt_truncate_sfstr
5537 && (*pith_opt_truncate_sfstr
)()
5539 && ourifield
->width
> 0)
5540 width
= ourifield
->width
;
5545 width
= MIN(width
, strsize
-1);
5548 * We're counting on the fact that this initial part of the
5549 * string is ascii and we have one octet per character and
5550 * characters are width 1 on the screen.
5552 border
= str
+ width
;
5554 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
5556 if(pith_opt_condense_thread_cue
)
5557 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
5559 && get_lflag(idata
->stream
,
5564 * width is < available strsize and
5565 * border points to something less than or equal
5566 * to the end of the buffer.
5572 while(thd
->parent
&&
5573 (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
5577 if(ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
)
5580 sptr
+= (mult
*depth
);
5581 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
5582 thd
&& thd
->parent
&& p
>= str
;
5583 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
5584 if(p
+ mult
>= border
&& !q
){
5585 if(width
>= 4 && depth
< 100){
5586 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
5589 else if(width
>= 5 && depth
< 1000){
5590 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
5594 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
5607 if(ps_global
->thread_disp_style
== THREAD_STRUCT
5608 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5611 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5614 if(thd
== thdorig
&& !thd
->branch
)
5615 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
5616 else if(thd
== thdorig
|| thd
->branch
)
5619 if(p
+ 1 < border
&& thd
== thdorig
)
5626 if(sptr
&& !truncated_tree
){
5628 * Look to see if the subject is the same as the previous
5629 * message in the thread, if any. If it is the same, don't
5630 * reprint the subject.
5632 * Note that when we're prepending keywords to the subject,
5633 * and the user changes a keyword, we do invalidate
5634 * the index cache for that message but we don't go to the
5635 * trouble of invalidating the index cache for the the child
5636 * of that node in the thread, so the MUTT subject line
5637 * display for the child may be wrong. That is, it may show
5638 * it is the same as this subject even though it no longer
5639 * is, or vice versa.
5641 if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5645 if(thdorig
->parent
&&
5646 (thd
= fetch_thread(idata
->stream
, thdorig
->parent
))
5648 char *this_orig
= NULL
,
5650 *free_prev_orig
= NULL
,
5651 *this_prep
= NULL
, /* includes prepend */
5655 SORTCACHE
*sc
= NULL
;
5657 /* get the stripped subject of previous message */
5658 mc
= (mailcache_t
) mail_parameters(NIL
, GET_CACHE
, NIL
);
5660 sc
= (*mc
)(idata
->stream
, thd
->rawno
, CH_SORTCACHE
);
5662 if(sc
&& sc
->subject
)
5663 prev_orig
= sc
->subject
;
5667 env
= pine_mail_fetchenvelope(idata
->stream
,
5669 stripthis
= (env
&& env
->subject
)
5670 ? env
->subject
: "";
5672 mail_strip_subject(stripthis
, &prev_orig
);
5674 free_prev_orig
= prev_orig
;
5677 mail_strip_subject(rawsubj
, &this_orig
);
5679 if(kwtype
== KW
|| kwtype
== KWInit
){
5680 prev_prep
= prepend_keyword_subject(idata
->stream
,
5684 ps_global
->VAR_KW_BRACES
);
5686 this_prep
= prepend_keyword_subject(idata
->stream
,
5690 ps_global
->VAR_KW_BRACES
);
5692 if((this_prep
|| prev_prep
)
5693 && ((this_prep
&& !prev_prep
)
5694 || (prev_prep
&& !this_prep
)
5695 || strucmp(this_prep
, prev_prep
)))
5699 if((this_orig
|| prev_orig
)
5700 && ((this_orig
&& !prev_orig
)
5701 || (prev_orig
&& !this_orig
)
5702 || strucmp(this_orig
, prev_orig
)))
5707 * If some of the thread is zoomed out of view, we
5708 * want to display the subject of the first one that
5709 * is in view. If any of the parents or grandparents
5710 * etc of this message are visible, then we don't
5711 * need to worry about it. If all of the parents have
5712 * been zoomed away, then this is the first one.
5714 * When you're looking at a particular case where
5715 * some of the messages of a thread are selected it
5716 * seems like we should look at not only our
5717 * direct parents, but the siblings of the parent
5718 * too. But that's not really correct, because those
5719 * siblings are basically the starts of different
5720 * branches, separate from our branch. They could
5721 * have their own subjects, for example. This will
5722 * give us cases where it looks like we are showing
5723 * the subject too much, but it will be correct!
5725 * In zoom_index() we clear_index_cache_ent for
5726 * some lines which have subjects which might become
5727 * visible when we zoom, and also in set_lflags
5728 * where we might change subjects by unselecting
5729 * something when zoomed.
5733 if(!msgline_hidden(idata
->stream
,
5734 sp_msgmap(idata
->stream
),
5735 mn_raw2m(sp_msgmap(idata
->stream
),
5738 break; /* found a visible parent */
5741 if(thd
&& thd
->parent
)
5742 thd
= fetch_thread(idata
->stream
,thd
->parent
);
5747 if(!thd
) /* none were visible */
5752 fs_give((void **) &this_orig
);
5755 fs_give((void **) &this_prep
);
5758 fs_give((void **) &free_prev_orig
);
5761 fs_give((void **) &prev_prep
);
5772 * We don't need to worry about truncating to width
5773 * here. If we go over the right hand edge it will be
5776 strsize
-= (sptr
- str
);
5778 strncpy(sptr
, subject
, strsize
-1);
5779 sptr
[strsize
-1] = '\0';
5781 else if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5782 strsize
-= (sptr
- str
);
5790 * We decided we don't need the subject so we'd better
5791 * eliminate subjielem.
5793 free_ielem(&subjielem
);
5797 free_ielem(&subjielem
); /* no room for actual subject */
5799 if(ourifield
&& sptr
&& sptr
> origstr
){
5800 ielem
= new_ielem(&ourifield
->ielem
);
5801 ielem
->type
= eThreadInfo
;
5802 ielem
->freedata
= 1;
5805 ielem
->data
= cpystr(origstr
);
5806 ielem
->datalen
= strlen(origstr
);
5812 * Not much to do for the non-threading case. Just copy the
5813 * subject we have so far into str and truncate it.
5815 strncpy(str
, subject
, strsize
-1);
5816 str
[strsize
-1] = '\0';
5821 * We need to add subjielem to the end of the ourifield->ielem list.
5824 if(ourifield
->ielem
){
5825 for(ielem
= ourifield
->ielem
;
5826 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5829 ielem
->next
= subjielem
;
5832 ourifield
->ielem
= subjielem
;
5835 ourifield
->leftadj
= 1;
5838 if(opening
&& ourifield
){
5839 IELEM_S
*ftielem
= NULL
;
5843 first_text
= fetch_firsttext(idata
, 0);
5849 strncpy(sep
, ps_global
->VAR_OPENING_SEP
? ps_global
->VAR_OPENING_SEP
: " - ",
5851 sep
[sizeof(sep
)-1] = '\0';
5852 removing_double_quotes(sep
);
5853 seplen
= strlen(sep
);
5855 ftielem
= new_ielem(&ftielem
);
5856 ftielem
->type
= eTypeCol
;
5857 ftielem
->freedata
= 1;
5858 len
= strlen(first_text
) + seplen
;
5859 ftielem
->data
= (char *) fs_get((len
+ 1) * sizeof(char));
5861 strncpy(ftielem
->data
, sep
, seplen
);
5862 strncpy(ftielem
->data
+seplen
, first_text
, len
+1-seplen
);
5863 ftielem
->data
[len
] = '\0';
5865 ftielem
->datalen
= strlen(ftielem
->data
);
5867 fs_give((void **) &first_text
);
5870 if(pico_usingcolor()
5871 && ps_global
->VAR_IND_OP_FORE_COLOR
5872 && ps_global
->VAR_IND_OP_BACK_COLOR
){
5873 ftielem
->freecolor
= 1;
5874 ftielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
, ps_global
->VAR_IND_OP_BACK_COLOR
);
5877 * This space is here so that if the opening text does
5878 * not extend all the way to the end of the field then
5879 * we'll switch the color back and paint the rest of the
5880 * field in the Normal color or the index line color.
5882 ielem
= new_ielem(&ftielem
);
5883 ielem
->freedata
= 1;
5884 ielem
->data
= cpystr(" ");
5888 if(ourifield
->ielem
){
5889 for(ielem
= ourifield
->ielem
;
5890 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5893 ielem
->next
= ftielem
;
5896 ourifield
->ielem
= ftielem
;
5899 ourifield
->leftadj
= 1;
5904 set_ielem_widths_in_field(ourifield
);
5907 fs_give((void **) &origsubj
);
5910 fs_give((void **) &free_subj
);
5915 * Returns an allocated string which is the passed in subject with a
5916 * list of keywords prepended.
5918 * If kwtype == KW you will end up with
5920 * {keyword1 keyword2} subject
5922 * (actually, keyword nicknames will be used instead of the actual keywords
5923 * in the case that the user defined nicknames)
5925 * If kwtype == KWInit you get
5929 * where A is the first letter of the first keyword and B is the first letter
5930 * of the second defined keyword. No space between them. There could be more
5933 * If an ielemp is passed in it will be filled out with the data and colors
5934 * of the pieces of the subject but the print_format strings will not
5938 prepend_keyword_subject(MAILSTREAM
*stream
, long int rawno
, char *subject
,
5939 SubjKW kwtype
, IELEM_S
**ielemp
, char *braces
)
5941 char *p
, *next_piece
, *retsubj
= NULL
, *str
;
5942 char *left_brace
= NULL
, *right_brace
= NULL
;
5944 int some_set
= 0, save
;
5947 COLOR_PAIR
*color
= NULL
;
5948 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5953 if(braces
&& *braces
)
5954 get_pair(braces
, &left_brace
, &right_brace
, 1, 0);
5956 len
= (left_brace
? strlen(left_brace
) : 0) +
5957 (right_brace
? strlen(right_brace
) : 0);
5959 if(stream
&& rawno
>= 0L && rawno
<= stream
->nmsgs
){
5960 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
5961 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5964 len
++; /* space between keywords */
5966 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5969 else if(kwtype
== KWInit
){
5970 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5971 /* interested in only the first UTF-8 initial */
5974 unsigned long remaining_octets
;
5975 unsigned char *inputp
;
5977 remaining_octets
= strlen(str
);
5978 inputp
= (unsigned char *) str
;
5979 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5980 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5981 len
+= (unsigned) (inputp
- (unsigned char *) str
);
5990 if((kwtype
== KW
|| kwtype
== KWInit
) && some_set
){
5991 len
+= strlen(subject
); /* subject is already UTF-8 if needed */
5992 retsubj
= (char *) fs_get((len
+ 1) * sizeof(*retsubj
));
5993 memset(retsubj
, 0, (len
+ 1) * sizeof(*retsubj
));
5994 next_piece
= p
= retsubj
;
5996 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5997 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5999 if(left_brace
&& len
> 0)
6000 sstrncpy(&p
, left_brace
, len
);
6002 else if(kwtype
== KW
)
6005 if(ielemp
&& p
> next_piece
){
6008 ielem
= new_ielem(ielemp
);
6009 ielem
->freedata
= 1;
6010 ielem
->data
= cpystr(next_piece
);
6011 ielem
->datalen
= strlen(next_piece
);
6016 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
6018 if(kwtype
== KWInit
){
6021 unsigned long remaining_octets
;
6022 unsigned char *inputp
;
6024 remaining_octets
= strlen(str
);
6025 inputp
= (unsigned char *) str
;
6026 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
6027 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
6028 if(len
-(p
-retsubj
) > 0){
6029 sstrncpy(&p
, str
, MIN(inputp
- (unsigned char *) str
,len
-(p
-retsubj
)));
6030 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
6031 && ((kw
->nick
&& kw
->nick
[0]
6032 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
6033 || (kw
->kw
&& kw
->kw
[0]
6034 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
6035 ielem
= new_ielem(ielemp
);
6036 ielem
->freedata
= 1;
6039 ielem
->data
= cpystr(next_piece
);
6040 ielem
->datalen
= strlen(next_piece
);
6041 ielem
->color
= color
;
6050 free_color_pair(&color
);
6054 if(len
-(p
-retsubj
) > 0)
6055 sstrncpy(&p
, str
, len
-(p
-retsubj
));
6057 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
6058 && ((kw
->nick
&& kw
->nick
[0]
6059 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
6060 || (kw
->kw
&& kw
->kw
[0]
6061 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
6062 ielem
= new_ielem(ielemp
);
6063 ielem
->freedata
= 1;
6066 ielem
->data
= cpystr(next_piece
);
6067 ielem
->datalen
= strlen(next_piece
);
6068 ielem
->color
= color
;
6075 free_color_pair(&color
);
6080 if(len
-(p
-retsubj
) > 0 && right_brace
)
6081 sstrncpy(&p
, right_brace
, len
-(p
-retsubj
));
6083 if(ielemp
&& p
> next_piece
){
6086 ielem
= new_ielem(ielemp
);
6087 ielem
->freedata
= 1;
6088 ielem
->data
= cpystr(next_piece
);
6089 ielem
->datalen
= strlen(next_piece
);
6094 if(len
-(p
-retsubj
) > 0 && subject
)
6095 sstrncpy(&p
, subject
, len
-(p
-retsubj
));
6097 if(ielemp
&& p
> next_piece
){
6100 ielem
= new_ielem(ielemp
);
6101 ielem
->type
= eTypeCol
;
6102 ielem
->freedata
= 1;
6103 ielem
->data
= cpystr(next_piece
);
6104 ielem
->datalen
= strlen(next_piece
);
6107 if(pico_usingcolor()
6108 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
6109 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
6110 ielem
->freecolor
= 1;
6111 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
6115 retsubj
[len
] = '\0'; /* just making sure */
6119 ielem
= new_ielem(ielemp
);
6120 ielem
->type
= eTypeCol
;
6121 ielem
->freedata
= 1;
6122 ielem
->data
= cpystr(subject
);
6123 ielem
->datalen
= strlen(subject
);
6124 if(pico_usingcolor()
6125 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
6126 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
6127 ielem
->freecolor
= 1;
6128 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
6132 retsubj
= cpystr(subject
);
6137 fs_give((void **) &left_brace
);
6140 fs_give((void **) &right_brace
);
6148 * This means we should ensure that all data ends up being UTF-8 data.
6149 * That covers the data in ice ielems and str.
6152 from_str(IndexColType ctype
, INDEXDATA_S
*idata
, char *str
, size_t strsize
, ICE_S
*ice
)
6154 char *field
, *newsgroups
, *border
, *p
, *fptr
= NULL
, *q
= NULL
;
6157 int depth
= 0, mult
= 2;
6158 PINETHRD_S
*thd
, *thdorig
;
6161 && (ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
6162 || ps_global
->thread_disp_style
== THREAD_INDENT_FROM2
6163 || ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
)){
6165 if(pith_opt_truncate_sfstr
&& (*pith_opt_truncate_sfstr
)()){
6166 IFIELD_S
*ourifield
= NULL
;
6168 if(ice
&& ice
->ifield
){
6169 /* move to last ifield, the one we're working on */
6170 for(ourifield
= ice
->ifield
;
6171 ourifield
&& ourifield
->next
;
6172 ourifield
= ourifield
->next
)
6176 if(ourifield
&& ourifield
->width
> 0)
6177 width
= ourifield
->width
;
6183 width
= MIN(width
, strsize
-1);
6185 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
6186 border
= str
+ width
;
6187 if(pith_opt_condense_thread_cue
)
6188 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
6190 && get_lflag(idata
->stream
,
6197 while(thd
->parent
&& (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
6201 if(ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
)
6204 fptr
+= (mult
*depth
);
6205 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
6206 thd
&& thd
->parent
&& p
>= str
;
6207 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
6208 if(p
+ mult
>= border
&& !q
){
6209 if(width
>= 4 && depth
< 100){
6210 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
6213 else if(width
>= 5 && depth
< 1000){
6214 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
6218 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
6228 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6231 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6234 if(thd
== thdorig
&& !thd
->branch
)
6235 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6236 else if(thd
== thdorig
|| thd
->branch
)
6243 else if(p
< border
){
6245 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6248 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6251 if(thd
== thdorig
&& !thd
->branch
)
6252 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6253 else if(thd
== thdorig
|| thd
->branch
)
6264 strsize
-= (fptr
- str
);
6267 case iFromToNotNews
:
6268 if(!(addr
= fetch_from(idata
)) || address_is_us(addr
, ps_global
)){
6270 strncpy(fptr
, "To: ", strsize
-1);
6271 fptr
[strsize
-1] = '\0';
6275 if((field
= ((addr
= fetch_to(idata
))
6277 : (addr
= fetch_cc(idata
))
6280 && set_index_addr(idata
, field
, addr
, "To: ",
6284 if(ctype
== iFromTo
&&
6285 (newsgroups
= fetch_newsgroups(idata
)) &&
6287 snprintf(fptr
, strsize
, "To: %-*.*s", (int)(strsize
-1-4), (int)(strsize
-1-4),
6292 /* else fall thru to From: */
6295 /* else fall thru to From: */
6301 set_index_addr(idata
, "From", fetch_from(idata
), NULL
, strsize
-1, fptr
);
6306 if((addr
= fetch_from(idata
)) && addr
->mailbox
&& addr
->mailbox
[0]){
6307 char *mb
= NULL
, *hst
= NULL
, *at
= NULL
;
6311 if(ctype
== iAddress
&& addr
->host
&& addr
->host
[0]
6312 && addr
->host
[0] != '.'){
6318 if(!at
|| strsize
-1 <= len
)
6319 snprintf(fptr
, strsize
, "%-*.*s", (int)(strsize
-1), (int)(strsize
-1), mb
);
6321 snprintf(fptr
, strsize
, "%s@%-*.*s", mb
, (int)(strsize
-1-len
-1), (int)(strsize
-1-len
-1), hst
);
6330 replace_tabs_by_space(str
);
6335 * Set up the elements contained in field so that they take up the
6336 * whole field width. Data is assumed to be UTF-8.
6339 set_ielem_widths_in_field(IFIELD_S
*ifield
)
6341 IELEM_S
*ielem
= NULL
;
6342 int datawidth
, fmtwidth
;
6347 fmtwidth
= ifield
->width
;
6349 for(ielem
= ifield
->ielem
; ielem
&& fmtwidth
> 0; ielem
= ielem
->next
){
6350 if(!ifield
->leftadj
&& ielem
->next
){
6351 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield
->ctype
));
6355 datawidth
= (int) utf8_width(ielem
->data
);
6356 if(datawidth
>= fmtwidth
|| !ielem
->next
){
6357 set_print_format(ielem
, fmtwidth
, ifield
->leftadj
);
6361 set_print_format(ielem
, datawidth
, ifield
->leftadj
);
6362 fmtwidth
-= datawidth
;
6369 * Simple hash function from K&R 2nd edition, p. 144.
6371 * This one is modified to never return 0 so we can use that as a special
6372 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6373 * as a special value that can't be returned by line_hash.
6378 unsigned long hashval
;
6380 for(hashval
= 0; *s
!= '\0'; s
++)
6381 hashval
= *s
+ 31 * hashval
;
6383 hashval
= hashval
% LINE_HASH_N
;
6393 * Returns nonzero if considered hidden, 0 if not considered hidden.
6396 msgline_hidden(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
6400 if(flags
& MH_ANYTHD
){
6401 ret
= ((any_lflagged(msgmap
, MN_HIDE
) > 0)
6402 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
));
6404 else if(flags
& MH_THISTHD
&& THREADING() && sp_viewing_a_thread(stream
)){
6405 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6406 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
));
6409 if(THREADING() && sp_viewing_a_thread(stream
)){
6410 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6411 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
)
6412 || get_lflag(stream
, msgmap
, msgno
, MN_CHID
));
6414 else if(THRD_INDX()){
6416 * If this message is in the collapsed part of a thread,
6417 * it's hidden. It must be a top-level of a thread to be
6418 * considered visible. Even if it is top-level, it is only
6419 * visible if some message in the thread is not hidden.
6421 if(get_lflag(stream
, msgmap
, msgno
, MN_CHID
)) /* not top */
6424 unsigned long rawno
;
6425 PINETHRD_S
*thrd
= NULL
;
6427 rawno
= mn_m2raw(msgmap
, msgno
);
6429 thrd
= fetch_thread(stream
, rawno
);
6431 ret
= !thread_has_some_visible(stream
, thrd
);
6435 ret
= ((any_lflagged(msgmap
, MN_HIDE
| MN_CHID
) > 0)
6436 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
| MN_CHID
));
6441 "msgline_hidden(%ld): %s\n", msgno
, ret
? "HID" : "VIS"));
6448 adjust_cur_to_visible(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
6453 cur
= mn_get_cur(msgmap
);
6455 /* if current is hidden, adjust */
6456 if(cur
>= 1L && cur
<= mn_get_total(msgmap
)
6457 && msgline_hidden(stream
, msgmap
, cur
, 0)){
6459 dir
= mn_get_revsort(msgmap
) ? -1 : 1;
6462 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6463 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6464 && msgline_hidden(stream
, msgmap
, n
, 0);
6468 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6469 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6470 mn_reset_cur(msgmap
, n
);
6471 else{ /* no visible in that direction */
6473 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6474 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6475 && msgline_hidden(stream
, msgmap
, n
, 0);
6479 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6480 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6481 mn_reset_cur(msgmap
, n
);
6489 setup_for_index_index_screen(void)
6491 format_index_line
= format_index_index_line
;
6492 setup_header_widths
= setup_index_header_widths
;
6497 setup_for_thread_index_screen(void)
6499 format_index_line
= format_thread_index_line
;
6500 setup_header_widths
= setup_thread_header_widths
;
6505 ice_hash(ICE_S
*ice
)
6507 char buf
[MAX_SCREEN_COLS
+1];
6512 simple_index_line(buf
, sizeof(buf
), ice
, 0L);
6514 buf
[sizeof(buf
) - 1] = '\0';
6516 return(line_hash(buf
));
6521 left_adjust(int width
)
6523 return(format_str(width
, 1));
6528 right_adjust(int width
)
6530 return(format_str(width
, 0));
6535 * Returns allocated and filled in format string.
6538 format_str(int width
, int left
)
6543 len
= PRINT_FORMAT_LEN(width
,left
) * sizeof(char);
6544 format
= (char *) fs_get(len
+ 1);
6545 copy_format_str(width
, left
, format
, len
);
6553 * Put the left or right adjusted format string of width width into
6554 * dest. Dest is of size n+1.
6557 copy_format_str(int width
, int left
, char *dest
, int n
)
6561 p
= int2string(width
);
6563 snprintf(dest
, n
+1, "%%%s%s.%ss", left
? "-" : "", p
, p
);
6572 * Sets up the print_format string to be width wide with left or right
6573 * adjust. Takes care of memory freeing and allocation.
6576 set_print_format(IELEM_S
*ielem
, int width
, int leftadj
)
6581 if(ielem
->print_format
){
6582 /* is there enough room? */
6583 if(ielem
->freeprintf
< PRINT_FORMAT_LEN(width
,leftadj
)+1){
6584 fs_resize((void **) &ielem
->print_format
,
6585 (PRINT_FORMAT_LEN(width
,leftadj
)+1) * sizeof(char));
6586 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);
6589 copy_format_str(width
, leftadj
, ielem
->print_format
,
6590 PRINT_FORMAT_LEN(width
,leftadj
));
6593 ielem
->print_format
= leftadj
? left_adjust(width
)
6594 : right_adjust(width
);
6595 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);