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-2016 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
= monabb_width
;
244 (*answer
)[column
].monabb_width
= monabb_width
;
251 * Test to see how long it is.
253 for(i
= 0; i
< 7; i
++){
254 p
= day_abbrev_locale(i
);
255 w
= MAX(utf8_width(p
), w
);
258 (*answer
)[column
].req_width
= MIN(MAX(2, w
), 5);
262 (*answer
)[column
].req_width
= monabb_width
+ 3;
263 (*answer
)[column
].monabb_width
= monabb_width
;
270 * Test to see how long it is.
272 for(i
= 1; i
<= 12; i
++){
273 p
= month_name_locale(i
);
274 w
= MAX(utf8_width(p
), w
);
277 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
284 for(i
= 0; i
< 7; i
++){
285 p
= day_name_locale(i
);
286 w
= MAX(utf8_width(p
), w
);
289 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
300 * Format a date to see how long it is.
301 * Make it as least as long as "Yesterday".
302 * We should really use the width of the longest
303 * of the translated yesterdays and friends but...
309 memset(&tm
, 0, sizeof(tm
));
316 switch((*answer
)[column
].ctype
){
318 our_strftime(ss
, sizeof(ss
), "%X", &tm
);
321 our_strftime(ss
, sizeof(ss
), "%c", &tm
);
325 our_strftime(ss
, sizeof(ss
), "%x", &tm
);
328 (*answer
)[column
].req_width
= MIN(MAX(9, utf8_width(ss
)), len
);
331 (*answer
)[column
].monabb_width
= monabb_width
;
336 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
338 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
339 case iSDateTimeIsoS24
:
340 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
342 * These SDates are 8 wide but they need to be 9 for "Yesterday".
344 (*answer
)[column
].req_width
= 9;
347 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
348 (*answer
)[column
].req_width
= 10;
351 (*answer
)[column
].req_width
= 12;
352 (*answer
)[column
].monabb_width
= monabb_width
;
355 (*answer
)[column
].req_width
= 16;
365 (void) mail_parameters(NULL
, SET_IMAPEXTRAHEADERS
,
366 (void *) get_extra_hdrs());
371 reset_index_format(void)
373 long rflags
= ROLE_DO_OTHER
;
378 if(ps_global
->mail_stream
&& nonempty_patterns(rflags
, &pstate
)){
379 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
)){
380 if(match_pattern(pat
->patgrp
, ps_global
->mail_stream
, NULL
,
381 NULL
, NULL
, SE_NOSERVER
|SE_NOPREFETCH
))
385 if(pat
&& pat
->action
&& !pat
->action
->bogus
386 && pat
->action
->index_format
){
388 init_index_format(pat
->action
->index_format
,
389 &ps_global
->index_disp_format
);
394 init_index_format(ps_global
->VAR_INDEX_FORMAT
,
395 &ps_global
->index_disp_format
);
400 free_index_format(INDEX_COL_S
**disp_format
)
402 INDEX_COL_S
*cdesc
= NULL
;
404 if(disp_format
&& *disp_format
){
405 for(cdesc
= (*disp_format
); cdesc
->ctype
!= iNothing
; cdesc
++)
407 free_hdrtok(&cdesc
->hdrtok
);
409 fs_give((void **) disp_format
);
415 new_hdrtok(char *hdrname
)
417 HEADER_TOK_S
*hdrtok
;
419 hdrtok
= (HEADER_TOK_S
*) fs_get(sizeof(HEADER_TOK_S
));
420 memset(hdrtok
, 0, sizeof(HEADER_TOK_S
));
421 hdrtok
->hdrname
= hdrname
? cpystr(hdrname
) : NULL
;
422 hdrtok
->fieldnum
= 0;
423 hdrtok
->adjustment
= Left
;
424 hdrtok
->fieldsepcnt
= 1;
425 hdrtok
->fieldseps
= cpystr(" ");
432 free_hdrtok(HEADER_TOK_S
**hdrtok
)
434 if(hdrtok
&& *hdrtok
){
435 if((*hdrtok
)->hdrname
)
436 fs_give((void **) &(*hdrtok
)->hdrname
);
438 if((*hdrtok
)->fieldseps
)
439 fs_give((void **) &(*hdrtok
)->fieldseps
);
441 fs_give((void **) hdrtok
);
446 /* popular ones first to make it slightly faster */
447 static INDEX_PARSE_T itokens
[] = {
448 {"STATUS", iStatus
, FOR_INDEX
},
449 {"MSGNO", iMessNo
, FOR_INDEX
},
450 {"DATE", iDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
451 {"FROMORTO", iFromTo
, FOR_INDEX
},
452 {"FROMORTONOTNEWS", iFromToNotNews
, FOR_INDEX
},
453 {"SIZE", iSize
, FOR_INDEX
},
454 {"SIZECOMMA", iSizeComma
, FOR_INDEX
},
455 {"SIZENARROW", iSizeNarrow
, FOR_INDEX
},
456 {"KSIZE", iKSize
, FOR_INDEX
},
457 {"SUBJECT", iSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
458 {"SHORTSUBJECT", iShortSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
459 {"FULLSTATUS", iFStatus
, FOR_INDEX
},
460 {"IMAPSTATUS", iIStatus
, FOR_INDEX
},
461 {"SHORTIMAPSTATUS", iSIStatus
, FOR_INDEX
},
462 {"SUBJKEY", iSubjKey
, FOR_INDEX
},
463 {"SHORTSUBJKEY", iShortSubjKey
, FOR_INDEX
},
464 {"SUBJKEYINIT", iSubjKeyInit
, FOR_INDEX
},
465 {"SHORTSUBJKEYINIT",iShortSubjKeyInit
, FOR_INDEX
},
466 {"SUBJECTTEXT", iSubjectText
, FOR_INDEX
},
467 {"SUBJKEYTEXT", iSubjKeyText
, FOR_INDEX
},
468 {"SUBJKEYINITTEXT", iSubjKeyInitText
, FOR_INDEX
},
469 {"OPENINGTEXT", iOpeningText
, FOR_INDEX
},
470 {"OPENINGTEXTNQ", iOpeningTextNQ
, FOR_INDEX
},
471 {"KEY", iKey
, FOR_INDEX
},
472 {"KEYINIT", iKeyInit
, FOR_INDEX
},
473 {"DESCRIPSIZE", iDescripSize
, FOR_INDEX
},
474 {"ATT", iAtt
, FOR_INDEX
},
475 {"SCORE", iScore
, FOR_INDEX
},
476 {"PRIORITY", iPrio
, FOR_INDEX
},
477 {"PRIORITYALPHA", iPrioAlpha
, FOR_INDEX
},
478 {"PRIORITY!", iPrioBang
, FOR_INDEX
},
479 {"LONGDATE", iLDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
480 {"SHORTDATE1", iS1Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
481 {"SHORTDATE2", iS2Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
482 {"SHORTDATE3", iS3Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
483 {"SHORTDATE4", iS4Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
484 {"DATEISO", iDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
485 {"SHORTDATEISO", iDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
486 {"SMARTDATE", iSDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
487 {"SMARTTIME", iSTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
488 {"SMARTDATEISO", iSDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
489 {"SMARTDATESHORTISO",iSDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
490 {"SMARTDATES1", iSDateS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
491 {"SMARTDATES2", iSDateS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
492 {"SMARTDATES3", iSDateS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
493 {"SMARTDATES4", iSDateS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
494 {"SMARTDATETIME", iSDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
495 {"SMARTDATETIMEISO",iSDateTimeIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
496 {"SMARTDATETIMESHORTISO",iSDateTimeIsoS
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
497 {"SMARTDATETIMES1", iSDateTimeS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
498 {"SMARTDATETIMES2", iSDateTimeS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
499 {"SMARTDATETIMES3", iSDateTimeS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
500 {"SMARTDATETIMES4", iSDateTimeS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
501 {"SMARTDATETIME24", iSDateTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
502 {"SMARTDATETIMEISO24", iSDateTimeIso24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
503 {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
504 {"SMARTDATETIMES124", iSDateTimeS124
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
505 {"SMARTDATETIMES224", iSDateTimeS224
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
506 {"SMARTDATETIMES324", iSDateTimeS324
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
507 {"SMARTDATETIMES424", iSDateTimeS424
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
508 {"TIME24", iTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
509 {"TIME12", iTime12
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
510 {"TIMEZONE", iTimezone
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
511 {"MONTHABBREV", iMonAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
512 {"DAYOFWEEKABBREV", iDayOfWeekAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
513 {"DAYOFWEEK", iDayOfWeek
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
514 {"FROM", iFrom
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
515 {"TO", iTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
516 {"SENDER", iSender
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
517 {"CC", iCc
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
518 {"RECIPS", iRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
519 {"NEWS", iNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
520 {"TOANDNEWS", iToAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
521 {"NEWSANDTO", iNewsAndTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
522 {"RECIPSANDNEWS", iRecipsAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
523 {"NEWSANDRECIPS", iNewsAndRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
524 {"MSGID", iMsgID
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
525 {"CURNEWS", iCurNews
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
526 {"DAYDATE", iRDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
527 {"PREFDATE", iPrefDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
528 {"PREFTIME", iPrefTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
529 {"PREFDATETIME", iPrefDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
530 {"DAY", iDay
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
531 {"DAYORDINAL", iDayOrdinal
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
532 {"DAY2DIGIT", iDay2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
533 {"MONTHLONG", iMonLong
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
534 {"MONTH", iMon
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
535 {"MONTH2DIGIT", iMon2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
536 {"YEAR", iYear
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
537 {"YEAR2DIGIT", iYear2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
538 {"ADDRESS", iAddress
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
539 {"MAILBOX", iMailbox
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
540 {"ROLENICK", iRoleNick
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
541 {"INIT", iInit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
542 {"CURDATE", iCurDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
543 {"CURDATEISO", iCurDateIso
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
544 {"CURDATEISOS", iCurDateIsoS
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
545 {"CURTIME24", iCurTime24
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
546 {"CURTIME12", iCurTime12
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
547 {"CURDAY", iCurDay
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
548 {"CURDAY2DIGIT", iCurDay2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
549 {"CURDAYOFWEEK", iCurDayOfWeek
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
550 {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb
,
551 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
552 {"CURMONTH", iCurMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
553 {"CURMONTH2DIGIT", iCurMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
554 {"CURMONTHLONG", iCurMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
555 {"CURMONTHABBREV", iCurMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
556 {"CURYEAR", iCurYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
557 {"CURYEAR2DIGIT", iCurYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
558 {"CURPREFDATE", iCurPrefDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
559 {"CURPREFTIME", iCurPrefTime
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
560 {"CURPREFDATETIME", iCurPrefDateTime
,
561 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
562 {"LASTMONTH", iLstMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
563 {"LASTMONTH2DIGIT", iLstMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
564 {"LASTMONTHLONG", iLstMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
565 {"LASTMONTHABBREV", iLstMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
566 {"LASTMONTHYEAR", iLstMonYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
567 {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit
,
568 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
569 {"LASTYEAR", iLstYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
570 {"LASTYEAR2DIGIT", iLstYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
571 {"HEADER", iHeader
, FOR_INDEX
},
572 {"TEXT", iText
, FOR_INDEX
},
573 {"ARROW", iArrow
, FOR_INDEX
},
574 {"NEWLINE", iNewLine
, FOR_REPLY_INTRO
},
575 {"CURSORPOS", iCursorPos
, FOR_TEMPLATE
},
576 {NULL
, iNothing
, FOR_NOTHING
}
579 INDEX_PARSE_T itokensinv
[sizeof(itokens
)/sizeof(itokens
[0])];
582 inverse_itokens(void)
585 for (pt
= itokens
; pt
->name
; pt
++)
586 itokensinv
[pt
->ctype
].ctype
= pt
- itokens
;
593 return((i
< sizeof(itokens
) && itokens
[i
].name
) ? &itokens
[i
] : NULL
);
598 * Args txt -- The token being checked begins at the beginning
599 * of txt. The end of the token is delimited by a null, or
600 * white space, or an underscore if DELIM_USCORE is set,
601 * or a left paren if DELIM_PAREN is set.
602 * flags -- Flags contains the what_for value, and DELIM_ values.
604 * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
607 itoktype(char *txt
, int flags
)
614 * Separate a copy of the possible token out of txt.
618 while(w
< token
+sizeof(token
)-1 &&
620 !(!(*v
& 0x80) && isspace((unsigned char)*v
)) &&
621 !(flags
& DELIM_USCORE
&& *v
== '_') &&
622 !(flags
& DELIM_PAREN
&& *v
== '(') &&
623 !(flags
& DELIM_COLON
&& *v
== ':'))
628 for(pt
= itokens
; pt
->name
; pt
++)
629 if(pt
->what_for
& flags
&& !strucmp(pt
->name
, token
))
637 parse_index_format(char *format_str
, INDEX_COL_S
**answer
)
642 INDEX_COL_S cdesc
[200]; /* plenty of temp storage for answer */
644 memset((void *)cdesc
, 0, sizeof(cdesc
));
647 while(p
&& *p
&& column
< 200-1){
648 /* skip leading white space for next word */
649 p
= skip_white_space(p
);
650 pt
= itoktype(p
, FOR_INDEX
| DELIM_PAREN
| DELIM_COLON
);
652 /* ignore unrecognized word */
654 for(q
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
661 "parse_index_format: unrecognized token: %s\n",
663 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
664 _("Unrecognized word in index-format: %s"), q
);
668 cdesc
[column
].ctype
= pt
->ctype
;
670 if(pt
->ctype
== iHeader
|| pt
->ctype
== iText
){
672 * iHeader field has special syntax.
674 * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
676 * where width is the regular width or percentage width or
677 * left out for default width, fieldnum defaults to 0 for
678 * whole thing, 1 for first field, ...
679 * and field_separators is a list of characters which separate
680 * the fields. The whole parenthesized part is optional. If used
681 * the arguments can be dropped from the right, so
684 * HEADER:hdrname(10) or
685 * HEADER:hdrname(10%) or
686 * HEADER:hdrname(10,2) or
687 * HEADER:hdrname(,2) or
688 * HEADER:hdrname(10,2, ) or
689 * HEADER:hdrname(10,2,\,:) or
690 * HEADER:hdrname(10,2,\,:,R)
692 * iText field uses the hdrtok field for convenience. It has syntax
698 * and the literal text goes into the index line. It is also special
699 * because there is no 1 column space after this field.
703 p
+= strlen(pt
->name
);
705 /* look for header name */
707 char *w
, hdrname
[200];
712 if(*p
== '\"'){ /* quoted name */
714 while(w
< hdrname
+ sizeof(hdrname
)-1 && *p
!= '\"'){
726 while(w
< hdrname
+ sizeof(hdrname
)-1 &&
727 !(!(*p
& 0x80) && isspace((unsigned char)*p
)) &&
735 cdesc
[column
].hdrtok
= new_hdrtok(hdrname
);
738 if(pt
->ctype
== iHeader
){
739 dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
740 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
743 dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
744 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
749 if(pt
->ctype
== iHeader
){
750 dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p
));
751 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
754 dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p
));
755 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
758 /* skip over rest of bogus config */
759 while(!(!(*p
& 0x80) && isspace((unsigned char)*p
)) && *p
!= '(')
764 /* skip over name and look for parens */
765 p
+= strlen(pt
->name
);
771 while(p
&& *p
&& isdigit((unsigned char) *p
))
774 if(pt
->ctype
== iHeader
){
775 /* first argument is width or width percentage, like for others */
776 if(p
&& *p
&& (*p
== ')' || *p
== ',')){
778 cdesc
[column
].wtype
= Fixed
;
779 cdesc
[column
].req_width
= atoi(q
);
782 cdesc
[column
].wtype
= WeCalculate
;
783 cdesc
[column
].req_width
= 0;
786 else if(p
&& *p
&& *p
== '%' && p
> q
){
787 cdesc
[column
].wtype
= Percent
;
788 cdesc
[column
].req_width
= atoi(q
);
792 cdesc
[column
].wtype
= WeCalculate
;
793 cdesc
[column
].req_width
= 0;
796 /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
797 if(p
&& *p
&& *p
== ','){
799 /* no space allowed between arguments */
800 if(*p
&& isdigit((unsigned char) *p
)){
802 while(*p
&& isdigit((unsigned char) *p
))
805 cdesc
[column
].hdrtok
->fieldnum
= atoi(q
);
808 * Optional 3rd argument is field separators.
809 * Comma is \, and backslash is \\.
815 /* don't use default */
816 if(*p
&& *p
!= ')' && *p
!= ',' && cdesc
[column
].hdrtok
->fieldseps
)
817 cdesc
[column
].hdrtok
->fieldseps
[0] = '\0';
820 if(*p
== '\"' && strchr(p
+1, '\"')){
822 while(*p
&& *p
!= ')' && *p
!= '\"' && *p
!= ','){
823 if(cdesc
[column
].hdrtok
->fieldseps
)
824 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
826 if(*p
== '\\' && *(p
+1))
829 if(cdesc
[column
].hdrtok
->fieldseps
){
830 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
831 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
832 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
840 while(*p
&& *p
!= ')' && *p
!= ','){
841 if(cdesc
[column
].hdrtok
->fieldseps
)
842 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
843 if(*p
== '\\' && *(p
+1))
846 if(cdesc
[column
].hdrtok
->fieldseps
){
847 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
848 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
849 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
854 /* optional 4th argument, left or right adjust */
857 if(*p
== 'L' || *p
== 'l')
858 cdesc
[column
].hdrtok
->adjustment
= Left
;
859 else if(*p
== 'R' || *p
== 'r')
860 cdesc
[column
].hdrtok
->adjustment
= Right
;
862 dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p
? p
: "<null>"));
863 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 4th argument should be L or R");
869 dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p
? p
: "<null>"));
870 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
875 if(p
&& *p
&& *p
== ')' && p
> q
){
876 cdesc
[column
].wtype
= Fixed
;
877 cdesc
[column
].req_width
= atoi(q
);
879 else if(p
&& *p
&& *p
== '%' && p
> q
){
880 cdesc
[column
].wtype
= Percent
;
881 cdesc
[column
].req_width
= atoi(q
);
884 cdesc
[column
].wtype
= WeCalculate
;
885 cdesc
[column
].req_width
= 0;
890 /* if they left out width for iText we can figure it out */
891 if(pt
->ctype
== iText
&& cdesc
[column
].hdrtok
&& cdesc
[column
].hdrtok
->hdrname
){
892 cdesc
[column
].wtype
= Fixed
;
893 cdesc
[column
].req_width
= utf8_width(cdesc
[column
].hdrtok
->hdrname
);
896 cdesc
[column
].wtype
= WeCalculate
;
897 cdesc
[column
].req_width
= 0;
902 /* skip text at end of word */
903 while(p
&& *p
&& !isspace((unsigned char)*p
))
907 /* if, after all that, we didn't find anything recognizable, bitch */
909 dprint((1, "Completely unrecognizable index-format\n"));
910 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
911 _("Configured \"index-format\" unrecognizable. Using default."));
915 /* Finish with Nothing column */
916 cdesc
[column
].ctype
= iNothing
;
918 /* free up old answer */
920 free_index_format(answer
);
922 /* allocate space for new answer */
923 *answer
= (INDEX_COL_S
*)fs_get((column
+1)*sizeof(INDEX_COL_S
));
924 memset((void *)(*answer
), 0, (column
+1)*sizeof(INDEX_COL_S
));
925 /* copy answer to real place */
926 for(i
= 0; i
<= column
; i
++)
927 (*answer
)[i
] = cdesc
[i
];
934 * These types are basically fixed in width.
935 * The order is slightly significant. The ones towards the front of the
936 * list get space allocated sooner than the ones at the end of the list.
938 static IndexColType fixed_ctypes
[] = {
939 iMessNo
, iStatus
, iFStatus
, iIStatus
, iSIStatus
,
940 iDate
, iSDate
, iSDateTime
, iSDateTime24
,
942 iS1Date
, iS2Date
, iS3Date
, iS4Date
, iDateIso
, iDateIsoS
,
943 iSDateIso
, iSDateIsoS
,
944 iSDateS1
, iSDateS2
, iSDateS3
, iSDateS4
,
945 iSDateTimeIso
, iSDateTimeIsoS
,
946 iSDateTimeS1
, iSDateTimeS2
, iSDateTimeS3
, iSDateTimeS4
,
947 iSDateTimeIso24
, iSDateTimeIsoS24
,
948 iSDateTimeS124
, iSDateTimeS224
, iSDateTimeS324
, iSDateTimeS424
,
949 iSize
, iSizeComma
, iSizeNarrow
, iKSize
, iDescripSize
,
950 iPrio
, iPrioBang
, iPrioAlpha
, iInit
,
951 iAtt
, iTime24
, iTime12
, iTimezone
, iMonAbb
, iYear
, iYear2Digit
,
952 iDay2Digit
, iMon2Digit
, iDayOfWeekAbb
, iScore
, iMonLong
, iDayOfWeek
957 ctype_is_fixed_length(IndexColType ctype
)
962 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
965 if(ctype
== fixed_ctypes
[j
])
973 /*----------------------------------------------------------------------
974 Setup the widths of the various columns in the index display
977 setup_index_header_widths(MAILSTREAM
*stream
)
979 int colspace
; /* for reserving space between columns */
980 int j
, some_to_calculate
;
981 int space_left
, screen_width
, fix
;
982 int keep_going
, tot_pct
, was_sl
;
987 max_msgno
= mn_get_total(ps_global
->msgmap
);
989 dprint((8, "=== setup_index_header_widths() ===\n"));
991 clear_icache_flags(stream
);
992 screen_width
= ps_global
->ttyo
->screen_cols
;
993 space_left
= screen_width
;
994 some_to_calculate
= 0;
998 * Calculate how many fields there are so we know how many spaces
999 * between columns to reserve. Fill in Fixed widths now. Reserve
1000 * special case WeCalculate with non-zero req_widths before doing
1001 * Percent cases below.
1003 for(cdesc
= ps_global
->index_disp_format
;
1004 cdesc
->ctype
!= iNothing
;
1007 if(cdesc
->wtype
== Fixed
){
1008 cdesc
->width
= cdesc
->req_width
;
1009 if(cdesc
->width
> 0)
1012 else if(cdesc
->wtype
== Percent
){
1013 cdesc
->width
= 0; /* calculated later */
1016 else{ /* WeCalculate */
1017 cdesc
->width
= cdesc
->req_width
; /* reserve this for now */
1018 some_to_calculate
++;
1022 /* no space after iText */
1023 if(cdesc
->ctype
== iText
)
1026 space_left
-= cdesc
->width
;
1029 colspace
= MAX(colspace
, 0);
1031 space_left
-= colspace
; /* space between columns */
1033 ps_global
->display_keywords_in_subject
= 0;
1034 ps_global
->display_keywordinits_in_subject
= 0;
1037 * Set the actual lengths for the fixed width fields and set up
1038 * the left or right adjustment for everything.
1039 * There should be a case setting actual_length for all of the types
1042 for(cdesc
= ps_global
->index_disp_format
;
1043 cdesc
->ctype
!= iNothing
;
1046 wtype
= cdesc
->wtype
;
1048 if(cdesc
->ctype
== iSubjKey
|| cdesc
->ctype
== iSubjKeyText
)
1049 ps_global
->display_keywords_in_subject
= 1;
1050 else if(cdesc
->ctype
== iSubjKeyInit
|| cdesc
->ctype
== iSubjKeyInitText
)
1051 ps_global
->display_keywordinits_in_subject
= 1;
1053 if(wtype
== WeCalculate
|| wtype
== Percent
|| cdesc
->width
!= 0){
1055 switch(cdesc
->ctype
){
1056 case iSDate
: case iSDateIso
: case iSDateIsoS
:
1057 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1058 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
1059 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1060 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
1061 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1063 set_format_includes_smartdate(stream
);
1070 if(ctype_is_fixed_length(cdesc
->ctype
)){
1071 switch(cdesc
->ctype
){
1075 cdesc
->actual_length
= 1;
1076 cdesc
->adjustment
= Left
;
1082 cdesc
->actual_length
= 2;
1083 cdesc
->adjustment
= Left
;
1087 cdesc
->actual_length
= 2;
1088 cdesc
->adjustment
= Right
;
1093 cdesc
->actual_length
= 3;
1094 cdesc
->adjustment
= Left
;
1098 set_format_includes_msgno(stream
);
1099 if(max_msgno
< 1000)
1100 cdesc
->actual_length
= 3;
1101 else if(max_msgno
< 10000)
1102 cdesc
->actual_length
= 4;
1103 else if(max_msgno
< 100000)
1104 cdesc
->actual_length
= 5;
1106 cdesc
->actual_length
= 6;
1108 cdesc
->adjustment
= Right
;
1113 cdesc
->actual_length
= 4;
1114 cdesc
->adjustment
= Left
;
1119 cdesc
->actual_length
= 5;
1120 cdesc
->adjustment
= Left
;
1124 cdesc
->actual_length
= 5;
1125 cdesc
->adjustment
= Right
;
1130 cdesc
->actual_length
= 6;
1131 cdesc
->adjustment
= Left
;
1135 cdesc
->actual_length
= 6;
1136 cdesc
->adjustment
= Right
;
1142 cdesc
->actual_length
= 7;
1143 cdesc
->adjustment
= Right
;
1147 cdesc
->actual_length
= 7;
1148 cdesc
->adjustment
= Left
;
1152 cdesc
->actual_length
= 7;
1153 cdesc
->adjustment
= Left
;
1162 cdesc
->actual_length
= 8;
1163 cdesc
->adjustment
= Left
;
1167 cdesc
->actual_length
= 8;
1168 cdesc
->adjustment
= Right
;
1179 cdesc
->actual_length
= cdesc
->req_width
;
1180 cdesc
->adjustment
= Left
;
1184 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1185 case iSDateTimeIsoS
:
1186 case iSDateTimeIsoS24
:
1187 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1188 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1189 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
1190 if(cdesc
->ctype
== iSDateIso
1191 || cdesc
->ctype
== iSDateTimeIso
1192 || cdesc
->ctype
== iSDateTimeIso24
)
1193 cdesc
->actual_length
= 10;
1195 cdesc
->actual_length
= 9;
1197 cdesc
->adjustment
= Left
;
1201 cdesc
->actual_length
= 9;
1202 cdesc
->adjustment
= Right
;
1206 cdesc
->actual_length
= 10;
1207 cdesc
->adjustment
= Left
;
1211 cdesc
->actual_length
= 12;
1212 cdesc
->adjustment
= Left
;
1216 alpine_panic("Unhandled fixed case in setup_index_header");
1220 else if(cdesc
->ctype
== iHeader
)
1221 cdesc
->adjustment
= cdesc
->hdrtok
? cdesc
->hdrtok
->adjustment
: Left
;
1223 cdesc
->adjustment
= Left
;
1227 if(ps_global
->display_keywords_in_subject
)
1228 ps_global
->display_keywordinits_in_subject
= 0;
1230 /* if have reserved unneeded space for size, give it back */
1231 for(cdesc
= ps_global
->index_disp_format
;
1232 cdesc
->ctype
!= iNothing
;
1234 if(cdesc
->ctype
== iSize
|| cdesc
->ctype
== iKSize
||
1235 cdesc
->ctype
== iSizeNarrow
||
1236 cdesc
->ctype
== iSizeComma
|| cdesc
->ctype
== iDescripSize
){
1237 if(cdesc
->actual_length
== 0){
1238 if((fix
=cdesc
->width
) > 0){ /* had this reserved */
1243 space_left
++; /* +1 for space between columns */
1248 * Calculate the field widths that are basically fixed in width.
1249 * Do them in this order in case we don't have enough space to go around.
1250 * The set of fixed_ctypes here is the same as the set where we
1251 * set the actual_lengths above.
1253 for(j
= 0; space_left
> 0 && some_to_calculate
; j
++){
1255 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
1258 for(cdesc
= ps_global
->index_disp_format
;
1259 cdesc
->ctype
!= iNothing
&& space_left
> 0 && some_to_calculate
;
1261 if(cdesc
->ctype
== fixed_ctypes
[j
] && cdesc
->wtype
== WeCalculate
){
1262 some_to_calculate
--;
1263 fix
= MIN(cdesc
->actual_length
- cdesc
->width
, space_left
);
1264 cdesc
->width
+= fix
;
1270 * Fill in widths for Percent cases. If there are no more to calculate,
1271 * use the percentages as relative numbers and use the rest of the space,
1272 * else treat them as absolute percentages of the original avail screen.
1275 if(some_to_calculate
){
1276 int tot_requested
= 0;
1279 * Requests are treated as percent of screen width. See if they
1280 * will all fit. If not, trim them back proportionately.
1282 for(cdesc
= ps_global
->index_disp_format
;
1283 cdesc
->ctype
!= iNothing
;
1285 if(cdesc
->wtype
== Percent
){
1286 /* The 2, 200, and +100 are because we're rounding */
1287 fix
= ((2*cdesc
->req_width
*
1288 (screen_width
-colspace
))+100) / 200;
1289 tot_requested
+= fix
;
1293 if(tot_requested
> space_left
){
1294 int multiplier
= (100 * space_left
) / tot_requested
;
1296 for(cdesc
= ps_global
->index_disp_format
;
1297 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1299 if(cdesc
->wtype
== Percent
){
1300 /* The 2, 200, and +100 are because we're rounding */
1301 fix
= ((2*cdesc
->req_width
*
1302 (screen_width
-colspace
))+100) / 200;
1303 fix
= (2 * fix
* multiplier
+ 100) / 200;
1304 fix
= MIN(fix
, space_left
);
1305 cdesc
->width
+= fix
;
1311 for(cdesc
= ps_global
->index_disp_format
;
1312 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1314 if(cdesc
->wtype
== Percent
){
1315 /* The 2, 200, and +100 are because we're rounding */
1316 fix
= ((2*cdesc
->req_width
*
1317 (screen_width
-colspace
))+100) / 200;
1318 fix
= MIN(fix
, space_left
);
1319 cdesc
->width
+= fix
;
1327 was_sl
= space_left
;
1328 /* add up total percentages requested */
1329 for(cdesc
= ps_global
->index_disp_format
;
1330 cdesc
->ctype
!= iNothing
;
1332 if(cdesc
->wtype
== Percent
)
1333 tot_pct
+= cdesc
->req_width
;
1335 /* give relative weight to requests */
1336 for(cdesc
= ps_global
->index_disp_format
;
1337 cdesc
->ctype
!= iNothing
&& space_left
> 0 && tot_pct
> 0;
1339 if(cdesc
->wtype
== Percent
){
1340 fix
= ((2*cdesc
->req_width
*was_sl
)+tot_pct
) / (2*tot_pct
);
1341 fix
= MIN(fix
, space_left
);
1342 cdesc
->width
+= fix
;
1349 /* split up rest, give twice as much to Subject */
1351 while(space_left
> 0 && keep_going
){
1353 for(cdesc
= ps_global
->index_disp_format
;
1354 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1356 if(cdesc
->wtype
== WeCalculate
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1360 if(space_left
> 0 && (cdesc
->ctype
== iSubject
1361 || cdesc
->ctype
== iShortSubject
1362 || cdesc
->ctype
== iSubjectText
1363 || cdesc
->ctype
== iSubjKey
1364 || cdesc
->ctype
== iShortSubjKey
1365 || cdesc
->ctype
== iSubjKeyText
1366 || cdesc
->ctype
== iSubjKeyInit
1367 || cdesc
->ctype
== iShortSubjKeyInit
1368 || cdesc
->ctype
== iSubjKeyInitText
)){
1376 /* if still more, pad out percent's */
1378 while(space_left
> 0 && keep_going
){
1380 for(cdesc
= ps_global
->index_disp_format
;
1381 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1383 if(cdesc
->wtype
== Percent
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1391 /* if user made Fixed fields too big, give back space */
1393 while(space_left
< 0 && keep_going
){
1395 for(cdesc
= ps_global
->index_disp_format
;
1396 cdesc
->ctype
!= iNothing
&& space_left
< 0;
1398 if(cdesc
->wtype
== Fixed
&& cdesc
->width
> 0){
1406 if(pith_opt_save_index_state
)
1407 (*pith_opt_save_index_state
)(FALSE
);
1412 setup_thread_header_widths(MAILSTREAM
*stream
)
1414 clear_icache_flags(stream
);
1415 if(pith_opt_save_index_state
)
1416 (*pith_opt_save_index_state
)(TRUE
);
1421 * load_overview - c-client call back to gather overview data
1423 * Note: if we never get called, UID represents a hole
1424 * if we're passed a zero UID, totally bogus overview data
1425 * if we're passed a zero obuf, mostly bogus overview data
1428 load_overview(MAILSTREAM
*stream
, imapuid_t uid
, OVERVIEW
*obuf
, long unsigned int rawno
)
1430 if(obuf
&& rawno
>= 1L && stream
&& rawno
<= stream
->nmsgs
){
1434 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1438 * Only really load the thing if we've got an NNTP stream
1439 * otherwise we're just using mail_fetch_overview to load the
1440 * IMAP envelope cache with the specific set of messages
1443 idata
.stream
= stream
;
1444 idata
.rawno
= rawno
;
1445 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), idata
.rawno
);
1446 idata
.size
= obuf
->optional
.octets
;
1447 idata
.from
= obuf
->from
;
1448 idata
.date
= obuf
->date
;
1449 idata
.subject
= obuf
->subject
;
1451 ice
= (*format_index_line
)(&idata
);
1452 if(idata
.bogus
&& ice
){
1455 clear_ice(&ice
->tice
);
1460 else if(F_OFF(F_QUELL_NEWS_ENV_CB
, ps_global
)
1461 && (!THRD_INDX() || (ice
&& ice
->tice
))
1462 && !msgline_hidden(stream
, sp_msgmap(stream
), idata
.msgno
, 0)
1463 && pith_opt_paint_index_hline
){
1464 (*pith_opt_paint_index_hline
)(stream
, idata
.msgno
, ice
);
1471 build_header_work(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
1472 long int msgno
, long int top_msgno
, int msgcount
, int *fetched
)
1476 long n
, i
, cnt
, rawno
, visible
, limit
= -1L;
1478 rawno
= mn_m2raw(msgmap
, msgno
);
1482 ice
= fetch_ice(stream
, rawno
);
1486 if(ice
->tice
&& ice
->tice
->ifield
1487 && ice
->tice
->color_lookup_done
&& ice
->tice
->widths_done
){
1489 char buf
[MAX_SCREEN_COLS
+1];
1490 simple_index_line(buf
, sizeof(buf
), ice
->tice
, msgno
);
1492 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1495 buf
[0] ? strlen(buf
) : 0));
1500 ice
= fetch_ice(stream
, rawno
);
1504 if(ice
->ifield
&& ice
->color_lookup_done
&& ice
->widths_done
){
1506 char buf
[MAX_SCREEN_COLS
+1];
1507 simple_index_line(buf
, sizeof(buf
), ice
, msgno
);
1509 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1512 buf
[0] ? strlen(buf
) : 0));
1518 * If we are in THRD_INDX() and the width changed we don't currently
1519 * have a method of fixing just the widths and print_format strings.
1520 * Instead, we clear the index cache entry and start over.
1522 if(THRD_INDX() && ice
&& ice
->tice
&& ice
->tice
->ifield
1523 && !ice
->tice
->widths_done
){
1524 clear_ice(&ice
->tice
);
1528 * Fetch everything we need to start filling in the index line
1529 * explicitly via mail_fetch_overview. On an nntp stream
1530 * this has the effect of building the index lines in the
1531 * load_overview callback. Under IMAP we're either getting
1532 * the envelope data via the imap_envelope callback or
1533 * preloading the cache. Either way, we're getting exactly
1534 * what we want rather than relying on linear lookahead sort
1537 if(!(fetched
&& *fetched
) && index_in_overview(stream
)
1538 && ((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1539 || (!THRD_INDX() && !ice
->ifield
))){
1548 /* clear sequence bits */
1549 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1550 if((mc
= mail_elt(stream
, n
)) != NULL
)
1554 * Light interesting bits
1555 * NOTE: not set above because m2raw's cheaper
1556 * than raw2m for every message
1560 * Unfortunately, it is expensive to calculate visible pages
1561 * in thread index if we are zoomed, so we don't try.
1563 if(THRD_INDX() && any_lflagged(msgmap
, MN_HIDE
))
1564 visible
= msgmap
->visible_threads
;
1565 else if(THREADING() && sp_viewing_a_thread(stream
)){
1567 * We know that all visible messages in the thread are marked
1570 for(visible
= 0L, n
= top_msgno
;
1571 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1574 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1577 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1583 visible
= mn_get_total(msgmap
)
1584 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1586 limit
= MIN(visible
, msgcount
);
1592 * First add the msgno we're asking for in case it
1595 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, msgno
));
1596 if(msgno
<= mn_get_total(msgmap
)
1597 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1598 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1601 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, top_msgno
));
1604 * Loop through visible threads, marking them for fetching.
1605 * Stop at end of screen or sooner if we run out of visible
1609 n
= mn_raw2m(msgmap
, thrd
->rawno
);
1611 && n
<= mn_get_total(msgmap
)
1612 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1613 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1619 /* find next thread which is visible */
1621 if(mn_get_revsort(msgmap
) && thrd
->prevthd
)
1622 thrd
= fetch_thread(stream
, thrd
->prevthd
);
1623 else if(!mn_get_revsort(msgmap
) && thrd
->nextthd
)
1624 thrd
= fetch_thread(stream
, thrd
->nextthd
);
1628 && msgline_hidden(stream
, msgmap
,
1629 mn_raw2m(msgmap
, thrd
->rawno
), 0));
1636 * First add the msgno we're asking for in case it
1639 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)
1640 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,msgno
)))) || !ic
->ifield
)){
1641 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1643 * If we're doing a MUTTLIKE display the index line
1644 * may depend on the thread parent, and grandparent,
1645 * and further back. So just fetch the whole thread
1649 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1651 thrd
= fetch_thread(stream
, thrd
->top
);
1653 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1655 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1656 && (mc
= mail_elt(stream
,rawno
))
1657 && !mc
->private.msg
.env
){
1666 && n
<= mn_get_total(msgmap
)
1667 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,n
)))) || !ic
->ifield
)){
1668 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1670 * If we're doing a MUTTLIKE display the index line
1671 * may depend on the thread parent, and grandparent,
1672 * and further back. So just fetch the whole thread
1676 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1678 thrd
= fetch_thread(stream
, thrd
->top
);
1680 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1682 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1683 && (mc
= mail_elt(stream
,rawno
))
1684 && !mc
->private.msg
.env
){
1693 /* find next n which is visible */
1694 while(++n
<= mn_get_total(msgmap
)
1695 && msgline_hidden(stream
, msgmap
, n
, 0))
1701 seq
= build_sequence(stream
, NULL
, NULL
);
1703 ps_global
->dont_count_flagchanges
= 1;
1704 mail_fetch_overview_sequence(stream
, seq
,
1705 (stream
->dtb
&& stream
->dtb
->name
1706 && !strcmp(stream
->dtb
->name
, "imap"))
1707 ? NULL
: load_overview
);
1708 ps_global
->dont_count_flagchanges
= 0;
1709 fs_give((void **) &seq
);
1714 * reassign ice from the cache as it may've been built
1715 * within the overview callback or it may have become stale
1716 * in the prior sequence bit setting loop ...
1718 rawno
= mn_m2raw(msgmap
, msgno
);
1719 ice
= fetch_ice(stream
, rawno
);
1724 if((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1725 || (!THRD_INDX() && !ice
->ifield
)){
1729 * With pre-fetching/callback-formatting done and no success,
1730 * fall into formatting the requested line...
1732 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1733 idata
.stream
= stream
;
1734 idata
.msgno
= msgno
;
1735 idata
.rawno
= mn_m2raw(msgmap
, msgno
);
1736 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
1737 && (mc
= mail_elt(stream
, idata
.rawno
))){
1738 idata
.size
= mc
->rfc822_size
;
1739 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
1744 ice
= (*format_index_line
)(&idata
);
1750 * If needed, reset the print_format strings so that they add up to
1751 * the right total width. The reset width functionality isn't implemented
1752 * for THRD_INDX() so we are just doing a complete rebuild in that
1753 * case. This is driven by the clear_ice() call in clear_index_cache_ent()
1754 * so it should never be the case that THRD_INDX() is true and only
1755 * widths_done needs to be fixed.
1757 if((!THRD_INDX() && ice
->ifield
&& !ice
->widths_done
)){
1762 if(need_format_setup(stream
))
1763 setup_header_widths(stream
);
1766 working_ice
= ice
? ice
->tice
: NULL
;
1772 * First fix the ifield widths. The cdescs with nonzero widths
1773 * should correspond to the ifields that are defined.
1775 ifield
= working_ice
->ifield
;
1776 for(cdesc
= ps_global
->index_disp_format
;
1777 cdesc
->ctype
!= iNothing
&& ifield
; cdesc
++){
1779 if(cdesc
->ctype
!= ifield
->ctype
){
1780 dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno
, (int) cdesc
->ctype
, (int) ifield
->ctype
));
1784 ifield
->width
= cdesc
->width
;
1785 ifield
= ifield
->next
;
1789 /* fix the print_format strings and widths */
1790 for(ifield
= working_ice
->ifield
; ifield
; ifield
= ifield
->next
)
1791 set_ielem_widths_in_field(ifield
);
1793 working_ice
->widths_done
= 1;
1797 if(THRD_INDX() && ice
->tice
)
1798 ice
->tice
->color_lookup_done
= 1;
1801 * Look for a color for this line (and other lines in the current
1802 * view). This does a SEARCH for each role which has a color until
1803 * it finds a match. This will be satisfied by the c-client
1804 * cache created by the mail_fetch_overview above if it is a header
1807 if(!THRD_INDX() && !ice
->color_lookup_done
){
1808 COLOR_PAIR
*linecolor
;
1811 PAT_STATE
*pstate
= NULL
;
1813 if(pico_usingcolor()){
1815 if(THREADING() && sp_viewing_a_thread(stream
)){
1816 for(visible
= 0L, n
= top_msgno
;
1817 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1820 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1823 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1829 visible
= mn_get_total(msgmap
)
1830 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1832 limit
= MIN(visible
, msgcount
);
1834 /* clear sequence bits */
1835 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1836 if((mc
= mail_elt(stream
, n
)) != NULL
)
1843 && n
<= mn_get_total(msgmap
)
1844 && (!(ic
=fetch_ice(stream
,(rawno
= mn_m2raw(msgmap
, n
)))) || !ic
->color_lookup_done
)){
1846 if(rawno
>= 1L && rawno
<= stream
->nmsgs
1847 && (mc
= mail_elt(stream
, rawno
))){
1856 /* find next n which is visible */
1857 while(++n
<= mn_get_total(msgmap
)
1858 && msgline_hidden(stream
, msgmap
, n
, 0))
1863 * Why is there a loop here? The first call to get_index_line_color
1864 * will return a set of messages which match one of the roles.
1865 * Then, we eliminate those messages from the search set and try
1866 * again. This time we'd get past that role and into a different
1867 * role. Because of that, we hang onto the state and don't reset
1868 * to the first_pattern on the second and subsequent times
1869 * through the loop, avoiding fruitless match_pattern calls in
1870 * get_index_line_color.
1871 * Before the first call, pstate should be set to NULL.
1874 ss
= build_searchset(stream
);
1879 colormatch
= get_index_line_color(stream
, ss
, &pstate
,
1883 * Assign this color to all matched msgno's and
1884 * turn off the sequence bit so we won't check
1888 for(s
= ss
; s
; s
= s
->next
){
1889 for(n
= s
->first
; n
<= s
->last
; n
++){
1890 if(n
>= 1L && n
<= stream
->nmsgs
1891 && (mc
= mail_elt(stream
, n
))
1895 ic
= fetch_ice(stream
, n
);
1897 ic
->color_lookup_done
= 1;
1899 ic
->linecolor
= new_color_pair(linecolor
->fg
,
1907 free_color_pair(&linecolor
);
1910 /* have to mark the rest of the lookups done */
1911 for(s
= ss
; s
&& cnt
> 0; s
= s
->next
){
1912 for(n
= s
->first
; n
<= s
->last
&& cnt
> 0; n
++){
1913 if(n
>= 1L && n
<= stream
->nmsgs
1914 && (mc
= mail_elt(stream
, n
))
1917 ic
= fetch_ice(stream
, n
);
1919 ic
->color_lookup_done
= 1;
1924 /* just making sure */
1928 mail_free_searchset(&ss
);
1934 ice
= fetch_ice(stream
, mn_m2raw(msgmap
, msgno
));
1937 ice
->color_lookup_done
= 1;
1940 return(ice
); /* Return formatted index data */
1945 day_of_week(struct date
*d
)
1956 m
-= 3; /* March is month 0 */
1958 return((d
->day
+2+((7+31*m
)/12)+y
+(y
/4)+(y
/400)-(y
/100))%7);
1962 static int daytab
[2][13] = {
1963 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1964 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1968 day_of_year(struct date
*d
)
1972 if(d
->year
<= 0 || d
->month
< 1 || d
->month
> 12)
1976 leap
= (d
->year
%4 == 0 && d
->year
%100 != 0) || d
->year
%400 == 0;
1977 for(i
= 1; i
< d
->month
; i
++)
1978 doy
+= daytab
[leap
][i
];
1985 /*----------------------------------------------------------------------
1986 Format a string summarizing the message header for index on screen
1988 Args: buffer -- buffer to place formatted line
1989 idata -- snot it takes to format the line
1991 Result: returns pointer given buffer IF entry formatted
1992 else NULL if there was a problem (but the buffer is
1993 still suitable for display)
1996 format_index_index_line(INDEXDATA_S
*idata
)
1998 char str
[BIGWIDTH
+1], to_us
, status
, *field
,
2000 int i
, collapsed
= 0, start
, fromfield
;
2004 ADDRESS
*addr
, *toaddr
, *ccaddr
, *last_to
;
2005 PINETHRD_S
*thrd
= NULL
;
2006 INDEX_COL_S
*cdesc
= NULL
;
2010 COLOR_PAIR
*color
= NULL
;
2011 struct variable
*vars
= ps_global
->vars
;
2013 dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
2014 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
2017 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
2021 free_ifield(&ice
->ifield
);
2024 * Operate on a temporary copy of ice. The reason for this
2025 * is that we may end up causing a pine_mail_fetchenvelope() call
2026 * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
2027 * and mm_flags may do a clear_ice(), freeing the ice we are working
2028 * on out from under us. We try to fetch everything we need in
2029 * build_header_work() but c-client will short-circuit our request
2030 * if we already got the raw header for some reason. One possible
2031 * reason is a categorizer command in a filter. In that case
2032 * we still need a fetch fast to get the rest of the envelope data.
2034 ice
= copy_ice(ice
);
2036 /* is this a collapsed thread index line? */
2037 if(!idata
->bogus
&& THREADING()){
2038 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2039 collapsed
= thrd
&& thrd
->next
2040 && get_lflag(idata
->stream
, NULL
,
2041 idata
->rawno
, MN_COLL
);
2044 /* calculate contents of the required fields */
2045 for(cdesc
= ps_global
->index_disp_format
; cdesc
->ctype
!= iNothing
; cdesc
++)
2047 memset(str
, 0, sizeof(str
));
2048 ifield
= new_ifield(&ice
->ifield
);
2049 ifield
->ctype
= cdesc
->ctype
;
2050 ifield
->width
= cdesc
->width
;
2054 if(cdesc
->ctype
== iMessNo
)
2055 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2056 else if(idata
->bogus
< 2 && (cdesc
->ctype
== iSubject
2057 || cdesc
->ctype
== iShortSubject
2058 || cdesc
->ctype
== iSubjectText
2059 || cdesc
->ctype
== iSubjKey
2060 || cdesc
->ctype
== iShortSubjKey
2061 || cdesc
->ctype
== iSubjKeyText
2062 || cdesc
->ctype
== iSubjKeyInit
2063 || cdesc
->ctype
== iShortSubjKeyInit
2064 || cdesc
->ctype
== iSubjKeyInitText
))
2065 snprintf(str
, sizeof(str
), "%s", _("[ No Message Text Available ]"));
2068 switch(cdesc
->ctype
){
2070 to_us
= status
= ' ';
2072 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2073 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
2074 status
= status_symbol_for_thread(idata
->stream
, thrd
,
2078 if(idata
->rawno
> 0L && idata
->rawno
<= idata
->stream
->nmsgs
2079 && (mc
=mail_elt(idata
->stream
,idata
->rawno
)) && mc
->flagged
)
2080 to_us
= '*'; /* simple */
2081 else if(!IS_NEWS(idata
->stream
)){
2082 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2083 if(address_is_us(addr
, ps_global
)){
2091 if(to_us
!= '+' && resent_to_us(idata
)){
2097 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2098 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2099 if(address_is_us(addr
, ps_global
)){
2106 status
= (!idata
->stream
|| !IS_NEWS(idata
->stream
)
2107 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))
2113 if(user_flag_is_set(idata
->stream
, idata
->rawno
, FORWARDED_FLAG
))
2123 snprintf(str
, sizeof(str
), "%c %c", to_us
, status
);
2125 ifield
->leftadj
= 1;
2126 for(i
= 0; i
< 3; i
++){
2127 ielem
= new_ielem(&ifield
->ielem
);
2128 ielem
->freedata
= 1;
2129 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2130 ielem
->data
[0] = str
[i
];
2131 ielem
->data
[1] = '\0';
2133 set_print_format(ielem
, 1, ifield
->leftadj
);
2136 if(pico_usingcolor()){
2139 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2140 ielem
= ifield
->ielem
;
2141 ielem
->freecolor
= 1;
2142 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2145 else if(str
[0] == '+' || str
[0] == '-'){
2146 if(VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
2147 ielem
= ifield
->ielem
;
2148 ielem
->freecolor
= 1;
2149 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2154 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2155 ielem
= ifield
->ielem
->next
->next
;
2156 ielem
->freecolor
= 1;
2157 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2160 else if(str
[2] == 'A'){
2161 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2162 ielem
= ifield
->ielem
->next
->next
;
2163 ielem
->freecolor
= 1;
2164 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2167 else if(str
[2] == 'F'){
2168 if(VAR_IND_FWD_FORE_COLOR
&& VAR_IND_FWD_BACK_COLOR
){
2169 ielem
= ifield
->ielem
->next
->next
;
2170 ielem
->freecolor
= 1;
2171 ielem
->color
= new_color_pair(VAR_IND_FWD_FORE_COLOR
, VAR_IND_FWD_BACK_COLOR
);
2174 else if(str
[2] == 'N'){
2175 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2176 ielem
= ifield
->ielem
->next
->next
;
2177 ielem
->freecolor
= 1;
2178 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2189 char new, answered
, deleted
, flagged
;
2192 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2193 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 0);
2197 if(!IS_NEWS(idata
->stream
)){
2198 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2199 if(address_is_us(addr
, ps_global
)){
2204 if(to_us
== ' ' && resent_to_us(idata
))
2207 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2208 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2209 if(address_is_us(addr
, ps_global
)){
2216 new = answered
= deleted
= flagged
= ' ';
2219 unsigned long save_branch
, cnt
, tot_in_thrd
;
2222 * Branch is a sibling, not part of the thread, so
2223 * don't consider it when displaying this line.
2225 save_branch
= thrd
->branch
;
2228 tot_in_thrd
= count_flags_in_thread(idata
->stream
, thrd
,
2231 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_DEL
);
2233 deleted
= (cnt
== tot_in_thrd
) ? 'D' : 'd';
2235 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_ANS
);
2237 answered
= (cnt
== tot_in_thrd
) ? 'A' : 'a';
2239 /* no lower case *, same thing for some or all */
2240 if(count_flags_in_thread(idata
->stream
, thrd
, F_FLAG
))
2243 new = status_symbol_for_thread(idata
->stream
, thrd
,
2246 thrd
->branch
= save_branch
;
2249 mc
= (idata
->rawno
> 0L && idata
->stream
2250 && idata
->rawno
<= idata
->stream
->nmsgs
)
2251 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2252 if(mc
&& mc
->valid
){
2253 if(cdesc
->ctype
== iIStatus
|| cdesc
->ctype
== iSIStatus
){
2255 new = mc
->seen
? 'R' : 'N';
2260 && (!IS_NEWS(idata
->stream
)
2261 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)))
2275 snprintf(str
, sizeof(str
), "%c %c%c%c%c", to_us
, flagged
, new,
2278 if(cdesc
->ctype
== iSIStatus
)
2283 ifield
->leftadj
= 1;
2284 for(i
= start
; i
< 6; i
++){
2285 ielem
= new_ielem(&ifield
->ielem
);
2286 ielem
->freedata
= 1;
2287 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2288 ielem
->data
[0] = str
[i
];
2289 ielem
->data
[1] = '\0';
2291 set_print_format(ielem
, 1, ifield
->leftadj
);
2294 if(pico_usingcolor()){
2296 if(str
[0] == '+' || str
[0] == '-'){
2298 && VAR_IND_PLUS_FORE_COLOR
2299 && VAR_IND_PLUS_BACK_COLOR
){
2300 ielem
= ifield
->ielem
;
2301 ielem
->freecolor
= 1;
2302 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2307 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2309 ielem
= ifield
->ielem
;
2311 ielem
= ifield
->ielem
->next
->next
;
2313 ielem
->freecolor
= 1;
2314 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2318 if(str
[3] == 'N' || str
[3] == 'n'){
2319 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2321 ielem
= ifield
->ielem
->next
;
2323 ielem
= ifield
->ielem
->next
->next
->next
;
2325 ielem
->freecolor
= 1;
2326 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2329 else if(str
[3] == 'R' || str
[3] == 'r'){
2330 if(VAR_IND_REC_FORE_COLOR
&& VAR_IND_REC_BACK_COLOR
){
2332 ielem
= ifield
->ielem
->next
;
2334 ielem
= ifield
->ielem
->next
->next
->next
;
2336 ielem
->freecolor
= 1;
2337 ielem
->color
= new_color_pair(VAR_IND_REC_FORE_COLOR
, VAR_IND_REC_BACK_COLOR
);
2340 else if(str
[3] == 'U' || str
[3] == 'u'){
2341 if(VAR_IND_UNS_FORE_COLOR
&& VAR_IND_UNS_BACK_COLOR
){
2343 ielem
= ifield
->ielem
->next
;
2345 ielem
= ifield
->ielem
->next
->next
->next
;
2347 ielem
->freecolor
= 1;
2348 ielem
->color
= new_color_pair(VAR_IND_UNS_FORE_COLOR
, VAR_IND_UNS_BACK_COLOR
);
2352 if(str
[4] == 'A' || str
[4] == 'a'){
2353 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2355 ielem
= ifield
->ielem
->next
->next
;
2357 ielem
= ifield
->ielem
->next
->next
->next
->next
;
2359 ielem
->freecolor
= 1;
2360 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2364 if(str
[5] == 'D' || str
[5] == 'd'){
2365 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2367 ielem
= ifield
->ielem
->next
->next
->next
;
2369 ielem
= ifield
->ielem
->next
->next
->next
->next
->next
;
2371 ielem
->freecolor
= 1;
2372 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2382 * This is a special case. The message number is
2383 * generated on the fly in the painting routine.
2384 * But the data array is allocated here in case it
2385 * is useful for the paint routine.
2387 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2391 snprintf(str
, sizeof(str
), "%-*.*s", ifield
->width
, ifield
->width
, " ");
2392 if(VAR_IND_ARR_FORE_COLOR
&& VAR_IND_ARR_BACK_COLOR
){
2393 ifield
->leftadj
= 1;
2394 ielem
= new_ielem(&ifield
->ielem
);
2395 ielem
->freedata
= 1;
2396 ielem
->data
= cpystr(str
);
2397 ielem
->datalen
= strlen(str
);
2398 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
2399 ielem
->freecolor
= 1;
2400 ielem
->color
= new_color_pair(VAR_IND_ARR_FORE_COLOR
,
2401 VAR_IND_ARR_BACK_COLOR
);
2407 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2408 if(score
== SCORE_UNDEF
){
2409 SEARCHSET
*ss
= NULL
;
2411 ss
= mail_newsearchset();
2412 ss
->first
= ss
->last
= (unsigned long) idata
->rawno
;
2415 * This looks like it might be expensive to get the
2416 * score for each message when needed but it shouldn't
2417 * be too bad because we know we have the envelope
2418 * data cached. We can't calculate all of the scores
2419 * we need for the visible messages right here in
2420 * one fell swoop because we don't have the other
2421 * envelopes yet. And we can't get the other
2422 * envelopes at this point because we may be in
2423 * the middle of a c-client callback (pine_imap_env).
2424 * (Actually we could, because we know whether or
2425 * not we're in the callback because of the no_fetch
2427 * We have another problem if the score rules depend
2428 * on something other than envelope data. I guess they
2429 * only do that if they have an alltext (search the
2430 * text of the message) definition. So, we're going
2431 * to pass no_fetch to calculate_scores so that it
2432 * can return an error if we need the text data but
2433 * can't get it because of no_fetch. Setting bogus
2434 * will cause us to do the scores calculation later
2435 * when we are no longer in the callback.
2438 (calculate_some_scores(idata
->stream
,
2439 ss
, idata
->no_fetch
) == 0)
2441 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2442 mail_free_searchset(&ss
);
2446 snprintf(str
, sizeof(str
), "%ld", score
!= SCORE_UNDEF
? score
: 0L);
2449 case iDate
: case iMonAbb
: case iLDate
:
2450 case iSDate
: case iSTime
:
2451 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
2452 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
2453 case iSDateIsoS
: case iSDateIso
:
2454 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
2456 case iSDateTimeIsoS
: case iSDateTimeIso
:
2457 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
2459 case iSDateTimeIsoS24
: case iSDateTimeIso24
:
2460 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
2461 case iTimezone
: case iYear
: case iYear2Digit
:
2462 case iRDate
: case iDay
: case iDay2Digit
: case iMon2Digit
:
2463 case iDayOrdinal
: case iMon
: case iMonLong
:
2464 case iDayOfWeekAbb
: case iDayOfWeek
:
2465 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
2466 date_str(fetch_date(idata
), cdesc
->ctype
, 0, str
, sizeof(str
), cdesc
->monabb_width
);
2470 case iFromToNotNews
:
2475 from_str(cdesc
->ctype
, idata
, str
, sizeof(str
), ice
);
2479 if(((field
= ((addr
= fetch_to(idata
))
2481 : (addr
= fetch_cc(idata
))
2484 && !set_index_addr(idata
, field
, addr
, NULL
, BIGWIDTH
, str
))
2486 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2487 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, newsgroups
);
2492 set_index_addr(idata
, "Cc", fetch_cc(idata
), NULL
, BIGWIDTH
, str
);
2496 toaddr
= fetch_to(idata
);
2497 ccaddr
= fetch_cc(idata
);
2498 for(last_to
= toaddr
;
2499 last_to
&& last_to
->next
;
2500 last_to
= last_to
->next
)
2503 /* point end of to list temporarily at cc list */
2505 last_to
->next
= ccaddr
;
2507 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2510 last_to
->next
= NULL
;
2516 if((addr
= fetch_sender(idata
)) != NULL
)
2517 set_index_addr(idata
, "Sender", addr
, NULL
, BIGWIDTH
, str
);
2524 if((addr
= fetch_from(idata
)) && addr
->personal
){
2525 char *name
, *initials
= NULL
;
2527 name
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
2528 SIZEOF_20KBUF
, addr
->personal
);
2529 if(name
== addr
->personal
){
2530 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
2531 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
2532 name
= (char *) tmp_20k_buf
;
2536 initials
= reply_quote_initials(name
);
2537 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, initials
);
2546 if((l
= fetch_size(idata
)) < 10*1000L)
2547 snprintf(str
, sizeof(str
), "(%lu)", l
);
2549 else if(l
< 1000L*1000L - 1000L/2){
2550 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2551 snprintf(str
, sizeof(str
), "(%luK)", l
);
2553 /* 1.0M ... 99.9M */
2554 else if(l
< 1000L*100L*1000L - 100L*1000L/2){
2555 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2557 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2559 /* 100M ... 2000M */
2560 else if(l
<= 2*1000L*1000L*1000L){
2561 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2563 snprintf(str
, sizeof(str
), "(%luM)", l
);
2566 snprintf(str
, sizeof(str
), "(HUGE!)");
2572 if((l
= fetch_size(idata
)) < 100*1000L)
2573 snprintf(str
, sizeof(str
), "(%s)", comatose(l
));
2574 /* 100K ... 9,999K */
2575 else if(l
< 10L*1000L*1000L - 1000L/2){
2576 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2577 snprintf(str
, sizeof(str
), "(%sK)", comatose(l
));
2579 /* 10.0M ... 999.9M */
2580 else if(l
< 1000L*1000L*1000L - 100L*1000L/2){
2581 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2583 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2585 /* 1,000M ... 2,000M */
2586 else if(l
<= 2*1000L*1000L*1000L){
2587 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2589 snprintf(str
, sizeof(str
), "(%sM)", comatose(l
));
2592 snprintf(str
, sizeof(str
), "(HUGE!)");
2598 if((l
= fetch_size(idata
)) < 1000L)
2599 snprintf(str
, sizeof(str
), "(%lu)", l
);
2601 else if(l
< 100L*1000L - 1000L/2){
2602 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2603 snprintf(str
, sizeof(str
), "(%luK)", l
);
2606 else if(l
< 1000L*1000L - 100L*1000L/2){
2607 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= 100L*1000L/2
2609 snprintf(str
, sizeof(str
), "(.%luM)", l
);
2612 else if(l
< 1000L*100L*1000L - 1000L*1000L/2){
2613 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2615 snprintf(str
, sizeof(str
), "(%luM)", l
);
2618 else if(l
< 1000L*1000L*1000L - 100L*1000L*1000L/2){
2619 l
= l
/(100L*1000L*1000L) + (l
%(100L*1000L*1000L) >=
2620 (100L*1000L*1000L/2) ? 1L : 0L);
2621 snprintf(str
, sizeof(str
), "(.%luG)", l
);
2624 else if(l
<= 2*1000L*1000L*1000L){
2625 l
= l
/(1000L*1000L*1000L) + (l
%(1000L*1000L*1000L) >=
2626 (1000L*1000L*1000L/2) ? 1L : 0L);
2627 snprintf(str
, sizeof(str
), "(%luG)", l
);
2630 snprintf(str
, sizeof(str
), "(HUGE!)");
2634 /* From Carl Jacobsen <carl@ucsd.edu> */
2636 l
= fetch_size(idata
);
2637 l
= (l
/ 1024L) + (l
% 1024L != 0 ? 1 : 0);
2639 if(l
< 1024L) { /* 0k .. 1023k */
2640 snprintf(str
, sizeof(str
), "(%luk)", l
);
2642 } else if (l
< 100L * 1024L){ /* 1.0M .. 99.9M */
2643 snprintf(str
, sizeof(str
), "(%lu.M)", (l
* 10L) / 1024L);
2644 if ((p
= strchr(str
, '.')) != NULL
) {
2645 p
--; p
[1] = p
[0]; p
[0] = '.'; /* swap last digit & . */
2647 } else if (l
<= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2648 snprintf(str
, sizeof(str
), "(%luM)", l
/ 1024L);
2650 snprintf(str
, sizeof(str
), "(HUGE!)");
2656 if((body
= fetch_body(idata
)) != NULL
)
2660 mc
= (idata
->rawno
> 0L && idata
->stream
2661 && idata
->rawno
<= idata
->stream
->nmsgs
)
2662 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2663 if(mc
&& mc
->rfc822_size
< 6000)
2664 snprintf(str
, sizeof(str
), "(short )");
2665 else if(mc
&& mc
->rfc822_size
< 25000)
2666 snprintf(str
, sizeof(str
), "(medium )");
2667 else if(mc
&& mc
->rfc822_size
< 100000)
2668 snprintf(str
, sizeof(str
), "(long )");
2670 snprintf(str
, sizeof(str
), "(huge )");
2676 if(strucmp(body
->subtype
, "MIXED") == 0){
2679 x
= body
->nested
.part
2680 ? body
->nested
.part
->body
.type
2684 if(body
->nested
.part
->body
.size
.bytes
< 6000)
2685 snprintf(str
, sizeof(str
), "(short+ )");
2686 else if(body
->nested
.part
->body
.size
.bytes
2688 snprintf(str
, sizeof(str
), "(medium+)");
2689 else if(body
->nested
.part
->body
.size
.bytes
2691 snprintf(str
, sizeof(str
), "(long+ )");
2693 snprintf(str
, sizeof(str
), "(huge+ )");
2697 snprintf(str
, sizeof(str
), "(multi )");
2701 else if(strucmp(body
->subtype
, "DIGEST") == 0)
2702 snprintf(str
, sizeof(str
), "(digest )");
2703 else if(strucmp(body
->subtype
, "ALTERNATIVE") == 0)
2704 snprintf(str
, sizeof(str
), "(mul/alt)");
2705 else if(strucmp(body
->subtype
, "PARALLEL") == 0)
2706 snprintf(str
, sizeof(str
), "(mul/par)");
2708 snprintf(str
, sizeof(str
), "(multi )");
2713 snprintf(str
, sizeof(str
), "(message)");
2716 case TYPEAPPLICATION
:
2717 snprintf(str
, sizeof(str
), "(applica)");
2721 snprintf(str
, sizeof(str
), "(audio )");
2725 snprintf(str
, sizeof(str
), "(image )");
2729 snprintf(str
, sizeof(str
), "(video )");
2733 snprintf(str
, sizeof(str
), "(other )");
2742 if((body
= fetch_body(idata
)) &&
2743 body
->type
== TYPEMULTIPART
&&
2744 strucmp(body
->subtype
, "ALTERNATIVE") != 0){
2748 part
= body
->nested
.part
; /* 1st part, don't count */
2749 while(part
&& part
->next
&& atts
< 10){
2757 str
[0] = '0' + atts
;
2763 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 0, ice
);
2767 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 1, ice
);
2771 subj_str(idata
, str
, sizeof(str
), NoKW
, 1, 0, ice
);
2775 subj_str(idata
, str
, sizeof(str
), KW
, 0, 0, ice
);
2779 subj_str(idata
, str
, sizeof(str
), KW
, 0, 1, ice
);
2783 subj_str(idata
, str
, sizeof(str
), KW
, 1, 0, ice
);
2787 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 0, ice
);
2790 case iShortSubjKeyInit
:
2791 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 1, ice
);
2794 case iSubjKeyInitText
:
2795 subj_str(idata
, str
, sizeof(str
), KWInit
, 1, 0, ice
);
2799 case iOpeningTextNQ
:
2805 first_text
= fetch_firsttext(idata
, cdesc
->ctype
== iOpeningTextNQ
);
2808 strncpy(str
, first_text
, BIGWIDTH
);
2809 str
[BIGWIDTH
] = '\0';
2810 fs_give((void **) &first_text
);
2817 key_str(idata
, KW
, ice
);
2821 key_str(idata
, KWInit
, ice
);
2825 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
){
2826 strncpy(str
, newsgroups
, BIGWIDTH
);
2827 str
[BIGWIDTH
] = '\0';
2833 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2834 strncpy(str
, newsgroups
, sizeof(str
));
2836 if((l
= strlen(str
)) < sizeof(str
)){
2837 if(sizeof(str
) - l
< 6)
2838 strncpy(str
+l
, "...", sizeof(str
)-l
);
2841 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2842 set_index_addr(idata
, "To", fetch_to(idata
),
2843 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2848 set_index_addr(idata
, "To", fetch_to(idata
),
2849 NULL
, BIGWIDTH
, str
);
2856 set_index_addr(idata
, "To", fetch_to(idata
),
2857 NULL
, BIGWIDTH
, str
);
2858 if((l
= strlen(str
)) < sizeof(str
) &&
2859 (newsgroups
= fetch_newsgroups(idata
))){
2860 if(sizeof(str
) - l
< 6)
2861 strncpy(str
+l
, "...", sizeof(str
)-l
);
2864 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2867 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2869 strncpy(str
, newsgroups
, BIGWIDTH
);
2875 case iNewsAndRecips
:
2876 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2877 strncpy(str
, newsgroups
, BIGWIDTH
);
2879 if((l
= strlen(str
)) < BIGWIDTH
){
2880 if(BIGWIDTH
- l
< 6)
2881 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2883 toaddr
= fetch_to(idata
);
2884 ccaddr
= fetch_cc(idata
);
2885 for(last_to
= toaddr
;
2886 last_to
&& last_to
->next
;
2887 last_to
= last_to
->next
)
2890 /* point end of to list temporarily at cc list */
2892 last_to
->next
= ccaddr
;
2895 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2896 set_index_addr(idata
, "To", toaddr
,
2897 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2902 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2905 last_to
->next
= NULL
;
2911 case iRecipsAndNews
:
2912 toaddr
= fetch_to(idata
);
2913 ccaddr
= fetch_cc(idata
);
2914 for(last_to
= toaddr
;
2915 last_to
&& last_to
->next
;
2916 last_to
= last_to
->next
)
2919 /* point end of to list temporarily at cc list */
2921 last_to
->next
= ccaddr
;
2923 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2926 last_to
->next
= NULL
;
2928 if((l
= strlen(str
)) < BIGWIDTH
&&
2929 (newsgroups
= fetch_newsgroups(idata
))){
2930 if(BIGWIDTH
- l
< 6)
2931 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2934 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2937 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2939 strncpy(str
, newsgroups
, BIGWIDTH
);
2948 prio_str(idata
, cdesc
->ctype
, ice
);
2952 header_str(idata
, cdesc
->hdrtok
, ice
);
2956 strncpy(str
, (cdesc
->hdrtok
&& cdesc
->hdrtok
->hdrname
) ? cdesc
->hdrtok
->hdrname
: "", sizeof(str
));
2957 str
[sizeof(str
)-1] = '\0';
2965 * If the element wasn't already filled in above, do it here.
2968 ielem
= new_ielem(&ifield
->ielem
);
2970 if((color
= hdr_color(itokens
[itokensinv
[cdesc
->ctype
].ctype
].name
, NULL
, ps_global
->index_token_colors
)) != NULL
){
2971 if(pico_usingcolor()){
2972 ielem
->color
= new_color_pair(color
->fg
, color
->bg
);
2973 ielem
->type
= eTypeCol
;
2975 free_color_pair(&color
);
2978 ielem
->freedata
= 1;
2979 ielem
->data
= cpystr(str
);
2980 ielem
->datalen
= strlen(str
);
2982 if(fromfield
&& pico_usingcolor()
2983 && ps_global
->VAR_IND_FROM_FORE_COLOR
2984 && ps_global
->VAR_IND_FROM_BACK_COLOR
){
2985 ielem
->type
= eTypeCol
;
2986 ielem
->freecolor
= 1;
2987 ielem
->color
= new_color_pair(ps_global
->VAR_IND_FROM_FORE_COLOR
,
2988 ps_global
->VAR_IND_FROM_BACK_COLOR
);
2990 * This space is here so that if the text does
2991 * not extend all the way to the end of the field then
2992 * we'll switch the color back and paint the rest of the
2993 * field in the Normal color or the index line color.
2995 ielem
= new_ielem(&ielem
);
2996 ielem
->freedata
= 1;
2997 ielem
->data
= cpystr(" ");
3000 else if((cdesc
->ctype
== iOpeningText
|| cdesc
->ctype
== iOpeningTextNQ
)
3001 && pico_usingcolor()
3002 && ps_global
->VAR_IND_OP_FORE_COLOR
3003 && ps_global
->VAR_IND_OP_BACK_COLOR
){
3004 ielem
->type
= eTypeCol
;
3005 ielem
->freecolor
= 1;
3006 ielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
,
3007 ps_global
->VAR_IND_OP_BACK_COLOR
);
3009 * This space is here so that if the text does
3010 * not extend all the way to the end of the field then
3011 * we'll switch the color back and paint the rest of the
3012 * field in the Normal color or the index line color.
3014 ielem
= new_ielem(&ielem
);
3015 ielem
->freedata
= 1;
3016 ielem
->data
= cpystr(" ");
3020 ifield
->leftadj
= (cdesc
->adjustment
== Left
) ? 1 : 0;
3021 set_ielem_widths_in_field(ifield
);
3025 ice
->widths_done
= 1;
3026 ice
->id
= ice_hash(ice
);
3029 * Now we have to put the temporary copy of ice back as the
3032 icep
= fetch_ice_ptr(idata
->stream
, idata
->rawno
);
3034 free_ice(icep
); /* free what is already there */
3043 format_thread_index_line(INDEXDATA_S
*idata
)
3045 char *p
, buffer
[BIGWIDTH
+1];
3046 int thdlen
, space_left
, i
;
3047 PINETHRD_S
*thrd
= NULL
;
3048 ICE_S
*ice
, *tice
= NULL
, **ticep
= NULL
;
3051 int (*save_sfstr_func
)(void);
3052 struct variable
*vars
= ps_global
->vars
;
3054 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3055 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
3057 space_left
= ps_global
->ttyo
->screen_cols
;
3059 if(ps_global
->msgmap
->max_thrdno
< 1000)
3061 else if(ps_global
->msgmap
->max_thrdno
< 10000)
3063 else if(ps_global
->msgmap
->max_thrdno
< 100000)
3068 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
3070 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
3072 if(!thrd
|| !ice
) /* can't happen? */
3076 tice
= (ICE_S
*) fs_get(sizeof(*tice
));
3077 memset(tice
, 0, sizeof(*tice
));
3086 free_ifield(&tice
->ifield
);
3089 tice
= copy_ice(tice
);
3091 if(space_left
>= 3){
3095 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
3096 status
= status_symbol_for_thread(idata
->stream
, thrd
, iStatus
);
3098 if((p
-buffer
)+3 < sizeof(buffer
)){
3107 ifield
= new_ifield(&tice
->ifield
);
3108 ifield
->ctype
= iStatus
;
3110 ifield
->leftadj
= 1;
3111 for(i
= 0; i
< 3; i
++){
3112 ielem
= new_ielem(&ifield
->ielem
);
3113 ielem
->freedata
= 1;
3114 ielem
->data
= (char *) fs_get(2 * sizeof(char));
3115 ielem
->data
[0] = p
[i
];
3116 ielem
->data
[1] = '\0';
3118 set_print_format(ielem
, 1, ifield
->leftadj
);
3121 if(pico_usingcolor()){
3123 && VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
3124 ielem
= ifield
->ielem
;
3125 ielem
->freecolor
= 1;
3126 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3127 VAR_IND_IMP_BACK_COLOR
);
3128 if(F_ON(F_COLOR_LINE_IMPORTANT
, ps_global
))
3129 tice
->linecolor
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3130 VAR_IND_IMP_BACK_COLOR
);
3132 else if((to_us
== '+' || to_us
== '-')
3133 && VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
3134 ielem
= ifield
->ielem
;
3135 ielem
->freecolor
= 1;
3136 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
,
3137 VAR_IND_PLUS_BACK_COLOR
);
3141 && VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
3142 ielem
= ifield
->ielem
->next
->next
;
3143 ielem
->freecolor
= 1;
3144 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
,
3145 VAR_IND_DEL_BACK_COLOR
);
3147 else if(status
== 'N'
3148 && VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
3149 ielem
= ifield
->ielem
->next
->next
;
3150 ielem
->freecolor
= 1;
3151 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
,
3152 VAR_IND_NEW_BACK_COLOR
);
3157 if(space_left
>= thdlen
+1){
3161 snprintf(p
, sizeof(buffer
), "%*.*s", thdlen
, thdlen
, "");
3162 space_left
-= thdlen
;
3164 ifield
= new_ifield(&tice
->ifield
);
3165 ifield
->ctype
= iMessNo
;
3166 ifield
->width
= thdlen
;
3167 ifield
->leftadj
= 0;
3168 ielem
= new_ielem(&ifield
->ielem
);
3169 ielem
->freedata
= 1;
3170 ielem
->data
= cpystr(p
);
3171 ielem
->datalen
= strlen(p
);
3172 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3175 if(space_left
>= 7){
3180 date_str(fetch_date(idata
), iDate
, 0, p
, sizeof(buffer
), 0);
3181 if(sizeof(buffer
) > 6)
3184 if(strlen(p
) < 6 && (sizeof(buffer
)) > 6){
3187 for(q
= p
+ strlen(p
); q
< p
+ 6; q
++)
3193 ifield
= new_ifield(&tice
->ifield
);
3194 ifield
->ctype
= iDate
;
3196 ifield
->leftadj
= 1;
3197 ielem
= new_ielem(&ifield
->ielem
);
3198 ielem
->freedata
= 1;
3199 ielem
->data
= cpystr(p
);
3200 ielem
->datalen
= ifield
->width
;
3201 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3206 int from_width
, subj_width
, bigthread_adjust
;
3208 char from
[BIGWIDTH
+1];
3213 in_thread
= count_lflags_in_thread(idata
->stream
, thrd
,
3214 ps_global
->msgmap
, MN_NONE
);
3217 if(in_thread
== 1 && THRD_AUTO_VIEW())
3218 snprintf(tcnt
, sizeof(tcnt
), " ");
3220 snprintf(tcnt
, sizeof(tcnt
), "(%ld)", in_thread
);
3222 bigthread_adjust
= MAX(0, strlen(tcnt
) - 3);
3224 /* third of the rest */
3225 from_width
= MAX((space_left
-1)/3 - bigthread_adjust
, 1);
3228 subj_width
= space_left
- from_width
- 1;
3230 if(strlen(tcnt
) > subj_width
)
3231 tcnt
[subj_width
] = '\0';
3234 save_sfstr_func
= pith_opt_truncate_sfstr
;
3235 pith_opt_truncate_sfstr
= NULL
;
3236 from_str(iFromTo
, idata
, from
, sizeof(from
), tice
);
3237 pith_opt_truncate_sfstr
= save_sfstr_func
;
3239 ifield
= new_ifield(&tice
->ifield
);
3240 ifield
->leftadj
= 1;
3241 ielem
= new_ielem(&ifield
->ielem
);
3242 ielem
->freedata
= 1;
3243 ielem
->type
= eTypeCol
;
3244 ielem
->data
= cpystr(from
);
3245 ielem
->datalen
= strlen(from
);
3246 ifield
->width
= from_width
;
3247 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3248 ifield
->ctype
= iFrom
;
3249 if(from_width
> 0 && pico_usingcolor()
3250 && VAR_IND_FROM_FORE_COLOR
&& VAR_IND_FROM_BACK_COLOR
){
3251 ielem
->freecolor
= 1;
3252 ielem
->color
= new_color_pair(VAR_IND_FROM_FORE_COLOR
,
3253 VAR_IND_FROM_BACK_COLOR
);
3256 ifield
= new_ifield(&tice
->ifield
);
3257 ifield
->leftadj
= 0;
3258 ielem
= new_ielem(&ifield
->ielem
);
3259 ielem
->freedata
= 1;
3260 ielem
->data
= cpystr(tcnt
);
3261 ielem
->datalen
= strlen(tcnt
);
3262 ifield
->width
= ielem
->datalen
;
3263 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3264 ifield
->ctype
= iAtt
; /* not used, except that it isn't special */
3266 subj_width
-= strlen(tcnt
);
3273 if(idata
->bogus
< 2)
3274 snprintf(buffer
, sizeof(buffer
), "%-.*s", BIGWIDTH
,
3275 _("[ No Message Text Available ]"));
3279 save_sfstr_func
= pith_opt_truncate_sfstr
;
3280 pith_opt_truncate_sfstr
= NULL
;
3281 subj_str(idata
, buffer
, sizeof(buffer
), NoKW
, 0, 0, NULL
);
3282 pith_opt_truncate_sfstr
= save_sfstr_func
;
3285 ifield
= new_ifield(&tice
->ifield
);
3286 ifield
->leftadj
= 1;
3287 ielem
= new_ielem(&ifield
->ielem
);
3288 ielem
->freedata
= 1;
3289 ielem
->type
= eTypeCol
;
3290 ielem
->data
= cpystr(buffer
);
3291 ielem
->datalen
= strlen(buffer
);
3292 ifield
->width
= subj_width
;
3293 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3294 ifield
->ctype
= iSubject
;
3295 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR
&& VAR_IND_SUBJ_BACK_COLOR
){
3296 ielem
->freecolor
= 1;
3297 ielem
->color
= new_color_pair(VAR_IND_SUBJ_FORE_COLOR
,
3298 VAR_IND_SUBJ_BACK_COLOR
);
3302 else if(space_left
> 1){
3303 snprintf(p
, sizeof(buffer
)-(p
-buffer
), "%-.*s", space_left
-1, " ");
3304 ifield
= new_ifield(&tice
->ifield
);
3305 ifield
->leftadj
= 1;
3306 ielem
= new_ielem(&ifield
->ielem
);
3307 ielem
->freedata
= 1;
3308 ielem
->data
= cpystr(p
);
3309 ielem
->datalen
= strlen(p
);
3310 ifield
->width
= space_left
-1;
3311 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3312 ifield
->ctype
= iSubject
;
3315 tice
->widths_done
= 1;
3316 tice
->id
= ice_hash(tice
);
3319 free_ice(ticep
); /* free what is already there */
3328 * Print the fields of ice in buf with a single space between fields.
3330 * Args buf -- place to put the line
3331 * ice -- the data for the line
3332 * msgno -- this is the msgno to be used, blanks if <= 0
3334 * Returns a pointer to buf.
3337 simple_index_line(char *buf
, size_t buflen
, ICE_S
*ice
, long int msgno
)
3340 IFIELD_S
*ifield
, *previfield
= NULL
;
3344 alpine_panic("NULL buf in simple_index_line()");
3353 for(ifield
= ice
->ifield
; ifield
&& p
-buf
< buflen
; ifield
= ifield
->next
){
3355 /* space between fields */
3356 if(ifield
!= ice
->ifield
&& !(previfield
&& previfield
->ctype
== iText
))
3359 /* message number string is generated on the fly */
3360 if(ifield
->ctype
== iMessNo
){
3361 ielem
= ifield
->ielem
;
3362 if(ielem
&& ielem
->datalen
>= ifield
->width
){
3364 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.ld", ifield
->width
, msgno
);
3366 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.*s", ifield
->width
, ifield
->width
, "");
3370 for(ielem
= ifield
->ielem
;
3371 ielem
&& ielem
->print_format
&& p
-buf
< buflen
;
3372 ielem
= ielem
->next
){
3377 bytes_added
= utf8_pad_to_width(p
, src
,
3378 buflen
-(p
-buf
) * sizeof(char),
3379 ielem
->wid
, ifield
->leftadj
);
3383 previfield
= ifield
;
3390 buf
[buflen
-1] = '\0';
3397 * Look in current mail_stream for matches for messages in the searchset
3398 * which match a color rule pattern. Return the color.
3399 * The searched bit will be set for all of the messages which match the
3400 * first pattern which has a match.
3402 * Args stream -- the mail stream
3403 * searchset -- restrict attention to this set of messages
3404 * pstate -- The pattern state. On the first call it will be Null.
3405 * Null means start over with a new first_pattern.
3406 * After that it will be pointing to our local PAT_STATE
3407 * so that next_pattern goes to the next one after the
3408 * ones we've already checked.
3410 * Returns 0 if no match, 1 if a match.
3411 * The color that goes with the matched rule in returned_color.
3412 * It may be NULL, which indicates default.
3415 get_index_line_color(MAILSTREAM
*stream
, SEARCHSET
*searchset
,
3416 PAT_STATE
**pstate
, COLOR_PAIR
**returned_color
)
3419 long rflags
= ROLE_INCOL
;
3420 COLOR_PAIR
*color
= NULL
;
3422 static PAT_STATE localpstate
;
3424 dprint((7, "get_index_line_color\n"));
3427 *returned_color
= NULL
;
3430 pat
= next_pattern(*pstate
);
3432 *pstate
= &localpstate
;
3433 if(!nonempty_patterns(rflags
, *pstate
))
3437 pat
= first_pattern(*pstate
);
3442 /* Go through the possible roles one at a time until we get a match. */
3443 while(!match
&& pat
){
3444 if(match_pattern(pat
->patgrp
, stream
, searchset
, NULL
,
3445 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
3446 if(!pat
->action
|| pat
->action
->bogus
)
3450 if(pat
->action
&& pat
->action
->incol
)
3451 color
= new_color_pair(pat
->action
->incol
->fg
,
3452 pat
->action
->incol
->bg
);
3455 pat
= next_pattern(*pstate
);
3459 if(match
&& returned_color
)
3460 *returned_color
= color
;
3470 index_in_overview(MAILSTREAM
*stream
)
3472 INDEX_COL_S
*cdesc
= NULL
;
3474 if(!(stream
->mailbox
&& IS_REMOTE(stream
->mailbox
)))
3475 return(FALSE
); /* no point! */
3477 if(stream
->dtb
&& stream
->dtb
->name
&& !strcmp(stream
->dtb
->name
, "nntp")){
3482 for(cdesc
= ps_global
->index_disp_format
;
3483 cdesc
->ctype
!= iNothing
;
3485 switch(cdesc
->ctype
){
3486 case iTo
: /* can't be satisfied by XOVER */
3487 case iSender
: /* ... or specifically handled */
3488 case iDescripSize
: /* ... in news case */
3503 * fetch_from - called to get a the index entry's "From:" field
3506 resent_to_us(INDEXDATA_S
*idata
)
3508 if(!idata
->valid_resent_to
){
3509 static char *fields
[] = {"Resent-To", NULL
};
3512 if(idata
->no_fetch
){
3513 idata
->bogus
= 1; /* don't do this */
3517 if((h
= pine_fetchheader_lines(idata
->stream
,idata
->rawno
,NULL
,fields
)) != NULL
){
3518 idata
->resent_to_us
= parsed_resent_to_us(h
);
3519 fs_give((void **) &h
);
3522 idata
->valid_resent_to
= 1;
3525 return(idata
->resent_to_us
);
3530 parsed_resent_to_us(char *h
)
3533 ADDRESS
*addr
= NULL
;
3536 if((p
= strindex(h
, ':')) != NULL
){
3537 for(q
= ++p
; (q
= strpbrk(q
, "\015\012")) != NULL
; q
++)
3538 *q
= ' '; /* quash junk */
3540 rfc822_parse_adrlist(&addr
, p
, ps_global
->maildomain
);
3542 rv
= address_is_us(addr
, ps_global
);
3543 mail_free_address(&addr
);
3553 * fetch_from - called to get a the index entry's "From:" field
3556 fetch_from(INDEXDATA_S
*idata
)
3558 if(idata
->no_fetch
) /* implies from is valid */
3559 return(idata
->from
);
3560 else if(idata
->bogus
)
3565 /* c-client call's just cache access at this point */
3566 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3577 * fetch_to - called to get a the index entry's "To:" field
3580 fetch_to(INDEXDATA_S
*idata
)
3582 if(idata
->no_fetch
){ /* check for specific validity */
3586 idata
->bogus
= 1; /* can't give 'em what they want */
3588 else if(idata
->bogus
){
3589 idata
->bogus
= 2; /* elevate bogosity */
3594 /* c-client call's just cache access at this point */
3595 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3606 * fetch_cc - called to get a the index entry's "Cc:" field
3609 fetch_cc(INDEXDATA_S
*idata
)
3611 if(idata
->no_fetch
){ /* check for specific validity */
3615 idata
->bogus
= 1; /* can't give 'em what they want */
3617 else if(idata
->bogus
){
3618 idata
->bogus
= 2; /* elevate bogosity */
3623 /* c-client call's just cache access at this point */
3624 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3636 * fetch_sender - called to get a the index entry's "Sender:" field
3639 fetch_sender(INDEXDATA_S
*idata
)
3641 if(idata
->no_fetch
){ /* check for specific validity */
3642 if(idata
->valid_sender
)
3643 return(idata
->sender
);
3645 idata
->bogus
= 1; /* can't give 'em what they want */
3647 else if(idata
->bogus
){
3648 idata
->bogus
= 2; /* elevate bogosity */
3653 /* c-client call's just cache access at this point */
3654 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3655 return(env
->sender
);
3665 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3668 fetch_newsgroups(INDEXDATA_S
*idata
)
3670 if(idata
->no_fetch
){ /* check for specific validity */
3671 if(idata
->valid_news
)
3672 return(idata
->newsgroups
);
3674 idata
->bogus
= 1; /* can't give 'em what they want */
3676 else if(idata
->bogus
){
3677 idata
->bogus
= 2; /* elevate bogosity */
3682 /* c-client call's just cache access at this point */
3683 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3684 return(env
->newsgroups
);
3694 * fetch_subject - called to get at the index entry's "Subject:" field
3697 fetch_subject(INDEXDATA_S
*idata
)
3699 if(idata
->no_fetch
) /* implies subject is valid */
3700 return(idata
->subject
);
3701 else if(idata
->bogus
)
3706 /* c-client call's just cache access at this point */
3707 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3708 return(env
->subject
);
3718 * Return an allocated copy of the first few characters from the body
3719 * of the message for possible use in the index screen.
3721 * Maybe we could figure out some way to do aggregate calls to get
3722 * this info for all the lines in view instead of all the one at a
3723 * time calls we're doing now.
3726 fetch_firsttext(INDEXDATA_S
*idata
, int delete_quotes
)
3730 char *firsttext
= NULL
;
3733 long partial_fetch_len
= 0L;
3734 SEARCHSET
*ss
, **sset
;
3739 * Prevent wild prefetch, just get the one we're after.
3740 * Can we get this somehow in the overview call in build_header_work?
3742 ss
= mail_newsearchset();
3743 ss
->first
= idata
->rawno
;
3744 sset
= (SEARCHSET
**) mail_parameters(idata
->stream
,
3746 (void *) idata
->stream
);
3750 if((env
= pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
)) != NULL
){
3752 char *subtype
= NULL
;
3755 if((body
->type
== TYPETEXT
3756 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3758 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3759 && body
->nested
.part
->body
.type
== TYPETEXT
3760 && (subtype
=body
->nested
.part
->body
.subtype
)
3761 && ALLOWED_SUBTYPE(subtype
))
3763 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3764 && body
->nested
.part
->body
.type
== TYPEMULTIPART
3765 && body
->nested
.part
->body
.nested
.part
3766 && body
->nested
.part
->body
.nested
.part
->body
.type
== TYPETEXT
3767 && (subtype
=body
->nested
.part
->body
.nested
.part
->body
.subtype
)
3768 && ALLOWED_SUBTYPE(subtype
))){
3770 if((so
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
3774 int one_space_done
= 0;
3776 if(partial_fetch_len
== 0L){
3777 if(subtype
&& !strucmp(subtype
, "html"))
3778 partial_fetch_len
= 1024L;
3779 else if(subtype
&& !strucmp(subtype
, "plain"))
3780 partial_fetch_len
= delete_quotes
? 128L : 64L;
3782 partial_fetch_len
= 256L;
3785 if((body
->type
== TYPETEXT
3786 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3788 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3789 && body
->nested
.part
->body
.type
== TYPETEXT
3790 && (subtype
=body
->nested
.part
->body
.subtype
)
3791 && ALLOWED_SUBTYPE(subtype
)))
3796 gf_set_so_writec(&pc
, so
);
3797 success
= get_body_part_text(idata
->stream
, body
, idata
->rawno
,
3798 partno
, partial_fetch_len
, pc
,
3800 GBPT_NOINTR
| GBPT_PEEK
|
3801 (delete_quotes
? GBPT_DELQUOTES
: 0));
3802 gf_clear_so_writec(so
);
3807 while(p
-buf
< sizeof(buf
)-1 && so_readc(&c
, so
)){
3808 /* delete leading whitespace */
3809 if(p
== buf
&& isspace(c
))
3811 /* and include just one space per run of whitespace */
3812 else if(isspace(c
)){
3813 if(!one_space_done
){
3831 firsttext
= fs_get((l
+1) * sizeof(char));
3832 firsttext
[0] = '\0';
3833 iutf8ncpy(firsttext
, buf
, l
);
3834 firsttext
[l
] = '\0';
3835 removing_trailing_white_space(firsttext
);
3841 /* first if means we didn't fetch all of the data */
3842 if(!(success
> 1 && success
< partial_fetch_len
)){
3843 if(partial_fetch_len
< 4096L
3844 && (!firsttext
|| utf8_width(firsttext
) < 50)){
3846 fs_give((void **) &firsttext
);
3848 partial_fetch_len
= 4096L;
3858 mail_free_searchset(&ss
);
3865 * fetch_date - called to get at the index entry's "Date:" field
3868 fetch_date(INDEXDATA_S
*idata
)
3870 if(idata
->no_fetch
) /* implies date is valid */
3871 return(idata
->date
);
3872 else if(idata
->bogus
)
3877 /* c-client call's just cache access at this point */
3878 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3879 return((char *) env
->date
);
3889 * fetch_header - called to get at the index entry's "Hdrname:" field
3892 fetch_header(INDEXDATA_S
*idata
, char *hdrname
)
3896 else if(idata
->bogus
)
3899 char *h
, *p
, *q
, *decoded
, *fields
[2];
3900 size_t retsize
, decsize
;
3902 unsigned char *decode_buf
= NULL
;
3904 fields
[0] = hdrname
;
3906 if(hdrname
&& hdrname
[0]
3907 && (h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
3910 if(strlen(h
) < strlen(hdrname
) + 1){
3911 fs_give((void **) &h
);
3915 /* skip "hdrname:" */
3916 for(p
= h
+ strlen(hdrname
) + 1;
3917 *p
&& isspace((unsigned char)*p
); p
++)
3920 decsize
= (4 * strlen(p
)) + 1;
3921 decode_buf
= (unsigned char *) fs_get(decsize
* sizeof(unsigned char));
3922 decoded
= (char *) rfc1522_decode_to_utf8(decode_buf
, decsize
, p
);
3925 retsize
= strlen(decoded
);
3926 q
= ret
= (char *) fs_get((retsize
+1) * sizeof(char));
3929 while(q
-ret
< retsize
&& *p
){
3930 if(*p
== '\015' || *p
== '\012')
3932 else if(*p
== '\t'){
3942 fs_give((void **) &h
);
3944 fs_give((void **) &decode_buf
);
3957 * fetch_size - called to get at the index entry's "size" field
3960 fetch_size(INDEXDATA_S
*idata
)
3962 if(idata
->no_fetch
) /* implies size is valid */
3963 return(idata
->size
);
3964 else if(idata
->bogus
)
3969 if(idata
->stream
&& idata
->rawno
> 0L
3970 && idata
->rawno
<= idata
->stream
->nmsgs
3971 && (mc
= mail_elt(idata
->stream
, idata
->rawno
)))
3972 return(mc
->rfc822_size
);
3982 * fetch_body - called to get a the index entry's body structure
3985 fetch_body(INDEXDATA_S
*idata
)
3989 if(idata
->bogus
|| idata
->no_fetch
){
3994 if(pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
))
4003 * s is at least size width+1
4006 set_index_addr(INDEXDATA_S
*idata
,
4008 struct mail_address
*addr
,
4014 char *p
, *stmp
= NULL
, *sptr
;
4015 char *save_personal
= NULL
;
4020 for(atmp
= addr
; idata
->stream
&& atmp
; atmp
= atmp
->next
)
4021 if(atmp
->host
&& atmp
->host
[0] == '.'){
4022 char *pref
, *h
, *fields
[2];
4024 if(idata
->no_fetch
){
4031 if((h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
4032 NULL
, fields
)) != NULL
){
4033 if(strlen(h
) < strlen(field
) + 1){
4038 for(p
= h
+ strlen(field
) + 1;
4039 *p
&& isspace((unsigned char)*p
); p
++)
4044 sptr
= stmp
= (char *) fs_get((orig_width
+1) * sizeof(char));
4047 for(pref
= prefix
; pref
&& *pref
; pref
++)
4056 if(*p
== '\015' || *p
== '\012')
4057 p
++; /* skip CR LF */
4060 else if(*p
== '\t'){
4067 *sptr
= '\0'; /* tie off return string */
4070 iutf8ncpy(s
, stmp
, orig_width
+1);
4071 s
[orig_width
] = '\0';
4072 fs_give((void **) &stmp
);
4075 fs_give((void **) &h
);
4078 /* else fall thru and display what c-client gave us */
4081 if(addr
&& !addr
->next
/* only one address */
4082 && addr
->host
/* not group syntax */
4083 && addr
->personal
&& addr
->personal
[0]){ /* there is a personal name */
4084 char buftmp
[MAILTMPLEN
];
4087 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4088 strncpy(s
, prefix
, width
+1);
4090 snprintf(buftmp
, sizeof(buftmp
), "%s", addr
->personal
);
4091 p
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
4092 SIZEOF_20KBUF
, buftmp
);
4093 removing_leading_and_trailing_white_space(p
);
4095 iutf8ncpy(s
+ l
, p
, width
- l
);
4102 save_personal
= addr
->personal
;
4103 addr
->personal
= NULL
;
4111 a_string
= addr_list_string(addr
, NULL
, 0);
4113 addr
->personal
= save_personal
;
4115 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4116 strncpy(s
, prefix
, width
+1);
4118 iutf8ncpy(s
+ l
, a_string
, width
- l
);
4121 fs_give((void **)&a_string
);
4127 addr
->personal
= save_personal
;
4134 index_data_env(INDEXDATA_S
*idata
, ENVELOPE
*env
)
4141 idata
->from
= env
->from
;
4142 idata
->to
= env
->to
;
4143 idata
->cc
= env
->cc
;
4144 idata
->sender
= env
->sender
;
4145 idata
->subject
= env
->subject
;
4146 idata
->date
= (char *) env
->date
;
4147 idata
->newsgroups
= env
->newsgroups
;
4149 idata
->valid_to
= 1; /* signal that everythings here */
4150 idata
->valid_cc
= 1;
4151 idata
->valid_sender
= 1;
4152 idata
->valid_news
= 1;
4157 * Put a string representing the date into str. The source date is
4158 * in the string datesrc. The format to be used is in type.
4159 * Notice that type is an IndexColType, but really only a subset of
4160 * IndexColType types are allowed.
4162 * Args datesrc -- The source date string
4163 * type -- What type of output we want
4164 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4165 * str -- Put the answer here.
4166 * str_len -- Length of str
4167 * monabb_width -- This is a hack to get dates to line up right. For
4168 * example, in French (but without accents here)
4172 * For this monabb_width would be 5.
4175 date_str(char *datesrc
, IndexColType type
, int v
, char *str
, size_t str_len
,
4178 char year4
[5], /* 4 digit year */
4179 yearzero
[3], /* zero padded, 2-digit year */
4180 monzero
[3], /* zero padded, 2-digit month */
4181 mon
[3], /* 1 or 2-digit month, no pad */
4182 dayzero
[3], /* zero padded, 2-digit day */
4183 day
[3], /* 1 or 2-digit day, no pad */
4184 dayord
[3], /* 2-letter ordinal label */
4185 monabb
[10], /* 3-letter month abbrev */
4186 /* actually maybe not 3 if localized */
4187 hour24
[3], /* 2-digit, 24 hour clock hour */
4188 hour12
[3], /* 12 hour clock hour, no pad */
4189 minzero
[3], /* zero padded, 2-digit minutes */
4190 timezone
[6]; /* timezone, like -0800 or +... */
4192 int curtype
, lastmonthtype
, lastyeartype
, preftype
;
4193 int sdatetimetype
, sdatetime24type
;
4195 #define TODAYSTR N_("Today")
4197 curtype
= (type
== iCurDate
||
4198 type
== iCurDateIso
||
4199 type
== iCurDateIsoS
||
4200 type
== iCurPrefDate
||
4201 type
== iCurPrefDateTime
||
4202 type
== iCurPrefTime
||
4203 type
== iCurTime24
||
4204 type
== iCurTime12
||
4206 type
== iCurDay2Digit
||
4207 type
== iCurDayOfWeek
||
4208 type
== iCurDayOfWeekAbb
||
4210 type
== iCurMon2Digit
||
4211 type
== iCurMonLong
||
4212 type
== iCurMonAbb
||
4214 type
== iCurYear2Digit
);
4215 lastmonthtype
= (type
== iLstMon
||
4216 type
== iLstMon2Digit
||
4217 type
== iLstMonLong
||
4218 type
== iLstMonAbb
||
4219 type
== iLstMonYear
||
4220 type
== iLstMonYear2Digit
);
4221 lastyeartype
= (type
== iLstYear
||
4222 type
== iLstYear2Digit
);
4223 sdatetimetype
= (type
== iSDateTime
||
4224 type
== iSDateTimeIso
||
4225 type
== iSDateTimeIsoS
||
4226 type
== iSDateTimeS1
||
4227 type
== iSDateTimeS2
||
4228 type
== iSDateTimeS3
||
4229 type
== iSDateTimeS4
||
4230 type
== iSDateTime24
||
4231 type
== iSDateTimeIso24
||
4232 type
== iSDateTimeIsoS24
||
4233 type
== iSDateTimeS124
||
4234 type
== iSDateTimeS224
||
4235 type
== iSDateTimeS324
||
4236 type
== iSDateTimeS424
);
4237 sdatetime24type
= (type
== iSDateTime24
||
4238 type
== iSDateTimeIso24
||
4239 type
== iSDateTimeIsoS24
||
4240 type
== iSDateTimeS124
||
4241 type
== iSDateTimeS224
||
4242 type
== iSDateTimeS324
||
4243 type
== iSDateTimeS424
);
4244 preftype
= (type
== iPrefDate
||
4245 type
== iPrefDateTime
||
4246 type
== iPrefTime
||
4247 type
== iCurPrefDate
||
4248 type
== iCurPrefDateTime
||
4249 type
== iCurPrefTime
);
4253 if(!(datesrc
&& datesrc
[0]) && !(curtype
|| lastmonthtype
|| lastyeartype
))
4256 if(curtype
|| lastmonthtype
|| lastyeartype
){
4260 parse_date(dbuf
, &d
);
4264 else if(lastmonthtype
){
4273 parse_date(F_ON(F_DATES_TO_LOCAL
,ps_global
)
4274 ? convert_date_to_local(datesrc
) : datesrc
, &d
);
4275 if(d
.year
== -1 || d
.month
== -1 || d
.day
== -1){
4277 sdatetime24type
= 0;
4280 case iSDate
: case iSDateTime
: case iSDateTime24
:
4284 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4285 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
4289 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4293 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4297 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4301 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4305 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4315 /* some special ones to start with */
4317 struct tm tm
, *tmptr
= NULL
;
4321 * Make sure we get the right one if we're using current time.
4324 now
= time((time_t *) 0);
4325 if(now
!= (time_t) -1)
4326 tmptr
= localtime(&now
);
4330 memset(&tm
, 0, sizeof(tm
));
4331 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4332 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4333 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4334 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4335 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4336 tm
.tm_wday
= MIN(MAX(d
.wkday
, 0), 6);
4343 our_strftime(str
, str_len
, "%x", tmptr
);
4347 our_strftime(str
, str_len
, "%X", tmptr
);
4350 case iCurPrefDateTime
:
4351 our_strftime(str
, str_len
, "%c", tmptr
);
4361 strncpy(monabb
, (d
.month
> 0 && d
.month
< 13)
4362 ? month_abbrev_locale(d
.month
) : "", sizeof(monabb
));
4363 monabb
[sizeof(monabb
)-1] = '\0';
4365 strncpy(mon
, (d
.month
> 0 && d
.month
< 13)
4366 ? int2string(d
.month
) : "", sizeof(mon
));
4367 mon
[sizeof(mon
)-1] = '\0';
4369 strncpy(day
, (d
.day
> 0 && d
.day
< 32)
4370 ? int2string(d
.day
) : "", sizeof(day
));
4371 day
[sizeof(day
)-1] = '\0';
4374 (d
.day
<= 0 || d
.day
> 31) ? "" :
4375 (d
.day
== 1 || d
.day
== 21 || d
.day
== 31) ? "st" :
4376 (d
.day
== 2 || d
.day
== 22 ) ? "nd" :
4377 (d
.day
== 3 || d
.day
== 23 ) ? "rd" : "th", sizeof(dayord
));
4379 dayord
[sizeof(dayord
)-1] = '\0';
4381 strncpy(year4
, (d
.year
>= 1000 && d
.year
< 10000)
4382 ? int2string(d
.year
) : "????", sizeof(year4
));
4383 year4
[sizeof(year4
)-1] = '\0';
4386 if((d
.year
% 100) < 10){
4388 strncpy(yearzero
+1, int2string(d
.year
% 100), sizeof(yearzero
)-1);
4391 strncpy(yearzero
, int2string(d
.year
% 100), sizeof(yearzero
));
4394 strncpy(yearzero
, "??", sizeof(yearzero
));
4396 yearzero
[sizeof(yearzero
)-1] = '\0';
4398 if(d
.month
> 0 && d
.month
< 10){
4400 strncpy(monzero
+1, int2string(d
.month
), sizeof(monzero
)-1);
4402 else if(d
.month
>= 10 && d
.month
<= 12)
4403 strncpy(monzero
, int2string(d
.month
), sizeof(monzero
));
4405 strncpy(monzero
, "??", sizeof(monzero
));
4407 monzero
[sizeof(monzero
)-1] = '\0';
4409 if(d
.day
> 0 && d
.day
< 10){
4411 strncpy(dayzero
+1, int2string(d
.day
), sizeof(dayzero
)-1);
4413 else if(d
.day
>= 10 && d
.day
<= 31)
4414 strncpy(dayzero
, int2string(d
.day
), sizeof(dayzero
));
4416 strncpy(dayzero
, "??", sizeof(dayzero
));
4418 dayzero
[sizeof(dayzero
)-1] = '\0';
4420 hr12
= (d
.hour
== 0) ? 12 :
4421 (d
.hour
> 12) ? (d
.hour
- 12) : d
.hour
;
4423 if(hr12
> 0 && hr12
<= 12)
4424 strncpy(hour12
, int2string(hr12
), sizeof(hour12
));
4426 hour12
[sizeof(hour12
)-1] = '\0';
4429 if(d
.hour
>= 0 && d
.hour
< 10){
4431 strncpy(hour24
+1, int2string(d
.hour
), sizeof(hour24
)-1);
4433 else if(d
.hour
>= 10 && d
.hour
< 24)
4434 strncpy(hour24
, int2string(d
.hour
), sizeof(hour24
));
4436 hour24
[sizeof(hour24
)-1] = '\0';
4439 if(d
.minute
>= 0 && d
.minute
< 10){
4441 strncpy(minzero
+1, int2string(d
.minute
), sizeof(minzero
)-1);
4443 else if(d
.minute
>= 10 && d
.minute
<= 60)
4444 strncpy(minzero
, int2string(d
.minute
), sizeof(minzero
));
4446 minzero
[sizeof(minzero
)-1] = '\0';
4448 if(sizeof(timezone
) > 5){
4449 if(d
.hours_off_gmt
<= 0){
4451 d
.hours_off_gmt
*= -1;
4452 d
.min_off_gmt
*= -1;
4458 if(d
.hours_off_gmt
>= 0 && d
.hours_off_gmt
< 10){
4460 strncpy(timezone
+2, int2string(d
.hours_off_gmt
), sizeof(timezone
)-2);
4462 else if(d
.hours_off_gmt
>= 10 && d
.hours_off_gmt
< 24)
4463 strncpy(timezone
+1, int2string(d
.hours_off_gmt
), sizeof(timezone
)-1);
4470 if(d
.min_off_gmt
>= 0 && d
.min_off_gmt
< 10){
4472 strncpy(timezone
+4, int2string(d
.min_off_gmt
), sizeof(timezone
)-4);
4474 else if(d
.min_off_gmt
>= 10 && d
.min_off_gmt
<= 60)
4475 strncpy(timezone
+3, int2string(d
.min_off_gmt
), sizeof(timezone
)-3);
4482 timezone
[sizeof(timezone
)-1] = '\0';
4487 /* this one is not locale-specific */
4488 snprintf(str
, str_len
, "%s%s%s %s %s",
4489 (d
.wkday
!= -1) ? day_abbrev(d
.wkday
) : "",
4490 (d
.wkday
!= -1) ? ", " : "",
4492 (d
.month
> 0 && d
.month
< 13) ? month_abbrev(d
.month
) : "",
4496 case iCurDayOfWeekAbb
:
4497 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_abbrev_locale(d
.wkday
) : "", str_len
);
4498 str
[str_len
-1] = '\0';
4502 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_name_locale(d
.wkday
) : "", str_len
);
4503 str
[str_len
-1] = '\0';
4509 strncpy(str
, year4
, str_len
);
4513 strncpy(str
, dayzero
, str_len
);
4518 strncpy(str
, monzero
, str_len
);
4521 case iCurYear2Digit
:
4522 case iLstYear2Digit
:
4523 case iLstMonYear2Digit
:
4524 strncpy(str
, yearzero
, str_len
);
4527 strncpy(str
, timezone
, str_len
);
4531 strncpy(str
, day
, str_len
);
4534 snprintf(str
, str_len
, "%s%s", day
, dayord
);
4539 if(d
.month
> 0 && d
.month
<= 12)
4540 strncpy(str
, int2string(d
.month
), str_len
);
4546 strncpy(str
, monabb
, str_len
);
4551 strncpy(str
, (d
.month
> 0 && d
.month
< 13)
4552 ? month_name_locale(d
.month
) : "", str_len
);
4557 snprintf(str
, str_len
, "%s%s%s", monabb
, (monabb
[0] && day
[0]) ? " " : "", day
);
4559 if(monabb_width
> 0)
4560 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4561 monabb_width
, monabb_width
, monabb
, day
);
4563 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4569 snprintf(str
, str_len
, "%s%s%s%s%s", monabb
,
4570 (monabb
[0] && day
[0]) ? " " : "", day
,
4571 ((monabb
[0] || day
[0]) && year4
[0]) ? ", " : "",
4574 if(monabb_width
> 0)
4575 utf8_snprintf(str
, str_len
, "%-*.*w %2s%c %4s",
4576 monabb_width
, monabb_width
,
4578 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ', year4
);
4580 snprintf(str
, str_len
, "%s %2s%c %4s", monabb
, day
,
4581 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ',
4594 if(monzero
[0] == '?' && dayzero
[0] == '?' &&
4596 snprintf(str
, str_len
, "%8s", "");
4600 snprintf(str
, str_len
, "%2s/%2s/%2s",
4601 monzero
, dayzero
, yearzero
);
4604 snprintf(str
, str_len
, "%2s/%2s/%2s",
4605 dayzero
, monzero
, yearzero
);
4608 snprintf(str
, str_len
, "%2s.%2s.%2s",
4609 dayzero
, monzero
, yearzero
);
4612 snprintf(str
, str_len
, "%2s.%2s.%2s",
4613 yearzero
, monzero
, dayzero
);
4617 snprintf(str
, str_len
, "%2s-%2s-%2s",
4618 yearzero
, monzero
, dayzero
);
4622 snprintf(str
, str_len
, "%4s-%2s-%2s",
4623 year4
, monzero
, dayzero
);
4633 snprintf(str
, str_len
, "%2s%c%2s",
4634 (hour24
[0] && minzero
[0]) ? hour24
: "",
4635 (hour24
[0] && minzero
[0]) ? ':' : ' ',
4636 (hour24
[0] && minzero
[0]) ? minzero
: "");
4640 snprintf(str
, str_len
, "%s%c%2s%s",
4641 (hour12
[0] && minzero
[0]) ? hour12
: "",
4642 (hour12
[0] && minzero
[0]) ? ':' : ' ',
4643 (hour12
[0] && minzero
[0]) ? minzero
: "",
4644 (hour12
[0] && minzero
[0] && d
.hour
< 12) ? "am" :
4645 (hour12
[0] && minzero
[0] && d
.hour
>= 12) ? "pm" :
4648 case iSDate
: case iSDateIso
: case iSDateIsoS
:
4649 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
4650 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
4651 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
4652 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
4653 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
4654 { struct date now
, last_day
;
4656 int msg_day_of_year
, now_day_of_year
, today
;
4657 int diff
, ydiff
, last_day_of_year
;
4660 parse_date(dbuf
, &now
);
4661 today
= day_of_week(&now
) + 7;
4663 if(today
>= 0+7 && today
<= 6+7){
4664 now_day_of_year
= day_of_year(&now
);
4665 msg_day_of_year
= day_of_year(&d
);
4666 ydiff
= now
.year
- d
.year
;
4668 if(msg_day_of_year
== -1)
4671 diff
= now_day_of_year
- msg_day_of_year
;
4672 else if(ydiff
== 1){
4674 last_day
.month
= 12;
4676 last_day_of_year
= day_of_year(&last_day
);
4678 diff
= now_day_of_year
+
4679 (last_day_of_year
- msg_day_of_year
);
4681 else if(ydiff
== -1){
4683 last_day
.month
= 12;
4685 last_day_of_year
= day_of_year(&last_day
);
4687 diff
= -1 * (msg_day_of_year
+
4688 (last_day_of_year
- now_day_of_year
));
4696 strncpy(str
, _(TODAYSTR
), str_len
);
4698 strncpy(str
, _("Yesterday"), str_len
);
4699 else if(diff
> 1 && diff
< 7)
4700 snprintf(str
, str_len
, "%s", day_name_locale((today
- diff
) % 7));
4702 strncpy(str
, _("Tomorrow"), str_len
);
4703 else if(diff
< -1 && diff
> -7)
4704 snprintf(str
, str_len
, _("Next %.3s!"),
4705 day_name_locale((today
- diff
) % 7));
4708 || (ydiff
== 1 && 12 + now
.month
- d
.month
< 6))){
4710 snprintf(str
, str_len
, "%s%s%s", monabb
,
4711 (monabb
[0] && day
[0]) ? " " : "", day
);
4713 if(monabb_width
> 0)
4714 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4715 monabb_width
, monabb_width
, monabb
, day
);
4717 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4721 if(msg_day_of_year
== -1 && (type
== iSDate
|| type
== iSDateTime
))
4722 type
= iSDateTimeIsoS
;
4725 case iSDate
: case iSDateTime
: case iSDateTime24
:
4729 memset(&tm
, 0, sizeof(tm
));
4730 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4731 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4732 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4733 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4734 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4735 our_strftime(str
, str_len
, "%x", &tm
);
4739 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4741 snprintf(str
, str_len
, "%s/%s/%s%s", mon
, day
, yearzero
,
4742 diff
< 0 ? "!" : "");
4744 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4745 (mon
[0] && mon
[1]) ? "" : " ",
4746 mon
, dayzero
, yearzero
,
4747 diff
< 0 ? "!" : "");
4749 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4751 snprintf(str
, str_len
, "%s/%s/%s%s", day
, mon
, yearzero
,
4752 diff
< 0 ? "!" : "");
4754 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4755 (day
[0] && day
[1]) ? "" : " ",
4756 day
, monzero
, yearzero
,
4757 diff
< 0 ? "!" : "");
4759 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4761 snprintf(str
, str_len
, "%s.%s.%s%s", day
, mon
, yearzero
,
4762 diff
< 0 ? "!" : "");
4764 snprintf(str
, str_len
, "%s%s.%s.%s%s",
4765 (day
[0] && day
[1]) ? "" : " ",
4766 day
, monzero
, yearzero
,
4767 diff
< 0 ? "!" : "");
4769 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4771 snprintf(str
, str_len
, "%s.%s.%s%s",
4772 yearzero
, monzero
, dayzero
,
4773 diff
< 0 ? "!" : "");
4775 snprintf(str
, str_len
, "%s.%s.%s%s",
4776 yearzero
, monzero
, dayzero
,
4777 diff
< 0 ? "!" : "");
4779 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4780 snprintf(str
, str_len
, "%2s-%2s-%2s",
4781 yearzero
, monzero
, dayzero
);
4783 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4784 snprintf(str
, str_len
, "%4s-%2s-%2s",
4785 year4
, monzero
, dayzero
);
4795 snprintf(str
, str_len
, "%s%s%s", monabb
,
4796 (monabb
[0] && day
[0]) ? " " : "", day
);
4798 if(monabb_width
> 0)
4799 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4800 monabb_width
, monabb_width
, monabb
, day
);
4802 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4813 str
[str_len
-1] = '\0';
4815 if(type
== iSTime
||
4816 (sdatetimetype
&& !strcmp(str
, _(TODAYSTR
)))){
4817 struct date now
, last_day
;
4818 char dbuf
[200], *Ddd
, *ampm
;
4823 parse_date(dbuf
, &now
);
4825 /* Figure out if message date lands in the past week */
4827 /* (if message dated this month or last month...) */
4828 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 1) ||
4829 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 1)){
4831 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4834 * If msg in end of last year (and we're in first bit of "this"
4835 * year), diff will be backwards; fix up by adding number of days
4836 * in last year (usually 365, but occasionally 366)...
4838 if(d
.year
== now
.year
- 1){
4840 last_day
.month
= 12;
4843 daydiff
+= day_of_year(&last_day
);
4847 daydiff
= -100; /* comfortably out of range (of past week) */
4849 /* Build 2-digit hour and am/pm indicator, used below */
4851 if(d
.hour
>= 0 && d
.hour
< 24){
4852 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4853 ampm
= (d
.hour
< 12) ? "am" : "pm";
4854 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4857 strncpy(hour12
, "??", sizeof(hour12
));
4858 hour12
[sizeof(hour12
)-1] = '\0';
4860 strncpy(hour24
, "??", sizeof(hour24
));
4861 hour24
[sizeof(hour24
)-1] = '\0';
4864 /* Build date/time in str, in format similar to that used by w(1) */
4866 if(daydiff
== 0){ /* If date is today, "HH:MMap" */
4867 if(d
.minute
>= 0 && d
.minute
< 60)
4868 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4870 strncpy(minzero
, "??", sizeof(minzero
));
4871 minzero
[sizeof(minzero
)-1] = '\0';
4874 snprintf(str
, str_len
, "%s:%s%s", sdatetime24type
? hour24
: hour12
,
4875 minzero
, sdatetime24type
? "" : ampm
);
4877 else if(daydiff
>= 1 && daydiff
< 6){ /* If <1wk ago, "DddHHap" */
4879 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4880 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4881 Ddd
= day_abbrev_locale(day_of_week(&d
));
4885 snprintf(str
, str_len
, "%s%s%s", Ddd
, hour12
, ampm
);
4887 else{ /* date is old or future, "ddMmmyy" */
4888 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4889 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4890 monabb
[sizeof(monabb
)-1] = '\0';
4892 if(d
.day
>= 1 && d
.day
<= 31)
4893 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
4895 strncpy(dayzero
, "??", sizeof(dayzero
));
4896 dayzero
[sizeof(dayzero
)-1] = '\0';
4899 if(d
.year
>= 0 && d
.year
<= 9999)
4900 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
4902 strncpy(yearzero
, "??", sizeof(yearzero
));
4903 yearzero
[sizeof(yearzero
)-1] = '\0';
4906 snprintf(str
, str_len
, "%s%s%s", dayzero
, monabb
, yearzero
);
4909 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4911 memmove(str
, str
+ 1, strlen(str
));
4920 * Format a string representing the keywords into ice.
4922 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
4924 * Args idata -- which message?
4925 * kwtype -- keywords or kw initials
4926 * ice -- index cache entry for message
4929 key_str(INDEXDATA_S
*idata
, SubjKW kwtype
, ICE_S
*ice
)
4934 COLOR_PAIR
*color
= NULL
;
4935 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
4936 IELEM_S
*ielem
= NULL
;
4937 IFIELD_S
*ourifield
= NULL
;
4939 if(ice
&& ice
->ifield
){
4940 /* move to last ifield, the one we're working */
4941 for(ourifield
= ice
->ifield
;
4942 ourifield
&& ourifield
->next
;
4943 ourifield
= ourifield
->next
)
4950 if(kwtype
== KWInit
){
4951 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4952 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4953 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
4954 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
4957 * Pick off the first initial. Since word is UTF-8 it may
4958 * take more than one byte for the first initial.
4961 if(word
&& word
[0]){
4963 unsigned long remaining_octets
;
4964 unsigned char *inputp
;
4966 remaining_octets
= strlen(word
);
4967 inputp
= (unsigned char *) word
;
4968 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
4969 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
4970 ielem
= new_ielem(&ourifield
->ielem
);
4971 ielem
->freedata
= 1;
4972 ielem
->datalen
= (unsigned) (inputp
- (unsigned char *) word
);
4973 ielem
->data
= (char *) fs_get((ielem
->datalen
+ 1) * sizeof(char));
4974 strncpy(ielem
->data
, word
, ielem
->datalen
);
4975 ielem
->data
[ielem
->datalen
] = '\0';
4977 if(pico_usingcolor()
4978 && ((kw
->nick
&& kw
->nick
[0]
4979 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
4980 || (kw
->kw
&& kw
->kw
[0]
4981 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
4982 ielem
->color
= color
;
4989 free_color_pair(&color
);
4993 else if(kwtype
== KW
){
4994 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4995 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4998 ielem
= new_ielem(&ourifield
->ielem
);
4999 ielem
->freedata
= 1;
5000 ielem
->data
= cpystr(" ");
5006 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
5007 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
5010 ielem
= new_ielem(&ourifield
->ielem
);
5011 ielem
->freedata
= 1;
5012 ielem
->data
= cpystr(word
);
5013 ielem
->datalen
= strlen(word
);
5015 if(pico_usingcolor()
5016 && ((kw
->nick
&& kw
->nick
[0]
5017 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5018 || (kw
->kw
&& kw
->kw
[0]
5019 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5020 ielem
->color
= color
;
5026 free_color_pair(&color
);
5032 * If we're coloring some of the fields then add a dummy field
5033 * at the end that can soak up the rest of the space after the last
5034 * colored keyword. Otherwise, the last one's color will extend to
5035 * the end of the field.
5037 if(pico_usingcolor()){
5038 ielem
= new_ielem(&ourifield
->ielem
);
5039 ielem
->freedata
= 1;
5040 ielem
->data
= cpystr(" ");
5044 ourifield
->leftadj
= 1;
5045 set_ielem_widths_in_field(ourifield
);
5050 prio_str(INDEXDATA_S
*idata
, IndexColType ctype
, ICE_S
*ice
)
5052 IFIELD_S
*ourifield
= NULL
;
5053 IELEM_S
*ielem
= NULL
;
5058 if(ice
&& ice
->ifield
){
5059 /* move to last ifield, the one we're working */
5060 for(ourifield
= ice
->ifield
;
5061 ourifield
&& ourifield
->next
;
5062 ourifield
= ourifield
->next
)
5069 hdrval
= fetch_header(idata
, PRIORITYNAME
);
5071 if(hdrval
&& hdrval
[0] && isdigit(hdrval
[0])){
5073 if(v
>= 1 && v
<= 5 && v
!= 3){
5075 ielem
= new_ielem(&ourifield
->ielem
);
5076 ielem
->freedata
= 1;
5080 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5081 ielem
->data
[0] = hdrval
[0];
5082 ielem
->data
[1] = '\0';
5086 for(p
= priorities
; p
&& p
->desc
; p
++)
5091 ielem
->data
= cpystr(p
->desc
);
5096 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5097 ielem
->data
[0] = '\0';
5100 ielem
->data
[0] = '!';
5105 * We could put a Unicode downarrow in here but
5106 * we have no way of knowing if the user's font
5107 * will have it (I think).
5109 ielem
->data
[0] = 'v';
5113 ielem
->data
[1] = '\0';
5118 alpine_panic("Unhandled case in prio_str");
5123 ielem
->data
= cpystr("");
5125 if(ielem
&& ielem
->data
)
5126 ielem
->datalen
= strlen(ielem
->data
);
5128 if((v
== 1 || v
== 2) && pico_usingcolor()
5129 && ps_global
->VAR_IND_HIPRI_FORE_COLOR
5130 && ps_global
->VAR_IND_HIPRI_BACK_COLOR
){
5131 ielem
->type
= eTypeCol
;
5132 ielem
->freecolor
= 1;
5133 ielem
->color
= new_color_pair(ps_global
->VAR_IND_HIPRI_FORE_COLOR
, ps_global
->VAR_IND_HIPRI_BACK_COLOR
);
5135 else if((v
== 4 || v
== 5) && pico_usingcolor()
5136 && ps_global
->VAR_IND_LOPRI_FORE_COLOR
5137 && ps_global
->VAR_IND_LOPRI_BACK_COLOR
){
5138 ielem
->type
= eTypeCol
;
5139 ielem
->freecolor
= 1;
5140 ielem
->color
= new_color_pair(ps_global
->VAR_IND_LOPRI_FORE_COLOR
, ps_global
->VAR_IND_LOPRI_BACK_COLOR
);
5143 ourifield
->leftadj
= 1;
5144 set_ielem_widths_in_field(ourifield
);
5147 fs_give((void **) &hdrval
);
5153 header_str(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
, ICE_S
*ice
)
5155 IFIELD_S
*ourifield
= NULL
;
5156 IELEM_S
*ielem
= NULL
;
5157 char *fieldval
= NULL
;
5159 if(ice
&& ice
->ifield
){
5160 /* move to last ifield, the one we're working */
5161 for(ourifield
= ice
->ifield
;
5162 ourifield
&& ourifield
->next
;
5163 ourifield
= ourifield
->next
)
5170 fieldval
= get_fieldval(idata
, hdrtok
);
5173 ielem
= new_ielem(&ourifield
->ielem
);
5174 ielem
->freedata
= 1;
5175 ielem
->data
= fieldval
;
5176 ielem
->datalen
= strlen(fieldval
);
5178 ourifield
->leftadj
= (hdrtok
->adjustment
== Left
) ? 1 : 0;
5181 set_ielem_widths_in_field(ourifield
);
5186 get_fieldval(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
)
5189 char *hdrval
= NULL
, *testval
;
5190 char *fieldval
= NULL
, *firstval
;
5191 char *retfieldval
= NULL
;
5194 return(retfieldval
);
5196 if(hdrtok
&& hdrtok
->hdrname
&& hdrtok
->hdrname
[0])
5197 hdrval
= fetch_header(idata
, hdrtok
? hdrtok
->hdrname
: "");
5199 /* find start of fieldnum'th field */
5201 for(fieldnum
= MAX(hdrtok
->fieldnum
-1, 0);
5202 fieldnum
> 0 && fieldval
&& *fieldval
; fieldnum
--){
5205 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5206 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5207 if(testval
&& (!firstval
|| testval
< firstval
))
5211 fieldval
= firstval
;
5212 if(fieldval
&& *fieldval
)
5216 /* tie off end of field */
5217 if(fieldval
&& *fieldval
&& hdrtok
->fieldnum
> 0){
5219 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5220 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5221 if(testval
&& (!firstval
|| testval
< firstval
))
5232 retfieldval
= cpystr(fieldval
);
5235 fs_give((void **) &hdrval
);
5237 return(retfieldval
);
5242 scorevalfrommsg(MAILSTREAM
*stream
, MsgNo rawno
, HEADER_TOK_S
*hdrtok
, int no_fetch
)
5246 char *fieldval
= NULL
;
5249 memset(&idata
, 0, sizeof(INDEXDATA_S
));
5250 idata
.stream
= stream
;
5251 idata
.no_fetch
= no_fetch
;
5252 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), rawno
);
5253 idata
.rawno
= rawno
;
5254 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
5255 && (mc
= mail_elt(stream
, idata
.rawno
))){
5256 idata
.size
= mc
->rfc822_size
;
5257 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
5262 fieldval
= get_fieldval(&idata
, hdrtok
);
5265 retval
= atol(fieldval
);
5266 fs_give((void **) &fieldval
);
5273 * Put a string representing the subject into str. Idata tells us which
5274 * message we are referring to.
5276 * This means we should ensure that all data ends up being UTF-8 data.
5277 * That covers the data in ice ielems and str.
5279 * Args idata -- which message?
5280 * str -- destination buffer
5281 * strsize -- size of str buffer
5282 * kwtype -- prepend keywords or kw initials before the subject
5283 * opening -- add first text from body of message if there's room
5284 * shorten -- if on, shorten the subject.
5285 * ice -- index cache entry for message
5288 subj_str(INDEXDATA_S
*idata
, char *str
, size_t strsize
, SubjKW kwtype
, int opening
, int shorten
, ICE_S
*ice
)
5290 char *subject
, *origsubj
, *origstr
, *rawsubj
, *sptr
= NULL
;
5291 char *p
, *border
, *q
= NULL
, *free_subj
= NULL
;
5295 int depth
= 0, mult
= 2;
5297 int do_subj
= 0, truncated_tree
= 0;
5298 PINETHRD_S
*thd
, *thdorig
;
5299 IELEM_S
*ielem
= NULL
, *subjielem
= NULL
;
5300 IFIELD_S
*ourifield
= NULL
;
5306 * If we need the data at the start of the message and we're in
5307 * a c-client callback, defer the data lookup until later.
5309 if(opening
&& idata
->no_fetch
){
5314 if(ice
&& ice
->ifield
){
5315 /* move to last ifield, the one we're working on */
5316 for(ourifield
= ice
->ifield
;
5317 ourifield
&& ourifield
->next
;
5318 ourifield
= ourifield
->next
)
5322 str
[0] = str
[strsize
-1] = '\0';
5324 rawsubj
= fetch_subject(idata
);
5329 * Before we do anything else, decode the character set in the subject and
5330 * work with the result.
5332 sp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5333 SIZEOF_20KBUF
, rawsubj
);
5336 len
+= 100; /* for possible charset, escaped characters */
5337 origsubj
= fs_get((len
+1) * sizeof(unsigned char));
5340 iutf8ncpy(origsubj
, sp
, len
);
5342 origsubj
[len
] = '\0';
5343 replace_tabs_by_space(origsubj
);
5344 removing_trailing_white_space(origsubj
);
5347 * origsubj is the original subject but it has been decoded. We need
5348 * to free it at the end of this routine.
5352 shorten_subject(origsubj
);
5355 * prepend_keyword will put the keyword stuff before the subject
5356 * and split the subject up into its colored parts in subjielem.
5357 * Subjielem is a local ielem which will have to be fit into the
5358 * real ifield->ielem later. The print_format strings in subjielem will
5359 * not be filled in by prepend_keyword because of the fact that we
5360 * may have to adjust things for threading below.
5361 * We use subjielem in case we want to insert some threading information
5362 * at the front of the subject.
5364 if(kwtype
== KW
|| kwtype
== KWInit
){
5365 subject
= prepend_keyword_subject(idata
->stream
, idata
->rawno
,
5367 ourifield
? &subjielem
: NULL
,
5368 ps_global
->VAR_KW_BRACES
);
5369 free_subj
= subject
;
5374 subjielem
= new_ielem(&subjielem
);
5375 subjielem
->type
= eTypeCol
;
5376 subjielem
->freedata
= 1;
5377 subjielem
->data
= cpystr(subject
);
5378 subjielem
->datalen
= strlen(subject
);
5379 if(pico_usingcolor()
5380 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5381 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5382 subjielem
->freecolor
= 1;
5383 subjielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5389 * This space is here so that if the subject does
5390 * not extend all the way to the end of the field then
5391 * we'll switch the color back and paint the rest of the
5392 * field in the Normal color or the index line color.
5395 ielem
= new_ielem(&subjielem
);
5396 ielem
->freedata
= 1;
5397 ielem
->data
= cpystr(" ");
5405 && (ps_global
->thread_disp_style
== THREAD_STRUCT
5406 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
5407 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
5408 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ2
)){
5411 * Why do we want to truncate the subject and from strs?
5412 * It's so we can put the [5] thread count things in below when
5413 * we are threading and the thread structure runs off the right
5414 * hand edge of the screen. This routine doesn't know that it
5415 * is running off the edge unless it knows the actual width
5416 * that we have to draw in.
5418 if(pith_opt_truncate_sfstr
5419 && (*pith_opt_truncate_sfstr
)()
5421 && ourifield
->width
> 0)
5422 width
= ourifield
->width
;
5427 width
= MIN(width
, strsize
-1);
5430 * We're counting on the fact that this initial part of the
5431 * string is ascii and we have one octet per character and
5432 * characters are width 1 on the screen.
5434 border
= str
+ width
;
5436 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
5438 if(pith_opt_condense_thread_cue
)
5439 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
5441 && get_lflag(idata
->stream
,
5446 * width is < available strsize and
5447 * border points to something less than or equal
5448 * to the end of the buffer.
5454 while(thd
->parent
&&
5455 (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
5459 if(ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
)
5462 sptr
+= (mult
*depth
);
5463 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
5464 thd
&& thd
->parent
&& p
>= str
;
5465 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
5466 if(p
+ mult
>= border
&& !q
){
5467 if(width
>= 4 && depth
< 100){
5468 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
5471 else if(width
>= 5 && depth
< 1000){
5472 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
5476 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
5489 if(ps_global
->thread_disp_style
== THREAD_STRUCT
5490 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5493 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5496 if(thd
== thdorig
&& !thd
->branch
)
5497 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
5498 else if(thd
== thdorig
|| thd
->branch
)
5501 if(p
+ 1 < border
&& thd
== thdorig
)
5508 if(sptr
&& !truncated_tree
){
5510 * Look to see if the subject is the same as the previous
5511 * message in the thread, if any. If it is the same, don't
5512 * reprint the subject.
5514 * Note that when we're prepending keywords to the subject,
5515 * and the user changes a keyword, we do invalidate
5516 * the index cache for that message but we don't go to the
5517 * trouble of invalidating the index cache for the the child
5518 * of that node in the thread, so the MUTT subject line
5519 * display for the child may be wrong. That is, it may show
5520 * it is the same as this subject even though it no longer
5521 * is, or vice versa.
5523 if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5527 if(thdorig
->parent
&&
5528 (thd
= fetch_thread(idata
->stream
, thdorig
->parent
))
5530 char *this_orig
= NULL
,
5532 *free_prev_orig
= NULL
,
5533 *this_prep
= NULL
, /* includes prepend */
5537 SORTCACHE
*sc
= NULL
;
5539 /* get the stripped subject of previous message */
5540 mc
= (mailcache_t
) mail_parameters(NIL
, GET_CACHE
, NIL
);
5542 sc
= (*mc
)(idata
->stream
, thd
->rawno
, CH_SORTCACHE
);
5544 if(sc
&& sc
->subject
)
5545 prev_orig
= sc
->subject
;
5549 env
= pine_mail_fetchenvelope(idata
->stream
,
5551 stripthis
= (env
&& env
->subject
)
5552 ? env
->subject
: "";
5554 mail_strip_subject(stripthis
, &prev_orig
);
5556 free_prev_orig
= prev_orig
;
5559 mail_strip_subject(rawsubj
, &this_orig
);
5561 if(kwtype
== KW
|| kwtype
== KWInit
){
5562 prev_prep
= prepend_keyword_subject(idata
->stream
,
5566 ps_global
->VAR_KW_BRACES
);
5568 this_prep
= prepend_keyword_subject(idata
->stream
,
5572 ps_global
->VAR_KW_BRACES
);
5574 if((this_prep
|| prev_prep
)
5575 && ((this_prep
&& !prev_prep
)
5576 || (prev_prep
&& !this_prep
)
5577 || strucmp(this_prep
, prev_prep
)))
5581 if((this_orig
|| prev_orig
)
5582 && ((this_orig
&& !prev_orig
)
5583 || (prev_orig
&& !this_orig
)
5584 || strucmp(this_orig
, prev_orig
)))
5589 * If some of the thread is zoomed out of view, we
5590 * want to display the subject of the first one that
5591 * is in view. If any of the parents or grandparents
5592 * etc of this message are visible, then we don't
5593 * need to worry about it. If all of the parents have
5594 * been zoomed away, then this is the first one.
5596 * When you're looking at a particular case where
5597 * some of the messages of a thread are selected it
5598 * seems like we should look at not only our
5599 * direct parents, but the siblings of the parent
5600 * too. But that's not really correct, because those
5601 * siblings are basically the starts of different
5602 * branches, separate from our branch. They could
5603 * have their own subjects, for example. This will
5604 * give us cases where it looks like we are showing
5605 * the subject too much, but it will be correct!
5607 * In zoom_index() we clear_index_cache_ent for
5608 * some lines which have subjects which might become
5609 * visible when we zoom, and also in set_lflags
5610 * where we might change subjects by unselecting
5611 * something when zoomed.
5615 if(!msgline_hidden(idata
->stream
,
5616 sp_msgmap(idata
->stream
),
5617 mn_raw2m(sp_msgmap(idata
->stream
),
5620 break; /* found a visible parent */
5623 if(thd
&& thd
->parent
)
5624 thd
= fetch_thread(idata
->stream
,thd
->parent
);
5629 if(!thd
) /* none were visible */
5634 fs_give((void **) &this_orig
);
5637 fs_give((void **) &this_prep
);
5640 fs_give((void **) &free_prev_orig
);
5643 fs_give((void **) &prev_prep
);
5654 * We don't need to worry about truncating to width
5655 * here. If we go over the right hand edge it will be
5658 strsize
-= (sptr
- str
);
5660 strncpy(sptr
, subject
, strsize
-1);
5661 sptr
[strsize
-1] = '\0';
5663 else if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5664 strsize
-= (sptr
- str
);
5672 * We decided we don't need the subject so we'd better
5673 * eliminate subjielem.
5675 free_ielem(&subjielem
);
5679 free_ielem(&subjielem
); /* no room for actual subject */
5681 if(ourifield
&& sptr
&& sptr
> origstr
){
5682 ielem
= new_ielem(&ourifield
->ielem
);
5683 ielem
->type
= eThreadInfo
;
5684 ielem
->freedata
= 1;
5687 ielem
->data
= cpystr(origstr
);
5688 ielem
->datalen
= strlen(origstr
);
5694 * Not much to do for the non-threading case. Just copy the
5695 * subject we have so far into str and truncate it.
5697 strncpy(str
, subject
, strsize
-1);
5698 str
[strsize
-1] = '\0';
5703 * We need to add subjielem to the end of the ourifield->ielem list.
5706 if(ourifield
->ielem
){
5707 for(ielem
= ourifield
->ielem
;
5708 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5711 ielem
->next
= subjielem
;
5714 ourifield
->ielem
= subjielem
;
5717 ourifield
->leftadj
= 1;
5720 if(opening
&& ourifield
){
5721 IELEM_S
*ftielem
= NULL
;
5725 first_text
= fetch_firsttext(idata
, 0);
5731 strncpy(sep
, ps_global
->VAR_OPENING_SEP
? ps_global
->VAR_OPENING_SEP
: " - ",
5733 sep
[sizeof(sep
)-1] = '\0';
5734 removing_double_quotes(sep
);
5735 seplen
= strlen(sep
);
5737 ftielem
= new_ielem(&ftielem
);
5738 ftielem
->type
= eTypeCol
;
5739 ftielem
->freedata
= 1;
5740 len
= strlen(first_text
) + seplen
;
5741 ftielem
->data
= (char *) fs_get((len
+ 1) * sizeof(char));
5743 strncpy(ftielem
->data
, sep
, seplen
);
5744 strncpy(ftielem
->data
+seplen
, first_text
, len
+1-seplen
);
5745 ftielem
->data
[len
] = '\0';
5747 ftielem
->datalen
= strlen(ftielem
->data
);
5749 fs_give((void **) &first_text
);
5752 if(pico_usingcolor()
5753 && ps_global
->VAR_IND_OP_FORE_COLOR
5754 && ps_global
->VAR_IND_OP_BACK_COLOR
){
5755 ftielem
->freecolor
= 1;
5756 ftielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
, ps_global
->VAR_IND_OP_BACK_COLOR
);
5759 * This space is here so that if the opening text does
5760 * not extend all the way to the end of the field then
5761 * we'll switch the color back and paint the rest of the
5762 * field in the Normal color or the index line color.
5764 ielem
= new_ielem(&ftielem
);
5765 ielem
->freedata
= 1;
5766 ielem
->data
= cpystr(" ");
5770 if(ourifield
->ielem
){
5771 for(ielem
= ourifield
->ielem
;
5772 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5775 ielem
->next
= ftielem
;
5778 ourifield
->ielem
= ftielem
;
5781 ourifield
->leftadj
= 1;
5786 set_ielem_widths_in_field(ourifield
);
5789 fs_give((void **) &origsubj
);
5792 fs_give((void **) &free_subj
);
5797 * Returns an allocated string which is the passed in subject with a
5798 * list of keywords prepended.
5800 * If kwtype == KW you will end up with
5802 * {keyword1 keyword2} subject
5804 * (actually, keyword nicknames will be used instead of the actual keywords
5805 * in the case that the user defined nicknames)
5807 * If kwtype == KWInit you get
5811 * where A is the first letter of the first keyword and B is the first letter
5812 * of the second defined keyword. No space between them. There could be more
5815 * If an ielemp is passed in it will be filled out with the data and colors
5816 * of the pieces of the subject but the print_format strings will not
5820 prepend_keyword_subject(MAILSTREAM
*stream
, long int rawno
, char *subject
,
5821 SubjKW kwtype
, IELEM_S
**ielemp
, char *braces
)
5823 char *p
, *next_piece
, *retsubj
= NULL
, *str
;
5824 char *left_brace
= NULL
, *right_brace
= NULL
;
5826 int some_set
= 0, save
;
5829 COLOR_PAIR
*color
= NULL
;
5830 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5835 if(braces
&& *braces
)
5836 get_pair(braces
, &left_brace
, &right_brace
, 1, 0);
5838 len
= (left_brace
? strlen(left_brace
) : 0) +
5839 (right_brace
? strlen(right_brace
) : 0);
5841 if(stream
&& rawno
>= 0L && rawno
<= stream
->nmsgs
){
5842 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
5843 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5846 len
++; /* space between keywords */
5848 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5851 else if(kwtype
== KWInit
){
5852 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5853 /* interested in only the first UTF-8 initial */
5856 unsigned long remaining_octets
;
5857 unsigned char *inputp
;
5859 remaining_octets
= strlen(str
);
5860 inputp
= (unsigned char *) str
;
5861 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5862 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5863 len
+= (unsigned) (inputp
- (unsigned char *) str
);
5872 if((kwtype
== KW
|| kwtype
== KWInit
) && some_set
){
5873 len
+= strlen(subject
); /* subject is already UTF-8 if needed */
5874 retsubj
= (char *) fs_get((len
+ 1) * sizeof(*retsubj
));
5875 memset(retsubj
, 0, (len
+ 1) * sizeof(*retsubj
));
5876 next_piece
= p
= retsubj
;
5878 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5879 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5881 if(left_brace
&& len
> 0)
5882 sstrncpy(&p
, left_brace
, len
);
5884 else if(kwtype
== KW
)
5887 if(ielemp
&& p
> next_piece
){
5890 ielem
= new_ielem(ielemp
);
5891 ielem
->freedata
= 1;
5892 ielem
->data
= cpystr(next_piece
);
5893 ielem
->datalen
= strlen(next_piece
);
5898 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5900 if(kwtype
== KWInit
){
5903 unsigned long remaining_octets
;
5904 unsigned char *inputp
;
5906 remaining_octets
= strlen(str
);
5907 inputp
= (unsigned char *) str
;
5908 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5909 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5910 if(len
-(p
-retsubj
) > 0){
5911 sstrncpy(&p
, str
, MIN(inputp
- (unsigned char *) str
,len
-(p
-retsubj
)));
5912 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5913 && ((kw
->nick
&& kw
->nick
[0]
5914 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5915 || (kw
->kw
&& kw
->kw
[0]
5916 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5917 ielem
= new_ielem(ielemp
);
5918 ielem
->freedata
= 1;
5921 ielem
->data
= cpystr(next_piece
);
5922 ielem
->datalen
= strlen(next_piece
);
5923 ielem
->color
= color
;
5932 free_color_pair(&color
);
5936 if(len
-(p
-retsubj
) > 0)
5937 sstrncpy(&p
, str
, len
-(p
-retsubj
));
5939 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5940 && ((kw
->nick
&& kw
->nick
[0]
5941 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5942 || (kw
->kw
&& kw
->kw
[0]
5943 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5944 ielem
= new_ielem(ielemp
);
5945 ielem
->freedata
= 1;
5948 ielem
->data
= cpystr(next_piece
);
5949 ielem
->datalen
= strlen(next_piece
);
5950 ielem
->color
= color
;
5957 free_color_pair(&color
);
5962 if(len
-(p
-retsubj
) > 0 && right_brace
)
5963 sstrncpy(&p
, right_brace
, len
-(p
-retsubj
));
5965 if(ielemp
&& p
> next_piece
){
5968 ielem
= new_ielem(ielemp
);
5969 ielem
->freedata
= 1;
5970 ielem
->data
= cpystr(next_piece
);
5971 ielem
->datalen
= strlen(next_piece
);
5976 if(len
-(p
-retsubj
) > 0 && subject
)
5977 sstrncpy(&p
, subject
, len
-(p
-retsubj
));
5979 if(ielemp
&& p
> next_piece
){
5982 ielem
= new_ielem(ielemp
);
5983 ielem
->type
= eTypeCol
;
5984 ielem
->freedata
= 1;
5985 ielem
->data
= cpystr(next_piece
);
5986 ielem
->datalen
= strlen(next_piece
);
5989 if(pico_usingcolor()
5990 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5991 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5992 ielem
->freecolor
= 1;
5993 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5997 retsubj
[len
] = '\0'; /* just making sure */
6001 ielem
= new_ielem(ielemp
);
6002 ielem
->type
= eTypeCol
;
6003 ielem
->freedata
= 1;
6004 ielem
->data
= cpystr(subject
);
6005 ielem
->datalen
= strlen(subject
);
6006 if(pico_usingcolor()
6007 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
6008 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
6009 ielem
->freecolor
= 1;
6010 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
6014 retsubj
= cpystr(subject
);
6019 fs_give((void **) &left_brace
);
6022 fs_give((void **) &right_brace
);
6030 * This means we should ensure that all data ends up being UTF-8 data.
6031 * That covers the data in ice ielems and str.
6034 from_str(IndexColType ctype
, INDEXDATA_S
*idata
, char *str
, size_t strsize
, ICE_S
*ice
)
6036 char *field
, *newsgroups
, *border
, *p
, *fptr
= NULL
, *q
= NULL
;
6039 int depth
= 0, mult
= 2;
6040 PINETHRD_S
*thd
, *thdorig
;
6043 && (ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
6044 || ps_global
->thread_disp_style
== THREAD_INDENT_FROM2
6045 || ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
)){
6047 if(pith_opt_truncate_sfstr
&& (*pith_opt_truncate_sfstr
)()){
6048 IFIELD_S
*ourifield
= NULL
;
6050 if(ice
&& ice
->ifield
){
6051 /* move to last ifield, the one we're working on */
6052 for(ourifield
= ice
->ifield
;
6053 ourifield
&& ourifield
->next
;
6054 ourifield
= ourifield
->next
)
6058 if(ourifield
&& ourifield
->width
> 0)
6059 width
= ourifield
->width
;
6065 width
= MIN(width
, strsize
-1);
6067 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
6068 border
= str
+ width
;
6069 if(pith_opt_condense_thread_cue
)
6070 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
6072 && get_lflag(idata
->stream
,
6079 while(thd
->parent
&& (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
6083 if(ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
)
6086 fptr
+= (mult
*depth
);
6087 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
6088 thd
&& thd
->parent
&& p
>= str
;
6089 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
6090 if(p
+ mult
>= border
&& !q
){
6091 if(width
>= 4 && depth
< 100){
6092 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
6095 else if(width
>= 5 && depth
< 1000){
6096 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
6100 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
6110 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6113 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6116 if(thd
== thdorig
&& !thd
->branch
)
6117 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6118 else if(thd
== thdorig
|| thd
->branch
)
6125 else if(p
< border
){
6127 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6130 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6133 if(thd
== thdorig
&& !thd
->branch
)
6134 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6135 else if(thd
== thdorig
|| thd
->branch
)
6146 strsize
-= (fptr
- str
);
6149 case iFromToNotNews
:
6150 if(!(addr
= fetch_from(idata
)) || address_is_us(addr
, ps_global
)){
6152 strncpy(fptr
, "To: ", strsize
-1);
6153 fptr
[strsize
-1] = '\0';
6157 if((field
= ((addr
= fetch_to(idata
))
6159 : (addr
= fetch_cc(idata
))
6162 && set_index_addr(idata
, field
, addr
, "To: ",
6166 if(ctype
== iFromTo
&&
6167 (newsgroups
= fetch_newsgroups(idata
)) &&
6169 snprintf(fptr
, strsize
, "To: %-*.*s", (int)(strsize
-1-4), (int)(strsize
-1-4),
6174 /* else fall thru to From: */
6177 /* else fall thru to From: */
6183 set_index_addr(idata
, "From", fetch_from(idata
), NULL
, strsize
-1, fptr
);
6188 if((addr
= fetch_from(idata
)) && addr
->mailbox
&& addr
->mailbox
[0]){
6189 char *mb
= NULL
, *hst
= NULL
, *at
= NULL
;
6193 if(ctype
== iAddress
&& addr
->host
&& addr
->host
[0]
6194 && addr
->host
[0] != '.'){
6200 if(!at
|| strsize
-1 <= len
)
6201 snprintf(fptr
, strsize
, "%-*.*s", (int)(strsize
-1), (int)(strsize
-1), mb
);
6203 snprintf(fptr
, strsize
, "%s@%-*.*s", mb
, (int)(strsize
-1-len
-1), (int)(strsize
-1-len
-1), hst
);
6212 replace_tabs_by_space(str
);
6217 * Set up the elements contained in field so that they take up the
6218 * whole field width. Data is assumed to be UTF-8.
6221 set_ielem_widths_in_field(IFIELD_S
*ifield
)
6223 IELEM_S
*ielem
= NULL
;
6224 int datawidth
, fmtwidth
;
6229 fmtwidth
= ifield
->width
;
6231 for(ielem
= ifield
->ielem
; ielem
&& fmtwidth
> 0; ielem
= ielem
->next
){
6232 if(!ifield
->leftadj
&& ielem
->next
){
6233 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield
->ctype
));
6237 datawidth
= (int) utf8_width(ielem
->data
);
6238 if(datawidth
>= fmtwidth
|| !ielem
->next
){
6239 set_print_format(ielem
, fmtwidth
, ifield
->leftadj
);
6243 set_print_format(ielem
, datawidth
, ifield
->leftadj
);
6244 fmtwidth
-= datawidth
;
6251 * Simple hash function from K&R 2nd edition, p. 144.
6253 * This one is modified to never return 0 so we can use that as a special
6254 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6255 * as a special value that can't be returned by line_hash.
6260 unsigned long hashval
;
6262 for(hashval
= 0; *s
!= '\0'; s
++)
6263 hashval
= *s
+ 31 * hashval
;
6265 hashval
= hashval
% LINE_HASH_N
;
6275 * Returns nonzero if considered hidden, 0 if not considered hidden.
6278 msgline_hidden(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
6282 if(flags
& MH_ANYTHD
){
6283 ret
= ((any_lflagged(msgmap
, MN_HIDE
) > 0)
6284 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
));
6286 else if(flags
& MH_THISTHD
&& THREADING() && sp_viewing_a_thread(stream
)){
6287 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6288 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
));
6291 if(THREADING() && sp_viewing_a_thread(stream
)){
6292 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6293 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
)
6294 || get_lflag(stream
, msgmap
, msgno
, MN_CHID
));
6296 else if(THRD_INDX()){
6298 * If this message is in the collapsed part of a thread,
6299 * it's hidden. It must be a top-level of a thread to be
6300 * considered visible. Even if it is top-level, it is only
6301 * visible if some message in the thread is not hidden.
6303 if(get_lflag(stream
, msgmap
, msgno
, MN_CHID
)) /* not top */
6306 unsigned long rawno
;
6307 PINETHRD_S
*thrd
= NULL
;
6309 rawno
= mn_m2raw(msgmap
, msgno
);
6311 thrd
= fetch_thread(stream
, rawno
);
6313 ret
= !thread_has_some_visible(stream
, thrd
);
6317 ret
= ((any_lflagged(msgmap
, MN_HIDE
| MN_CHID
) > 0)
6318 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
| MN_CHID
));
6323 "msgline_hidden(%ld): %s\n", msgno
, ret
? "HID" : "VIS"));
6330 adjust_cur_to_visible(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
6335 cur
= mn_get_cur(msgmap
);
6337 /* if current is hidden, adjust */
6338 if(cur
>= 1L && cur
<= mn_get_total(msgmap
)
6339 && msgline_hidden(stream
, msgmap
, cur
, 0)){
6341 dir
= mn_get_revsort(msgmap
) ? -1 : 1;
6344 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6345 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6346 && msgline_hidden(stream
, msgmap
, n
, 0);
6350 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6351 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6352 mn_reset_cur(msgmap
, n
);
6353 else{ /* no visible in that direction */
6355 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6356 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6357 && msgline_hidden(stream
, msgmap
, n
, 0);
6361 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6362 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6363 mn_reset_cur(msgmap
, n
);
6371 setup_for_index_index_screen(void)
6373 format_index_line
= format_index_index_line
;
6374 setup_header_widths
= setup_index_header_widths
;
6379 setup_for_thread_index_screen(void)
6381 format_index_line
= format_thread_index_line
;
6382 setup_header_widths
= setup_thread_header_widths
;
6387 ice_hash(ICE_S
*ice
)
6389 char buf
[MAX_SCREEN_COLS
+1];
6394 simple_index_line(buf
, sizeof(buf
), ice
, 0L);
6396 buf
[sizeof(buf
) - 1] = '\0';
6398 return(line_hash(buf
));
6403 left_adjust(int width
)
6405 return(format_str(width
, 1));
6410 right_adjust(int width
)
6412 return(format_str(width
, 0));
6417 * Returns allocated and filled in format string.
6420 format_str(int width
, int left
)
6425 len
= PRINT_FORMAT_LEN(width
,left
) * sizeof(char);
6426 format
= (char *) fs_get(len
+ 1);
6427 copy_format_str(width
, left
, format
, len
);
6435 * Put the left or right adjusted format string of width width into
6436 * dest. Dest is of size n+1.
6439 copy_format_str(int width
, int left
, char *dest
, int n
)
6443 p
= int2string(width
);
6445 snprintf(dest
, n
+1, "%%%s%s.%ss", left
? "-" : "", p
, p
);
6454 * Sets up the print_format string to be width wide with left or right
6455 * adjust. Takes care of memory freeing and allocation.
6458 set_print_format(IELEM_S
*ielem
, int width
, int leftadj
)
6463 if(ielem
->print_format
){
6464 /* is there enough room? */
6465 if(ielem
->freeprintf
< PRINT_FORMAT_LEN(width
,leftadj
)+1){
6466 fs_resize((void **) &ielem
->print_format
,
6467 (PRINT_FORMAT_LEN(width
,leftadj
)+1) * sizeof(char));
6468 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);
6471 copy_format_str(width
, leftadj
, ielem
->print_format
,
6472 PRINT_FORMAT_LEN(width
,leftadj
));
6475 ielem
->print_format
= leftadj
? left_adjust(width
)
6476 : right_adjust(width
);
6477 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);