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 2006-2008 University of Washington
7 * Copyright 2013-2014 Eduardo Chappa
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, 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 {"FULLSTATUS", iFStatus
, FOR_INDEX
},
459 {"IMAPSTATUS", iIStatus
, FOR_INDEX
},
460 {"SHORTIMAPSTATUS", iSIStatus
, FOR_INDEX
},
461 {"SUBJKEY", iSubjKey
, FOR_INDEX
},
462 {"SUBJKEYINIT", iSubjKeyInit
, FOR_INDEX
},
463 {"SUBJECTTEXT", iSubjectText
, FOR_INDEX
},
464 {"SUBJKEYTEXT", iSubjKeyText
, FOR_INDEX
},
465 {"SUBJKEYINITTEXT", iSubjKeyInitText
, FOR_INDEX
},
466 {"OPENINGTEXT", iOpeningText
, FOR_INDEX
},
467 {"OPENINGTEXTNQ", iOpeningTextNQ
, FOR_INDEX
},
468 {"KEY", iKey
, FOR_INDEX
},
469 {"KEYINIT", iKeyInit
, FOR_INDEX
},
470 {"DESCRIPSIZE", iDescripSize
, FOR_INDEX
},
471 {"ATT", iAtt
, FOR_INDEX
},
472 {"SCORE", iScore
, FOR_INDEX
},
473 {"PRIORITY", iPrio
, FOR_INDEX
},
474 {"PRIORITYALPHA", iPrioAlpha
, FOR_INDEX
},
475 {"PRIORITY!", iPrioBang
, FOR_INDEX
},
476 {"LONGDATE", iLDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
477 {"SHORTDATE1", iS1Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
478 {"SHORTDATE2", iS2Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
479 {"SHORTDATE3", iS3Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
480 {"SHORTDATE4", iS4Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
481 {"DATEISO", iDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
482 {"SHORTDATEISO", iDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
483 {"SMARTDATE", iSDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
484 {"SMARTTIME", iSTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
485 {"SMARTDATEISO", iSDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
486 {"SMARTDATESHORTISO",iSDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
487 {"SMARTDATES1", iSDateS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
488 {"SMARTDATES2", iSDateS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
489 {"SMARTDATES3", iSDateS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
490 {"SMARTDATES4", iSDateS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
491 {"SMARTDATETIME", iSDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
492 {"SMARTDATETIMEISO",iSDateTimeIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
493 {"SMARTDATETIMESHORTISO",iSDateTimeIsoS
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
494 {"SMARTDATETIMES1", iSDateTimeS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
495 {"SMARTDATETIMES2", iSDateTimeS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
496 {"SMARTDATETIMES3", iSDateTimeS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
497 {"SMARTDATETIMES4", iSDateTimeS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
498 {"SMARTDATETIME24", iSDateTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
499 {"SMARTDATETIMEISO24", iSDateTimeIso24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
500 {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
501 {"SMARTDATETIMES124", iSDateTimeS124
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
502 {"SMARTDATETIMES224", iSDateTimeS224
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
503 {"SMARTDATETIMES324", iSDateTimeS324
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
504 {"SMARTDATETIMES424", iSDateTimeS424
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
505 {"TIME24", iTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
506 {"TIME12", iTime12
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
507 {"TIMEZONE", iTimezone
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
508 {"MONTHABBREV", iMonAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
509 {"DAYOFWEEKABBREV", iDayOfWeekAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
510 {"DAYOFWEEK", iDayOfWeek
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
511 {"FROM", iFrom
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
512 {"TO", iTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
513 {"SENDER", iSender
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
514 {"CC", iCc
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
515 {"RECIPS", iRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
516 {"NEWS", iNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
517 {"TOANDNEWS", iToAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
518 {"NEWSANDTO", iNewsAndTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
519 {"RECIPSANDNEWS", iRecipsAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
520 {"NEWSANDRECIPS", iNewsAndRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
521 {"MSGID", iMsgID
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
522 {"CURNEWS", iCurNews
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
523 {"DAYDATE", iRDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
524 {"PREFDATE", iPrefDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
525 {"PREFTIME", iPrefTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
526 {"PREFDATETIME", iPrefDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
527 {"DAY", iDay
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
528 {"DAYORDINAL", iDayOrdinal
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
529 {"DAY2DIGIT", iDay2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
530 {"MONTHLONG", iMonLong
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
531 {"MONTH", iMon
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
532 {"MONTH2DIGIT", iMon2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
533 {"YEAR", iYear
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
534 {"YEAR2DIGIT", iYear2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
535 {"ADDRESS", iAddress
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
536 {"MAILBOX", iMailbox
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
537 {"ROLENICK", iRoleNick
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
538 {"INIT", iInit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
539 {"CURDATE", iCurDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
540 {"CURDATEISO", iCurDateIso
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
541 {"CURDATEISOS", iCurDateIsoS
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
542 {"CURTIME24", iCurTime24
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
543 {"CURTIME12", iCurTime12
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
544 {"CURDAY", iCurDay
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
545 {"CURDAY2DIGIT", iCurDay2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
546 {"CURDAYOFWEEK", iCurDayOfWeek
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
547 {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb
,
548 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
549 {"CURMONTH", iCurMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
550 {"CURMONTH2DIGIT", iCurMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
551 {"CURMONTHLONG", iCurMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
552 {"CURMONTHABBREV", iCurMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
553 {"CURYEAR", iCurYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
554 {"CURYEAR2DIGIT", iCurYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
555 {"CURPREFDATE", iCurPrefDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
556 {"CURPREFTIME", iCurPrefTime
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
557 {"CURPREFDATETIME", iCurPrefDateTime
,
558 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
559 {"LASTMONTH", iLstMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
560 {"LASTMONTH2DIGIT", iLstMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
561 {"LASTMONTHLONG", iLstMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
562 {"LASTMONTHABBREV", iLstMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
563 {"LASTMONTHYEAR", iLstMonYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
564 {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit
,
565 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
566 {"LASTYEAR", iLstYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
567 {"LASTYEAR2DIGIT", iLstYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
568 {"HEADER", iHeader
, FOR_INDEX
},
569 {"TEXT", iText
, FOR_INDEX
},
570 {"ARROW", iArrow
, FOR_INDEX
},
571 {"NEWLINE", iNewLine
, FOR_REPLY_INTRO
},
572 {"CURSORPOS", iCursorPos
, FOR_TEMPLATE
},
573 {NULL
, iNothing
, FOR_NOTHING
}
576 INDEX_PARSE_T itokensinv
[sizeof(itokens
)/sizeof(itokens
[0])];
579 inverse_itokens(void)
582 for (pt
= itokens
; pt
->name
; pt
++)
583 itokensinv
[pt
->ctype
].ctype
= pt
- itokens
;
590 return((i
< sizeof(itokens
) && itokens
[i
].name
) ? &itokens
[i
] : NULL
);
595 * Args txt -- The token being checked begins at the beginning
596 * of txt. The end of the token is delimited by a null, or
597 * white space, or an underscore if DELIM_USCORE is set,
598 * or a left paren if DELIM_PAREN is set.
599 * flags -- Flags contains the what_for value, and DELIM_ values.
601 * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
604 itoktype(char *txt
, int flags
)
611 * Separate a copy of the possible token out of txt.
615 while(w
< token
+sizeof(token
)-1 &&
617 !(!(*v
& 0x80) && isspace((unsigned char)*v
)) &&
618 !(flags
& DELIM_USCORE
&& *v
== '_') &&
619 !(flags
& DELIM_PAREN
&& *v
== '(') &&
620 !(flags
& DELIM_COLON
&& *v
== ':'))
625 for(pt
= itokens
; pt
->name
; pt
++)
626 if(pt
->what_for
& flags
&& !strucmp(pt
->name
, token
))
634 parse_index_format(char *format_str
, INDEX_COL_S
**answer
)
639 INDEX_COL_S cdesc
[200]; /* plenty of temp storage for answer */
641 memset((void *)cdesc
, 0, sizeof(cdesc
));
644 while(p
&& *p
&& column
< 200-1){
645 /* skip leading white space for next word */
646 p
= skip_white_space(p
);
647 pt
= itoktype(p
, FOR_INDEX
| DELIM_PAREN
| DELIM_COLON
);
649 /* ignore unrecognized word */
651 for(q
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
658 "parse_index_format: unrecognized token: %s\n",
660 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
661 _("Unrecognized word in index-format: %s"), q
);
665 cdesc
[column
].ctype
= pt
->ctype
;
667 if(pt
->ctype
== iHeader
|| pt
->ctype
== iText
){
669 * iHeader field has special syntax.
671 * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
673 * where width is the regular width or percentage width or
674 * left out for default width, fieldnum defaults to 0 for
675 * whole thing, 1 for first field, ...
676 * and field_separators is a list of characters which separate
677 * the fields. The whole parenthesized part is optional. If used
678 * the arguments can be dropped from the right, so
681 * HEADER:hdrname(10) or
682 * HEADER:hdrname(10%) or
683 * HEADER:hdrname(10,2) or
684 * HEADER:hdrname(,2) or
685 * HEADER:hdrname(10,2, ) or
686 * HEADER:hdrname(10,2,\,:) or
687 * HEADER:hdrname(10,2,\,:,R)
689 * iText field uses the hdrtok field for convenience. It has syntax
695 * and the literal text goes into the index line. It is also special
696 * because there is no 1 column space after this field.
700 p
+= strlen(pt
->name
);
702 /* look for header name */
704 char *w
, hdrname
[200];
709 if(*p
== '\"'){ /* quoted name */
711 while(w
< hdrname
+ sizeof(hdrname
)-1 && *p
!= '\"'){
723 while(w
< hdrname
+ sizeof(hdrname
)-1 &&
724 !(!(*p
& 0x80) && isspace((unsigned char)*p
)) &&
732 cdesc
[column
].hdrtok
= new_hdrtok(hdrname
);
735 if(pt
->ctype
== iHeader
){
736 dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
737 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
740 dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
741 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
746 if(pt
->ctype
== iHeader
){
747 dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p
));
748 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
751 dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p
));
752 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
755 /* skip over rest of bogus config */
756 while(!(!(*p
& 0x80) && isspace((unsigned char)*p
)) && *p
!= '(')
761 /* skip over name and look for parens */
762 p
+= strlen(pt
->name
);
768 while(p
&& *p
&& isdigit((unsigned char) *p
))
771 if(pt
->ctype
== iHeader
){
772 /* first argument is width or width percentage, like for others */
773 if(p
&& *p
&& (*p
== ')' || *p
== ',')){
775 cdesc
[column
].wtype
= Fixed
;
776 cdesc
[column
].req_width
= atoi(q
);
779 cdesc
[column
].wtype
= WeCalculate
;
780 cdesc
[column
].req_width
= 0;
783 else if(p
&& *p
&& *p
== '%' && p
> q
){
784 cdesc
[column
].wtype
= Percent
;
785 cdesc
[column
].req_width
= atoi(q
);
789 cdesc
[column
].wtype
= WeCalculate
;
790 cdesc
[column
].req_width
= 0;
793 /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
794 if(p
&& *p
&& *p
== ','){
796 /* no space allowed between arguments */
797 if(*p
&& isdigit((unsigned char) *p
)){
799 while(*p
&& isdigit((unsigned char) *p
))
802 cdesc
[column
].hdrtok
->fieldnum
= atoi(q
);
805 * Optional 3rd argument is field separators.
806 * Comma is \, and backslash is \\.
812 /* don't use default */
813 if(*p
&& *p
!= ')' && *p
!= ',' && cdesc
[column
].hdrtok
->fieldseps
)
814 cdesc
[column
].hdrtok
->fieldseps
[0] = '\0';
817 if(*p
== '\"' && strchr(p
+1, '\"')){
819 while(*p
&& *p
!= ')' && *p
!= '\"' && *p
!= ','){
820 if(cdesc
[column
].hdrtok
->fieldseps
)
821 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
823 if(*p
== '\\' && *(p
+1))
826 if(cdesc
[column
].hdrtok
->fieldseps
){
827 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
828 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
829 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
837 while(*p
&& *p
!= ')' && *p
!= ','){
838 if(cdesc
[column
].hdrtok
->fieldseps
)
839 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
840 if(*p
== '\\' && *(p
+1))
843 if(cdesc
[column
].hdrtok
->fieldseps
){
844 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
845 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
846 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
851 /* optional 4th argument, left or right adjust */
854 if(*p
== 'L' || *p
== 'l')
855 cdesc
[column
].hdrtok
->adjustment
= Left
;
856 else if(*p
== 'R' || *p
== 'r')
857 cdesc
[column
].hdrtok
->adjustment
= Right
;
859 dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p
? p
: "<null>"));
860 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 4th argument should be L or R");
866 dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p
? p
: "<null>"));
867 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
872 if(p
&& *p
&& *p
== ')' && p
> q
){
873 cdesc
[column
].wtype
= Fixed
;
874 cdesc
[column
].req_width
= atoi(q
);
876 else if(p
&& *p
&& *p
== '%' && p
> q
){
877 cdesc
[column
].wtype
= Percent
;
878 cdesc
[column
].req_width
= atoi(q
);
881 cdesc
[column
].wtype
= WeCalculate
;
882 cdesc
[column
].req_width
= 0;
887 /* if they left out width for iText we can figure it out */
888 if(pt
->ctype
== iText
&& cdesc
[column
].hdrtok
&& cdesc
[column
].hdrtok
->hdrname
){
889 cdesc
[column
].wtype
= Fixed
;
890 cdesc
[column
].req_width
= utf8_width(cdesc
[column
].hdrtok
->hdrname
);
893 cdesc
[column
].wtype
= WeCalculate
;
894 cdesc
[column
].req_width
= 0;
899 /* skip text at end of word */
900 while(p
&& *p
&& !isspace((unsigned char)*p
))
904 /* if, after all that, we didn't find anything recognizable, bitch */
906 dprint((1, "Completely unrecognizable index-format\n"));
907 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
908 _("Configured \"index-format\" unrecognizable. Using default."));
912 /* Finish with Nothing column */
913 cdesc
[column
].ctype
= iNothing
;
915 /* free up old answer */
917 free_index_format(answer
);
919 /* allocate space for new answer */
920 *answer
= (INDEX_COL_S
*)fs_get((column
+1)*sizeof(INDEX_COL_S
));
921 memset((void *)(*answer
), 0, (column
+1)*sizeof(INDEX_COL_S
));
922 /* copy answer to real place */
923 for(i
= 0; i
<= column
; i
++)
924 (*answer
)[i
] = cdesc
[i
];
931 * These types are basically fixed in width.
932 * The order is slightly significant. The ones towards the front of the
933 * list get space allocated sooner than the ones at the end of the list.
935 static IndexColType fixed_ctypes
[] = {
936 iMessNo
, iStatus
, iFStatus
, iIStatus
, iSIStatus
,
937 iDate
, iSDate
, iSDateTime
, iSDateTime24
,
939 iS1Date
, iS2Date
, iS3Date
, iS4Date
, iDateIso
, iDateIsoS
,
940 iSDateIso
, iSDateIsoS
,
941 iSDateS1
, iSDateS2
, iSDateS3
, iSDateS4
,
942 iSDateTimeIso
, iSDateTimeIsoS
,
943 iSDateTimeS1
, iSDateTimeS2
, iSDateTimeS3
, iSDateTimeS4
,
944 iSDateTimeIso24
, iSDateTimeIsoS24
,
945 iSDateTimeS124
, iSDateTimeS224
, iSDateTimeS324
, iSDateTimeS424
,
946 iSize
, iSizeComma
, iSizeNarrow
, iKSize
, iDescripSize
,
947 iPrio
, iPrioBang
, iPrioAlpha
, iInit
,
948 iAtt
, iTime24
, iTime12
, iTimezone
, iMonAbb
, iYear
, iYear2Digit
,
949 iDay2Digit
, iMon2Digit
, iDayOfWeekAbb
, iScore
, iMonLong
, iDayOfWeek
954 ctype_is_fixed_length(IndexColType ctype
)
959 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
962 if(ctype
== fixed_ctypes
[j
])
970 /*----------------------------------------------------------------------
971 Setup the widths of the various columns in the index display
974 setup_index_header_widths(MAILSTREAM
*stream
)
976 int colspace
; /* for reserving space between columns */
977 int j
, some_to_calculate
;
978 int space_left
, screen_width
, fix
;
979 int keep_going
, tot_pct
, was_sl
;
984 max_msgno
= mn_get_total(ps_global
->msgmap
);
986 dprint((8, "=== setup_index_header_widths() ===\n"));
988 clear_icache_flags(stream
);
989 screen_width
= ps_global
->ttyo
->screen_cols
;
990 space_left
= screen_width
;
991 some_to_calculate
= 0;
995 * Calculate how many fields there are so we know how many spaces
996 * between columns to reserve. Fill in Fixed widths now. Reserve
997 * special case WeCalculate with non-zero req_widths before doing
998 * Percent cases below.
1000 for(cdesc
= ps_global
->index_disp_format
;
1001 cdesc
->ctype
!= iNothing
;
1004 if(cdesc
->wtype
== Fixed
){
1005 cdesc
->width
= cdesc
->req_width
;
1006 if(cdesc
->width
> 0)
1009 else if(cdesc
->wtype
== Percent
){
1010 cdesc
->width
= 0; /* calculated later */
1013 else{ /* WeCalculate */
1014 cdesc
->width
= cdesc
->req_width
; /* reserve this for now */
1015 some_to_calculate
++;
1019 /* no space after iText */
1020 if(cdesc
->ctype
== iText
)
1023 space_left
-= cdesc
->width
;
1026 colspace
= MAX(colspace
, 0);
1028 space_left
-= colspace
; /* space between columns */
1030 ps_global
->display_keywords_in_subject
= 0;
1031 ps_global
->display_keywordinits_in_subject
= 0;
1034 * Set the actual lengths for the fixed width fields and set up
1035 * the left or right adjustment for everything.
1036 * There should be a case setting actual_length for all of the types
1039 for(cdesc
= ps_global
->index_disp_format
;
1040 cdesc
->ctype
!= iNothing
;
1043 wtype
= cdesc
->wtype
;
1045 if(cdesc
->ctype
== iSubjKey
|| cdesc
->ctype
== iSubjKeyText
)
1046 ps_global
->display_keywords_in_subject
= 1;
1047 else if(cdesc
->ctype
== iSubjKeyInit
|| cdesc
->ctype
== iSubjKeyInitText
)
1048 ps_global
->display_keywordinits_in_subject
= 1;
1050 if(wtype
== WeCalculate
|| wtype
== Percent
|| cdesc
->width
!= 0){
1052 switch(cdesc
->ctype
){
1053 case iSDate
: case iSDateIso
: case iSDateIsoS
:
1054 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1055 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
1056 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1057 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
1058 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1060 set_format_includes_smartdate(stream
);
1067 if(ctype_is_fixed_length(cdesc
->ctype
)){
1068 switch(cdesc
->ctype
){
1072 cdesc
->actual_length
= 1;
1073 cdesc
->adjustment
= Left
;
1079 cdesc
->actual_length
= 2;
1080 cdesc
->adjustment
= Left
;
1084 cdesc
->actual_length
= 2;
1085 cdesc
->adjustment
= Right
;
1090 cdesc
->actual_length
= 3;
1091 cdesc
->adjustment
= Left
;
1095 set_format_includes_msgno(stream
);
1096 if(max_msgno
< 1000)
1097 cdesc
->actual_length
= 3;
1098 else if(max_msgno
< 10000)
1099 cdesc
->actual_length
= 4;
1100 else if(max_msgno
< 100000)
1101 cdesc
->actual_length
= 5;
1103 cdesc
->actual_length
= 6;
1105 cdesc
->adjustment
= Right
;
1110 cdesc
->actual_length
= 4;
1111 cdesc
->adjustment
= Left
;
1116 cdesc
->actual_length
= 5;
1117 cdesc
->adjustment
= Left
;
1121 cdesc
->actual_length
= 5;
1122 cdesc
->adjustment
= Right
;
1127 cdesc
->actual_length
= 6;
1128 cdesc
->adjustment
= Left
;
1132 cdesc
->actual_length
= 6;
1133 cdesc
->adjustment
= Right
;
1139 cdesc
->actual_length
= 7;
1140 cdesc
->adjustment
= Right
;
1144 cdesc
->actual_length
= 7;
1145 cdesc
->adjustment
= Left
;
1149 cdesc
->actual_length
= 7;
1150 cdesc
->adjustment
= Left
;
1159 cdesc
->actual_length
= 8;
1160 cdesc
->adjustment
= Left
;
1164 cdesc
->actual_length
= 8;
1165 cdesc
->adjustment
= Right
;
1176 cdesc
->actual_length
= cdesc
->req_width
;
1177 cdesc
->adjustment
= Left
;
1181 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1182 case iSDateTimeIsoS
:
1183 case iSDateTimeIsoS24
:
1184 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1185 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1186 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
1187 if(cdesc
->ctype
== iSDateIso
1188 || cdesc
->ctype
== iSDateTimeIso
1189 || cdesc
->ctype
== iSDateTimeIso24
)
1190 cdesc
->actual_length
= 10;
1192 cdesc
->actual_length
= 9;
1194 cdesc
->adjustment
= Left
;
1198 cdesc
->actual_length
= 9;
1199 cdesc
->adjustment
= Right
;
1203 cdesc
->actual_length
= 10;
1204 cdesc
->adjustment
= Left
;
1208 cdesc
->actual_length
= 12;
1209 cdesc
->adjustment
= Left
;
1213 panic("Unhandled fixed case in setup_index_header");
1217 else if(cdesc
->ctype
== iHeader
)
1218 cdesc
->adjustment
= cdesc
->hdrtok
? cdesc
->hdrtok
->adjustment
: Left
;
1220 cdesc
->adjustment
= Left
;
1224 if(ps_global
->display_keywords_in_subject
)
1225 ps_global
->display_keywordinits_in_subject
= 0;
1227 /* if have reserved unneeded space for size, give it back */
1228 for(cdesc
= ps_global
->index_disp_format
;
1229 cdesc
->ctype
!= iNothing
;
1231 if(cdesc
->ctype
== iSize
|| cdesc
->ctype
== iKSize
||
1232 cdesc
->ctype
== iSizeNarrow
||
1233 cdesc
->ctype
== iSizeComma
|| cdesc
->ctype
== iDescripSize
){
1234 if(cdesc
->actual_length
== 0){
1235 if((fix
=cdesc
->width
) > 0){ /* had this reserved */
1240 space_left
++; /* +1 for space between columns */
1245 * Calculate the field widths that are basically fixed in width.
1246 * Do them in this order in case we don't have enough space to go around.
1247 * The set of fixed_ctypes here is the same as the set where we
1248 * set the actual_lengths above.
1250 for(j
= 0; space_left
> 0 && some_to_calculate
; j
++){
1252 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
1255 for(cdesc
= ps_global
->index_disp_format
;
1256 cdesc
->ctype
!= iNothing
&& space_left
> 0 && some_to_calculate
;
1258 if(cdesc
->ctype
== fixed_ctypes
[j
] && cdesc
->wtype
== WeCalculate
){
1259 some_to_calculate
--;
1260 fix
= MIN(cdesc
->actual_length
- cdesc
->width
, space_left
);
1261 cdesc
->width
+= fix
;
1267 * Fill in widths for Percent cases. If there are no more to calculate,
1268 * use the percentages as relative numbers and use the rest of the space,
1269 * else treat them as absolute percentages of the original avail screen.
1272 if(some_to_calculate
){
1273 int tot_requested
= 0;
1276 * Requests are treated as percent of screen width. See if they
1277 * will all fit. If not, trim them back proportionately.
1279 for(cdesc
= ps_global
->index_disp_format
;
1280 cdesc
->ctype
!= iNothing
;
1282 if(cdesc
->wtype
== Percent
){
1283 /* The 2, 200, and +100 are because we're rounding */
1284 fix
= ((2*cdesc
->req_width
*
1285 (screen_width
-colspace
))+100) / 200;
1286 tot_requested
+= fix
;
1290 if(tot_requested
> space_left
){
1291 int multiplier
= (100 * space_left
) / tot_requested
;
1293 for(cdesc
= ps_global
->index_disp_format
;
1294 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1296 if(cdesc
->wtype
== Percent
){
1297 /* The 2, 200, and +100 are because we're rounding */
1298 fix
= ((2*cdesc
->req_width
*
1299 (screen_width
-colspace
))+100) / 200;
1300 fix
= (2 * fix
* multiplier
+ 100) / 200;
1301 fix
= MIN(fix
, space_left
);
1302 cdesc
->width
+= fix
;
1308 for(cdesc
= ps_global
->index_disp_format
;
1309 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1311 if(cdesc
->wtype
== Percent
){
1312 /* The 2, 200, and +100 are because we're rounding */
1313 fix
= ((2*cdesc
->req_width
*
1314 (screen_width
-colspace
))+100) / 200;
1315 fix
= MIN(fix
, space_left
);
1316 cdesc
->width
+= fix
;
1324 was_sl
= space_left
;
1325 /* add up total percentages requested */
1326 for(cdesc
= ps_global
->index_disp_format
;
1327 cdesc
->ctype
!= iNothing
;
1329 if(cdesc
->wtype
== Percent
)
1330 tot_pct
+= cdesc
->req_width
;
1332 /* give relative weight to requests */
1333 for(cdesc
= ps_global
->index_disp_format
;
1334 cdesc
->ctype
!= iNothing
&& space_left
> 0 && tot_pct
> 0;
1336 if(cdesc
->wtype
== Percent
){
1337 fix
= ((2*cdesc
->req_width
*was_sl
)+tot_pct
) / (2*tot_pct
);
1338 fix
= MIN(fix
, space_left
);
1339 cdesc
->width
+= fix
;
1346 /* split up rest, give twice as much to Subject */
1348 while(space_left
> 0 && keep_going
){
1350 for(cdesc
= ps_global
->index_disp_format
;
1351 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1353 if(cdesc
->wtype
== WeCalculate
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1357 if(space_left
> 0 && (cdesc
->ctype
== iSubject
1358 || cdesc
->ctype
== iSubjectText
1359 || cdesc
->ctype
== iSubjKey
1360 || cdesc
->ctype
== iSubjKeyText
1361 || cdesc
->ctype
== iSubjKeyInit
1362 || cdesc
->ctype
== iSubjKeyInitText
)){
1370 /* if still more, pad out percent's */
1372 while(space_left
> 0 && keep_going
){
1374 for(cdesc
= ps_global
->index_disp_format
;
1375 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1377 if(cdesc
->wtype
== Percent
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1385 /* if user made Fixed fields too big, give back space */
1387 while(space_left
< 0 && keep_going
){
1389 for(cdesc
= ps_global
->index_disp_format
;
1390 cdesc
->ctype
!= iNothing
&& space_left
< 0;
1392 if(cdesc
->wtype
== Fixed
&& cdesc
->width
> 0){
1400 if(pith_opt_save_index_state
)
1401 (*pith_opt_save_index_state
)(FALSE
);
1406 setup_thread_header_widths(MAILSTREAM
*stream
)
1408 clear_icache_flags(stream
);
1409 if(pith_opt_save_index_state
)
1410 (*pith_opt_save_index_state
)(TRUE
);
1415 * load_overview - c-client call back to gather overview data
1417 * Note: if we never get called, UID represents a hole
1418 * if we're passed a zero UID, totally bogus overview data
1419 * if we're passed a zero obuf, mostly bogus overview data
1422 load_overview(MAILSTREAM
*stream
, imapuid_t uid
, OVERVIEW
*obuf
, long unsigned int rawno
)
1424 if(obuf
&& rawno
>= 1L && stream
&& rawno
<= stream
->nmsgs
){
1428 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1432 * Only really load the thing if we've got an NNTP stream
1433 * otherwise we're just using mail_fetch_overview to load the
1434 * IMAP envelope cache with the specific set of messages
1437 idata
.stream
= stream
;
1438 idata
.rawno
= rawno
;
1439 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), idata
.rawno
);
1440 idata
.size
= obuf
->optional
.octets
;
1441 idata
.from
= obuf
->from
;
1442 idata
.date
= obuf
->date
;
1443 idata
.subject
= obuf
->subject
;
1445 ice
= (*format_index_line
)(&idata
);
1446 if(idata
.bogus
&& ice
){
1449 clear_ice(&ice
->tice
);
1454 else if(F_OFF(F_QUELL_NEWS_ENV_CB
, ps_global
)
1455 && (!THRD_INDX() || (ice
&& ice
->tice
))
1456 && !msgline_hidden(stream
, sp_msgmap(stream
), idata
.msgno
, 0)
1457 && pith_opt_paint_index_hline
){
1458 (*pith_opt_paint_index_hline
)(stream
, idata
.msgno
, ice
);
1465 build_header_work(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
1466 long int msgno
, long int top_msgno
, int msgcount
, int *fetched
)
1470 long n
, i
, cnt
, rawno
, visible
, limit
= -1L;
1472 rawno
= mn_m2raw(msgmap
, msgno
);
1476 ice
= fetch_ice(stream
, rawno
);
1480 if(ice
->tice
&& ice
->tice
->ifield
1481 && ice
->tice
->color_lookup_done
&& ice
->tice
->widths_done
){
1483 char buf
[MAX_SCREEN_COLS
+1];
1484 simple_index_line(buf
, sizeof(buf
), ice
->tice
, msgno
);
1486 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1489 buf
[0] ? strlen(buf
) : 0));
1494 ice
= fetch_ice(stream
, rawno
);
1498 if(ice
->ifield
&& ice
->color_lookup_done
&& ice
->widths_done
){
1500 char buf
[MAX_SCREEN_COLS
+1];
1501 simple_index_line(buf
, sizeof(buf
), ice
, msgno
);
1503 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1506 buf
[0] ? strlen(buf
) : 0));
1512 * If we are in THRD_INDX() and the width changed we don't currently
1513 * have a method of fixing just the widths and print_format strings.
1514 * Instead, we clear the index cache entry and start over.
1516 if(THRD_INDX() && ice
&& ice
->tice
&& ice
->tice
->ifield
1517 && !ice
->tice
->widths_done
){
1518 clear_ice(&ice
->tice
);
1522 * Fetch everything we need to start filling in the index line
1523 * explicitly via mail_fetch_overview. On an nntp stream
1524 * this has the effect of building the index lines in the
1525 * load_overview callback. Under IMAP we're either getting
1526 * the envelope data via the imap_envelope callback or
1527 * preloading the cache. Either way, we're getting exactly
1528 * what we want rather than relying on linear lookahead sort
1531 if(!(fetched
&& *fetched
) && index_in_overview(stream
)
1532 && ((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1533 || (!THRD_INDX() && !ice
->ifield
))){
1542 /* clear sequence bits */
1543 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1544 if((mc
= mail_elt(stream
, n
)) != NULL
)
1548 * Light interesting bits
1549 * NOTE: not set above because m2raw's cheaper
1550 * than raw2m for every message
1554 * Unfortunately, it is expensive to calculate visible pages
1555 * in thread index if we are zoomed, so we don't try.
1557 if(THRD_INDX() && any_lflagged(msgmap
, MN_HIDE
))
1558 visible
= msgmap
->visible_threads
;
1559 else if(THREADING() && sp_viewing_a_thread(stream
)){
1561 * We know that all visible messages in the thread are marked
1564 for(visible
= 0L, n
= top_msgno
;
1565 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1568 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1571 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1577 visible
= mn_get_total(msgmap
)
1578 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1580 limit
= MIN(visible
, msgcount
);
1586 * First add the msgno we're asking for in case it
1589 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, msgno
));
1590 if(msgno
<= mn_get_total(msgmap
)
1591 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1592 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1595 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, top_msgno
));
1598 * Loop through visible threads, marking them for fetching.
1599 * Stop at end of screen or sooner if we run out of visible
1603 n
= mn_raw2m(msgmap
, thrd
->rawno
);
1605 && n
<= mn_get_total(msgmap
)
1606 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1607 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1613 /* find next thread which is visible */
1615 if(mn_get_revsort(msgmap
) && thrd
->prevthd
)
1616 thrd
= fetch_thread(stream
, thrd
->prevthd
);
1617 else if(!mn_get_revsort(msgmap
) && thrd
->nextthd
)
1618 thrd
= fetch_thread(stream
, thrd
->nextthd
);
1622 && msgline_hidden(stream
, msgmap
,
1623 mn_raw2m(msgmap
, thrd
->rawno
), 0));
1630 * First add the msgno we're asking for in case it
1633 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)
1634 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,msgno
)))) || !ic
->ifield
)){
1635 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1637 * If we're doing a MUTTLIKE display the index line
1638 * may depend on the thread parent, and grandparent,
1639 * and further back. So just fetch the whole thread
1643 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1645 thrd
= fetch_thread(stream
, thrd
->top
);
1647 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1649 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1650 && (mc
= mail_elt(stream
,rawno
))
1651 && !mc
->private.msg
.env
){
1660 && n
<= mn_get_total(msgmap
)
1661 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,n
)))) || !ic
->ifield
)){
1662 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1664 * If we're doing a MUTTLIKE display the index line
1665 * may depend on the thread parent, and grandparent,
1666 * and further back. So just fetch the whole thread
1670 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1672 thrd
= fetch_thread(stream
, thrd
->top
);
1674 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1676 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1677 && (mc
= mail_elt(stream
,rawno
))
1678 && !mc
->private.msg
.env
){
1687 /* find next n which is visible */
1688 while(++n
<= mn_get_total(msgmap
)
1689 && msgline_hidden(stream
, msgmap
, n
, 0))
1695 seq
= build_sequence(stream
, NULL
, NULL
);
1697 ps_global
->dont_count_flagchanges
= 1;
1698 mail_fetch_overview_sequence(stream
, seq
,
1699 (stream
->dtb
&& stream
->dtb
->name
1700 && !strcmp(stream
->dtb
->name
, "imap"))
1701 ? NULL
: load_overview
);
1702 ps_global
->dont_count_flagchanges
= 0;
1703 fs_give((void **) &seq
);
1708 * reassign ice from the cache as it may've been built
1709 * within the overview callback or it may have become stale
1710 * in the prior sequence bit setting loop ...
1712 rawno
= mn_m2raw(msgmap
, msgno
);
1713 ice
= fetch_ice(stream
, rawno
);
1718 if((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1719 || (!THRD_INDX() && !ice
->ifield
)){
1723 * With pre-fetching/callback-formatting done and no success,
1724 * fall into formatting the requested line...
1726 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1727 idata
.stream
= stream
;
1728 idata
.msgno
= msgno
;
1729 idata
.rawno
= mn_m2raw(msgmap
, msgno
);
1730 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
1731 && (mc
= mail_elt(stream
, idata
.rawno
))){
1732 idata
.size
= mc
->rfc822_size
;
1733 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
1738 ice
= (*format_index_line
)(&idata
);
1744 * If needed, reset the print_format strings so that they add up to
1745 * the right total width. The reset width functionality isn't implemented
1746 * for THRD_INDX() so we are just doing a complete rebuild in that
1747 * case. This is driven by the clear_ice() call in clear_index_cache_ent()
1748 * so it should never be the case that THRD_INDX() is true and only
1749 * widths_done needs to be fixed.
1751 if((!THRD_INDX() && ice
->ifield
&& !ice
->widths_done
)){
1756 if(need_format_setup(stream
))
1757 setup_header_widths(stream
);
1760 working_ice
= ice
? ice
->tice
: NULL
;
1766 * First fix the ifield widths. The cdescs with nonzero widths
1767 * should correspond to the ifields that are defined.
1769 ifield
= working_ice
->ifield
;
1770 for(cdesc
= ps_global
->index_disp_format
;
1771 cdesc
->ctype
!= iNothing
&& ifield
; cdesc
++){
1773 if(cdesc
->ctype
!= ifield
->ctype
){
1774 dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno
, (int) cdesc
->ctype
, (int) ifield
->ctype
));
1778 ifield
->width
= cdesc
->width
;
1779 ifield
= ifield
->next
;
1783 /* fix the print_format strings and widths */
1784 for(ifield
= working_ice
->ifield
; ifield
; ifield
= ifield
->next
)
1785 set_ielem_widths_in_field(ifield
);
1787 working_ice
->widths_done
= 1;
1791 if(THRD_INDX() && ice
->tice
)
1792 ice
->tice
->color_lookup_done
= 1;
1795 * Look for a color for this line (and other lines in the current
1796 * view). This does a SEARCH for each role which has a color until
1797 * it finds a match. This will be satisfied by the c-client
1798 * cache created by the mail_fetch_overview above if it is a header
1801 if(!THRD_INDX() && !ice
->color_lookup_done
){
1802 COLOR_PAIR
*linecolor
;
1805 PAT_STATE
*pstate
= NULL
;
1807 if(pico_usingcolor()){
1809 if(THREADING() && sp_viewing_a_thread(stream
)){
1810 for(visible
= 0L, n
= top_msgno
;
1811 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1814 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1817 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1823 visible
= mn_get_total(msgmap
)
1824 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1826 limit
= MIN(visible
, msgcount
);
1828 /* clear sequence bits */
1829 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1830 if((mc
= mail_elt(stream
, n
)) != NULL
)
1837 && n
<= mn_get_total(msgmap
)
1838 && (!(ic
=fetch_ice(stream
,(rawno
= mn_m2raw(msgmap
, n
)))) || !ic
->color_lookup_done
)){
1840 if(rawno
>= 1L && rawno
<= stream
->nmsgs
1841 && (mc
= mail_elt(stream
, rawno
))){
1850 /* find next n which is visible */
1851 while(++n
<= mn_get_total(msgmap
)
1852 && msgline_hidden(stream
, msgmap
, n
, 0))
1857 * Why is there a loop here? The first call to get_index_line_color
1858 * will return a set of messages which match one of the roles.
1859 * Then, we eliminate those messages from the search set and try
1860 * again. This time we'd get past that role and into a different
1861 * role. Because of that, we hang onto the state and don't reset
1862 * to the first_pattern on the second and subsequent times
1863 * through the loop, avoiding fruitless match_pattern calls in
1864 * get_index_line_color.
1865 * Before the first call, pstate should be set to NULL.
1868 ss
= build_searchset(stream
);
1873 colormatch
= get_index_line_color(stream
, ss
, &pstate
,
1877 * Assign this color to all matched msgno's and
1878 * turn off the sequence bit so we won't check
1882 for(s
= ss
; s
; s
= s
->next
){
1883 for(n
= s
->first
; n
<= s
->last
; n
++){
1884 if(n
>= 1L && n
<= stream
->nmsgs
1885 && (mc
= mail_elt(stream
, n
))
1889 ic
= fetch_ice(stream
, n
);
1891 ic
->color_lookup_done
= 1;
1893 ic
->linecolor
= new_color_pair(linecolor
->fg
,
1901 free_color_pair(&linecolor
);
1904 /* have to mark the rest of the lookups done */
1905 for(s
= ss
; s
&& cnt
> 0; s
= s
->next
){
1906 for(n
= s
->first
; n
<= s
->last
&& cnt
> 0; n
++){
1907 if(n
>= 1L && n
<= stream
->nmsgs
1908 && (mc
= mail_elt(stream
, n
))
1911 ic
= fetch_ice(stream
, n
);
1913 ic
->color_lookup_done
= 1;
1918 /* just making sure */
1922 mail_free_searchset(&ss
);
1928 ice
= fetch_ice(stream
, mn_m2raw(msgmap
, msgno
));
1931 ice
->color_lookup_done
= 1;
1934 return(ice
); /* Return formatted index data */
1939 day_of_week(struct date
*d
)
1950 m
-= 3; /* March is month 0 */
1952 return((d
->day
+2+((7+31*m
)/12)+y
+(y
/4)+(y
/400)-(y
/100))%7);
1956 static int daytab
[2][13] = {
1957 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1958 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1962 day_of_year(struct date
*d
)
1966 if(d
->year
<= 0 || d
->month
< 1 || d
->month
> 12)
1970 leap
= (d
->year
%4 == 0 && d
->year
%100 != 0) || d
->year
%400 == 0;
1971 for(i
= 1; i
< d
->month
; i
++)
1972 doy
+= daytab
[leap
][i
];
1979 /*----------------------------------------------------------------------
1980 Format a string summarizing the message header for index on screen
1982 Args: buffer -- buffer to place formatted line
1983 idata -- snot it takes to format the line
1985 Result: returns pointer given buffer IF entry formatted
1986 else NULL if there was a problem (but the buffer is
1987 still suitable for display)
1990 format_index_index_line(INDEXDATA_S
*idata
)
1992 char str
[BIGWIDTH
+1], to_us
, status
, *field
,
1994 int i
, collapsed
= 0, start
, fromfield
;
1998 ADDRESS
*addr
, *toaddr
, *ccaddr
, *last_to
;
1999 PINETHRD_S
*thrd
= NULL
;
2000 INDEX_COL_S
*cdesc
= NULL
;
2004 COLOR_PAIR
*color
= NULL
;
2005 struct variable
*vars
= ps_global
->vars
;
2007 dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
2008 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
2011 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
2015 free_ifield(&ice
->ifield
);
2018 * Operate on a temporary copy of ice. The reason for this
2019 * is that we may end up causing a pine_mail_fetchenvelope() call
2020 * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
2021 * and mm_flags may do a clear_ice(), freeing the ice we are working
2022 * on out from under us. We try to fetch everything we need in
2023 * build_header_work() but c-client will short-circuit our request
2024 * if we already got the raw header for some reason. One possible
2025 * reason is a categorizer command in a filter. In that case
2026 * we still need a fetch fast to get the rest of the envelope data.
2028 ice
= copy_ice(ice
);
2030 /* is this a collapsed thread index line? */
2031 if(!idata
->bogus
&& THREADING()){
2032 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2033 collapsed
= thrd
&& thrd
->next
2034 && get_lflag(idata
->stream
, NULL
,
2035 idata
->rawno
, MN_COLL
);
2038 /* calculate contents of the required fields */
2039 for(cdesc
= ps_global
->index_disp_format
; cdesc
->ctype
!= iNothing
; cdesc
++)
2041 memset(str
, 0, sizeof(str
));
2042 ifield
= new_ifield(&ice
->ifield
);
2043 ifield
->ctype
= cdesc
->ctype
;
2044 ifield
->width
= cdesc
->width
;
2048 if(cdesc
->ctype
== iMessNo
)
2049 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2050 else if(idata
->bogus
< 2 && (cdesc
->ctype
== iSubject
2051 || cdesc
->ctype
== iSubjectText
2052 || cdesc
->ctype
== iSubjKey
2053 || cdesc
->ctype
== iSubjKeyText
2054 || cdesc
->ctype
== iSubjKeyInit
2055 || cdesc
->ctype
== iSubjKeyInitText
))
2056 snprintf(str
, sizeof(str
), "%s", _("[ No Message Text Available ]"));
2059 switch(cdesc
->ctype
){
2061 to_us
= status
= ' ';
2063 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2064 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
2065 status
= status_symbol_for_thread(idata
->stream
, thrd
,
2069 if((mc
=mail_elt(idata
->stream
,idata
->rawno
)) && mc
->flagged
)
2070 to_us
= '*'; /* simple */
2071 else if(!IS_NEWS(idata
->stream
)){
2072 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2073 if(address_is_us(addr
, ps_global
)){
2081 if(to_us
!= '+' && resent_to_us(idata
)){
2087 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2088 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2089 if(address_is_us(addr
, ps_global
)){
2096 status
= (!idata
->stream
|| !IS_NEWS(idata
->stream
)
2097 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))
2103 if(user_flag_is_set(idata
->stream
, idata
->rawno
, FORWARDED_FLAG
))
2113 snprintf(str
, sizeof(str
), "%c %c", to_us
, status
);
2115 ifield
->leftadj
= 1;
2116 for(i
= 0; i
< 3; i
++){
2117 ielem
= new_ielem(&ifield
->ielem
);
2118 ielem
->freedata
= 1;
2119 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2120 ielem
->data
[0] = str
[i
];
2121 ielem
->data
[1] = '\0';
2123 set_print_format(ielem
, 1, ifield
->leftadj
);
2126 if(pico_usingcolor()){
2129 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2130 ielem
= ifield
->ielem
;
2131 ielem
->freecolor
= 1;
2132 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2135 else if(str
[0] == '+' || str
[0] == '-'){
2136 if(VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
2137 ielem
= ifield
->ielem
;
2138 ielem
->freecolor
= 1;
2139 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2144 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2145 ielem
= ifield
->ielem
->next
->next
;
2146 ielem
->freecolor
= 1;
2147 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2150 else if(str
[2] == 'A'){
2151 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2152 ielem
= ifield
->ielem
->next
->next
;
2153 ielem
->freecolor
= 1;
2154 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2157 else if(str
[2] == 'F'){
2158 if(VAR_IND_FWD_FORE_COLOR
&& VAR_IND_FWD_BACK_COLOR
){
2159 ielem
= ifield
->ielem
->next
->next
;
2160 ielem
->freecolor
= 1;
2161 ielem
->color
= new_color_pair(VAR_IND_FWD_FORE_COLOR
, VAR_IND_FWD_BACK_COLOR
);
2164 else if(str
[2] == 'N'){
2165 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2166 ielem
= ifield
->ielem
->next
->next
;
2167 ielem
->freecolor
= 1;
2168 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2179 char new, answered
, deleted
, flagged
;
2182 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2183 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 0);
2187 if(!IS_NEWS(idata
->stream
)){
2188 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2189 if(address_is_us(addr
, ps_global
)){
2194 if(to_us
== ' ' && resent_to_us(idata
))
2197 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2198 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2199 if(address_is_us(addr
, ps_global
)){
2206 new = answered
= deleted
= flagged
= ' ';
2209 unsigned long save_branch
, cnt
, tot_in_thrd
;
2212 * Branch is a sibling, not part of the thread, so
2213 * don't consider it when displaying this line.
2215 save_branch
= thrd
->branch
;
2218 tot_in_thrd
= count_flags_in_thread(idata
->stream
, thrd
,
2221 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_DEL
);
2223 deleted
= (cnt
== tot_in_thrd
) ? 'D' : 'd';
2225 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_ANS
);
2227 answered
= (cnt
== tot_in_thrd
) ? 'A' : 'a';
2229 /* no lower case *, same thing for some or all */
2230 if(count_flags_in_thread(idata
->stream
, thrd
, F_FLAG
))
2233 new = status_symbol_for_thread(idata
->stream
, thrd
,
2236 thrd
->branch
= save_branch
;
2239 mc
= (idata
->rawno
> 0L && idata
->stream
2240 && idata
->rawno
<= idata
->stream
->nmsgs
)
2241 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2242 if(mc
&& mc
->valid
){
2243 if(cdesc
->ctype
== iIStatus
|| cdesc
->ctype
== iSIStatus
){
2245 new = mc
->seen
? 'R' : 'N';
2250 && (!IS_NEWS(idata
->stream
)
2251 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)))
2265 snprintf(str
, sizeof(str
), "%c %c%c%c%c", to_us
, flagged
, new,
2268 if(cdesc
->ctype
== iSIStatus
)
2273 ifield
->leftadj
= 1;
2274 for(i
= start
; i
< 6; i
++){
2275 ielem
= new_ielem(&ifield
->ielem
);
2276 ielem
->freedata
= 1;
2277 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2278 ielem
->data
[0] = str
[i
];
2279 ielem
->data
[1] = '\0';
2281 set_print_format(ielem
, 1, ifield
->leftadj
);
2284 if(pico_usingcolor()){
2286 if(str
[0] == '+' || str
[0] == '-'){
2288 && VAR_IND_PLUS_FORE_COLOR
2289 && VAR_IND_PLUS_BACK_COLOR
){
2290 ielem
= ifield
->ielem
;
2291 ielem
->freecolor
= 1;
2292 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2297 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2299 ielem
= ifield
->ielem
;
2301 ielem
= ifield
->ielem
->next
->next
;
2303 ielem
->freecolor
= 1;
2304 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2308 if(str
[3] == 'N' || str
[3] == 'n'){
2309 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2311 ielem
= ifield
->ielem
->next
;
2313 ielem
= ifield
->ielem
->next
->next
->next
;
2315 ielem
->freecolor
= 1;
2316 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2319 else if(str
[3] == 'R' || str
[3] == 'r'){
2320 if(VAR_IND_REC_FORE_COLOR
&& VAR_IND_REC_BACK_COLOR
){
2322 ielem
= ifield
->ielem
->next
;
2324 ielem
= ifield
->ielem
->next
->next
->next
;
2326 ielem
->freecolor
= 1;
2327 ielem
->color
= new_color_pair(VAR_IND_REC_FORE_COLOR
, VAR_IND_REC_BACK_COLOR
);
2330 else if(str
[3] == 'U' || str
[3] == 'u'){
2331 if(VAR_IND_UNS_FORE_COLOR
&& VAR_IND_UNS_BACK_COLOR
){
2333 ielem
= ifield
->ielem
->next
;
2335 ielem
= ifield
->ielem
->next
->next
->next
;
2337 ielem
->freecolor
= 1;
2338 ielem
->color
= new_color_pair(VAR_IND_UNS_FORE_COLOR
, VAR_IND_UNS_BACK_COLOR
);
2342 if(str
[4] == 'A' || str
[4] == 'a'){
2343 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2345 ielem
= ifield
->ielem
->next
->next
;
2347 ielem
= ifield
->ielem
->next
->next
->next
->next
;
2349 ielem
->freecolor
= 1;
2350 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2354 if(str
[5] == 'D' || str
[5] == 'd'){
2355 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2357 ielem
= ifield
->ielem
->next
->next
->next
;
2359 ielem
= ifield
->ielem
->next
->next
->next
->next
->next
;
2361 ielem
->freecolor
= 1;
2362 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2372 * This is a special case. The message number is
2373 * generated on the fly in the painting routine.
2374 * But the data array is allocated here in case it
2375 * is useful for the paint routine.
2377 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2381 snprintf(str
, sizeof(str
), "%-*.*s", ifield
->width
, ifield
->width
, " ");
2382 if(VAR_IND_ARR_FORE_COLOR
&& VAR_IND_ARR_BACK_COLOR
){
2383 ifield
->leftadj
= 1;
2384 ielem
= new_ielem(&ifield
->ielem
);
2385 ielem
->freedata
= 1;
2386 ielem
->data
= cpystr(str
);
2387 ielem
->datalen
= strlen(str
);
2388 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
2389 ielem
->freecolor
= 1;
2390 ielem
->color
= new_color_pair(VAR_IND_ARR_FORE_COLOR
,
2391 VAR_IND_ARR_BACK_COLOR
);
2397 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2398 if(score
== SCORE_UNDEF
){
2399 SEARCHSET
*ss
= NULL
;
2401 ss
= mail_newsearchset();
2402 ss
->first
= ss
->last
= (unsigned long) idata
->rawno
;
2405 * This looks like it might be expensive to get the
2406 * score for each message when needed but it shouldn't
2407 * be too bad because we know we have the envelope
2408 * data cached. We can't calculate all of the scores
2409 * we need for the visible messages right here in
2410 * one fell swoop because we don't have the other
2411 * envelopes yet. And we can't get the other
2412 * envelopes at this point because we may be in
2413 * the middle of a c-client callback (pine_imap_env).
2414 * (Actually we could, because we know whether or
2415 * not we're in the callback because of the no_fetch
2417 * We have another problem if the score rules depend
2418 * on something other than envelope data. I guess they
2419 * only do that if they have an alltext (search the
2420 * text of the message) definition. So, we're going
2421 * to pass no_fetch to calculate_scores so that it
2422 * can return an error if we need the text data but
2423 * can't get it because of no_fetch. Setting bogus
2424 * will cause us to do the scores calculation later
2425 * when we are no longer in the callback.
2428 (calculate_some_scores(idata
->stream
,
2429 ss
, idata
->no_fetch
) == 0)
2431 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2432 mail_free_searchset(&ss
);
2436 snprintf(str
, sizeof(str
), "%ld", score
!= SCORE_UNDEF
? score
: 0L);
2439 case iDate
: case iMonAbb
: case iLDate
:
2440 case iSDate
: case iSTime
:
2441 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
2442 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
2443 case iSDateIsoS
: case iSDateIso
:
2444 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
2446 case iSDateTimeIsoS
: case iSDateTimeIso
:
2447 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
2449 case iSDateTimeIsoS24
: case iSDateTimeIso24
:
2450 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
2451 case iTimezone
: case iYear
: case iYear2Digit
:
2452 case iRDate
: case iDay
: case iDay2Digit
: case iMon2Digit
:
2453 case iDayOrdinal
: case iMon
: case iMonLong
:
2454 case iDayOfWeekAbb
: case iDayOfWeek
:
2455 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
2456 date_str(fetch_date(idata
), cdesc
->ctype
, 0, str
, sizeof(str
), cdesc
->monabb_width
);
2460 case iFromToNotNews
:
2465 from_str(cdesc
->ctype
, idata
, str
, sizeof(str
), ice
);
2469 if(((field
= ((addr
= fetch_to(idata
))
2471 : (addr
= fetch_cc(idata
))
2474 && !set_index_addr(idata
, field
, addr
, NULL
, BIGWIDTH
, str
))
2476 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2477 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, newsgroups
);
2482 set_index_addr(idata
, "Cc", fetch_cc(idata
), NULL
, BIGWIDTH
, str
);
2486 toaddr
= fetch_to(idata
);
2487 ccaddr
= fetch_cc(idata
);
2488 for(last_to
= toaddr
;
2489 last_to
&& last_to
->next
;
2490 last_to
= last_to
->next
)
2493 /* point end of to list temporarily at cc list */
2495 last_to
->next
= ccaddr
;
2497 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2500 last_to
->next
= NULL
;
2506 if((addr
= fetch_sender(idata
)) != NULL
)
2507 set_index_addr(idata
, "Sender", addr
, NULL
, BIGWIDTH
, str
);
2514 if((addr
= fetch_from(idata
)) && addr
->personal
){
2515 char *name
, *initials
= NULL
;
2517 name
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
2518 SIZEOF_20KBUF
, addr
->personal
);
2519 if(name
== addr
->personal
){
2520 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
2521 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
2522 name
= (char *) tmp_20k_buf
;
2526 initials
= reply_quote_initials(name
);
2527 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, initials
);
2536 if((l
= fetch_size(idata
)) < 10*1000L)
2537 snprintf(str
, sizeof(str
), "(%lu)", l
);
2539 else if(l
< 1000L*1000L - 1000L/2){
2540 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2541 snprintf(str
, sizeof(str
), "(%luK)", l
);
2543 /* 1.0M ... 99.9M */
2544 else if(l
< 1000L*100L*1000L - 100L*1000L/2){
2545 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2547 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2549 /* 100M ... 2000M */
2550 else if(l
<= 2*1000L*1000L*1000L){
2551 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2553 snprintf(str
, sizeof(str
), "(%luM)", l
);
2556 snprintf(str
, sizeof(str
), "(HUGE!)");
2562 if((l
= fetch_size(idata
)) < 100*1000L)
2563 snprintf(str
, sizeof(str
), "(%s)", comatose(l
));
2564 /* 100K ... 9,999K */
2565 else if(l
< 10L*1000L*1000L - 1000L/2){
2566 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2567 snprintf(str
, sizeof(str
), "(%sK)", comatose(l
));
2569 /* 10.0M ... 999.9M */
2570 else if(l
< 1000L*1000L*1000L - 100L*1000L/2){
2571 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2573 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2575 /* 1,000M ... 2,000M */
2576 else if(l
<= 2*1000L*1000L*1000L){
2577 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2579 snprintf(str
, sizeof(str
), "(%sM)", comatose(l
));
2582 snprintf(str
, sizeof(str
), "(HUGE!)");
2588 if((l
= fetch_size(idata
)) < 1000L)
2589 snprintf(str
, sizeof(str
), "(%lu)", l
);
2591 else if(l
< 100L*1000L - 1000L/2){
2592 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2593 snprintf(str
, sizeof(str
), "(%luK)", l
);
2596 else if(l
< 1000L*1000L - 100L*1000L/2){
2597 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= 100L*1000L/2
2599 snprintf(str
, sizeof(str
), "(.%luM)", l
);
2602 else if(l
< 1000L*100L*1000L - 1000L*1000L/2){
2603 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2605 snprintf(str
, sizeof(str
), "(%luM)", l
);
2608 else if(l
< 1000L*1000L*1000L - 100L*1000L*1000L/2){
2609 l
= l
/(100L*1000L*1000L) + (l
%(100L*1000L*1000L) >=
2610 (100L*1000L*1000L/2) ? 1L : 0L);
2611 snprintf(str
, sizeof(str
), "(.%luG)", l
);
2614 else if(l
<= 2*1000L*1000L*1000L){
2615 l
= l
/(1000L*1000L*1000L) + (l
%(1000L*1000L*1000L) >=
2616 (1000L*1000L*1000L/2) ? 1L : 0L);
2617 snprintf(str
, sizeof(str
), "(%luG)", l
);
2620 snprintf(str
, sizeof(str
), "(HUGE!)");
2624 /* From Carl Jacobsen <carl@ucsd.edu> */
2626 l
= fetch_size(idata
);
2627 l
= (l
/ 1024L) + (l
% 1024L != 0 ? 1 : 0);
2629 if(l
< 1024L) { /* 0k .. 1023k */
2630 snprintf(str
, sizeof(str
), "(%luk)", l
);
2632 } else if (l
< 100L * 1024L){ /* 1.0M .. 99.9M */
2633 snprintf(str
, sizeof(str
), "(%lu.M)", (l
* 10L) / 1024L);
2634 if ((p
= strchr(str
, '.')) != NULL
) {
2635 p
--; p
[1] = p
[0]; p
[0] = '.'; /* swap last digit & . */
2637 } else if (l
<= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2638 snprintf(str
, sizeof(str
), "(%luM)", l
/ 1024L);
2640 snprintf(str
, sizeof(str
), "(HUGE!)");
2646 if((body
= fetch_body(idata
)) != NULL
)
2650 mc
= (idata
->rawno
> 0L && idata
->stream
2651 && idata
->rawno
<= idata
->stream
->nmsgs
)
2652 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2653 if(mc
&& mc
->rfc822_size
< 6000)
2654 snprintf(str
, sizeof(str
), "(short )");
2655 else if(mc
&& mc
->rfc822_size
< 25000)
2656 snprintf(str
, sizeof(str
), "(medium )");
2657 else if(mc
&& mc
->rfc822_size
< 100000)
2658 snprintf(str
, sizeof(str
), "(long )");
2660 snprintf(str
, sizeof(str
), "(huge )");
2666 if(strucmp(body
->subtype
, "MIXED") == 0){
2669 x
= body
->nested
.part
2670 ? body
->nested
.part
->body
.type
2674 if(body
->nested
.part
->body
.size
.bytes
< 6000)
2675 snprintf(str
, sizeof(str
), "(short+ )");
2676 else if(body
->nested
.part
->body
.size
.bytes
2678 snprintf(str
, sizeof(str
), "(medium+)");
2679 else if(body
->nested
.part
->body
.size
.bytes
2681 snprintf(str
, sizeof(str
), "(long+ )");
2683 snprintf(str
, sizeof(str
), "(huge+ )");
2687 snprintf(str
, sizeof(str
), "(multi )");
2691 else if(strucmp(body
->subtype
, "DIGEST") == 0)
2692 snprintf(str
, sizeof(str
), "(digest )");
2693 else if(strucmp(body
->subtype
, "ALTERNATIVE") == 0)
2694 snprintf(str
, sizeof(str
), "(mul/alt)");
2695 else if(strucmp(body
->subtype
, "PARALLEL") == 0)
2696 snprintf(str
, sizeof(str
), "(mul/par)");
2698 snprintf(str
, sizeof(str
), "(multi )");
2703 snprintf(str
, sizeof(str
), "(message)");
2706 case TYPEAPPLICATION
:
2707 snprintf(str
, sizeof(str
), "(applica)");
2711 snprintf(str
, sizeof(str
), "(audio )");
2715 snprintf(str
, sizeof(str
), "(image )");
2719 snprintf(str
, sizeof(str
), "(video )");
2723 snprintf(str
, sizeof(str
), "(other )");
2732 if((body
= fetch_body(idata
)) &&
2733 body
->type
== TYPEMULTIPART
&&
2734 strucmp(body
->subtype
, "ALTERNATIVE") != 0){
2738 part
= body
->nested
.part
; /* 1st part, don't count */
2739 while(part
&& part
->next
&& atts
< 10){
2747 str
[0] = '0' + atts
;
2753 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, ice
);
2757 subj_str(idata
, str
, sizeof(str
), NoKW
, 1, ice
);
2761 subj_str(idata
, str
, sizeof(str
), KW
, 0, ice
);
2765 subj_str(idata
, str
, sizeof(str
), KW
, 1, ice
);
2769 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, ice
);
2772 case iSubjKeyInitText
:
2773 subj_str(idata
, str
, sizeof(str
), KWInit
, 1, ice
);
2777 case iOpeningTextNQ
:
2783 first_text
= fetch_firsttext(idata
, cdesc
->ctype
== iOpeningTextNQ
);
2786 strncpy(str
, first_text
, BIGWIDTH
);
2787 str
[BIGWIDTH
] = '\0';
2788 fs_give((void **) &first_text
);
2795 key_str(idata
, KW
, ice
);
2799 key_str(idata
, KWInit
, ice
);
2803 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
){
2804 strncpy(str
, newsgroups
, BIGWIDTH
);
2805 str
[BIGWIDTH
] = '\0';
2811 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2812 strncpy(str
, newsgroups
, sizeof(str
));
2814 if((l
= strlen(str
)) < sizeof(str
)){
2815 if(sizeof(str
) - l
< 6)
2816 strncpy(str
+l
, "...", sizeof(str
)-l
);
2819 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2820 set_index_addr(idata
, "To", fetch_to(idata
),
2821 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2826 set_index_addr(idata
, "To", fetch_to(idata
),
2827 NULL
, BIGWIDTH
, str
);
2834 set_index_addr(idata
, "To", fetch_to(idata
),
2835 NULL
, BIGWIDTH
, str
);
2836 if((l
= strlen(str
)) < sizeof(str
) &&
2837 (newsgroups
= fetch_newsgroups(idata
))){
2838 if(sizeof(str
) - l
< 6)
2839 strncpy(str
+l
, "...", sizeof(str
)-l
);
2842 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2845 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2847 strncpy(str
, newsgroups
, BIGWIDTH
);
2853 case iNewsAndRecips
:
2854 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2855 strncpy(str
, newsgroups
, BIGWIDTH
);
2857 if((l
= strlen(str
)) < BIGWIDTH
){
2858 if(BIGWIDTH
- l
< 6)
2859 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2861 toaddr
= fetch_to(idata
);
2862 ccaddr
= fetch_cc(idata
);
2863 for(last_to
= toaddr
;
2864 last_to
&& last_to
->next
;
2865 last_to
= last_to
->next
)
2868 /* point end of to list temporarily at cc list */
2870 last_to
->next
= ccaddr
;
2873 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2874 set_index_addr(idata
, "To", toaddr
,
2875 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2880 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2883 last_to
->next
= NULL
;
2889 case iRecipsAndNews
:
2890 toaddr
= fetch_to(idata
);
2891 ccaddr
= fetch_cc(idata
);
2892 for(last_to
= toaddr
;
2893 last_to
&& last_to
->next
;
2894 last_to
= last_to
->next
)
2897 /* point end of to list temporarily at cc list */
2899 last_to
->next
= ccaddr
;
2901 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2904 last_to
->next
= NULL
;
2906 if((l
= strlen(str
)) < BIGWIDTH
&&
2907 (newsgroups
= fetch_newsgroups(idata
))){
2908 if(BIGWIDTH
- l
< 6)
2909 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2912 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2915 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2917 strncpy(str
, newsgroups
, BIGWIDTH
);
2926 prio_str(idata
, cdesc
->ctype
, ice
);
2930 header_str(idata
, cdesc
->hdrtok
, ice
);
2934 strncpy(str
, (cdesc
->hdrtok
&& cdesc
->hdrtok
->hdrname
) ? cdesc
->hdrtok
->hdrname
: "", sizeof(str
));
2935 str
[sizeof(str
)-1] = '\0';
2943 * If the element wasn't already filled in above, do it here.
2946 ielem
= new_ielem(&ifield
->ielem
);
2948 if(color
= hdr_color(itokens
[itokensinv
[cdesc
->ctype
].ctype
].name
, NULL
, ps_global
->index_token_colors
)){
2949 if(pico_usingcolor()){
2950 ielem
->color
= new_color_pair(color
->fg
, color
->bg
);
2951 ielem
->type
= eTypeCol
;
2953 free_color_pair(&color
);
2956 ielem
->freedata
= 1;
2957 ielem
->data
= cpystr(str
);
2958 ielem
->datalen
= strlen(str
);
2960 if(fromfield
&& pico_usingcolor()
2961 && ps_global
->VAR_IND_FROM_FORE_COLOR
2962 && ps_global
->VAR_IND_FROM_BACK_COLOR
){
2963 ielem
->type
= eTypeCol
;
2964 ielem
->freecolor
= 1;
2965 ielem
->color
= new_color_pair(ps_global
->VAR_IND_FROM_FORE_COLOR
,
2966 ps_global
->VAR_IND_FROM_BACK_COLOR
);
2968 * This space is here so that if the text does
2969 * not extend all the way to the end of the field then
2970 * we'll switch the color back and paint the rest of the
2971 * field in the Normal color or the index line color.
2973 ielem
= new_ielem(&ielem
);
2974 ielem
->freedata
= 1;
2975 ielem
->data
= cpystr(" ");
2978 else if((cdesc
->ctype
== iOpeningText
|| cdesc
->ctype
== iOpeningTextNQ
)
2979 && pico_usingcolor()
2980 && ps_global
->VAR_IND_OP_FORE_COLOR
2981 && ps_global
->VAR_IND_OP_BACK_COLOR
){
2982 ielem
->type
= eTypeCol
;
2983 ielem
->freecolor
= 1;
2984 ielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
,
2985 ps_global
->VAR_IND_OP_BACK_COLOR
);
2987 * This space is here so that if the text does
2988 * not extend all the way to the end of the field then
2989 * we'll switch the color back and paint the rest of the
2990 * field in the Normal color or the index line color.
2992 ielem
= new_ielem(&ielem
);
2993 ielem
->freedata
= 1;
2994 ielem
->data
= cpystr(" ");
2998 ifield
->leftadj
= (cdesc
->adjustment
== Left
) ? 1 : 0;
2999 set_ielem_widths_in_field(ifield
);
3003 ice
->widths_done
= 1;
3004 ice
->id
= ice_hash(ice
);
3007 * Now we have to put the temporary copy of ice back as the
3010 icep
= fetch_ice_ptr(idata
->stream
, idata
->rawno
);
3012 free_ice(icep
); /* free what is already there */
3021 format_thread_index_line(INDEXDATA_S
*idata
)
3023 char *p
, buffer
[BIGWIDTH
+1];
3024 int thdlen
, space_left
, i
;
3025 PINETHRD_S
*thrd
= NULL
;
3026 ICE_S
*ice
, *tice
= NULL
, **ticep
= NULL
;
3029 int (*save_sfstr_func
)(void);
3030 struct variable
*vars
= ps_global
->vars
;
3032 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3033 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
3035 space_left
= ps_global
->ttyo
->screen_cols
;
3037 if(ps_global
->msgmap
->max_thrdno
< 1000)
3039 else if(ps_global
->msgmap
->max_thrdno
< 10000)
3041 else if(ps_global
->msgmap
->max_thrdno
< 100000)
3046 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
3048 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
3050 if(!thrd
|| !ice
) /* can't happen? */
3054 tice
= (ICE_S
*) fs_get(sizeof(*tice
));
3055 memset(tice
, 0, sizeof(*tice
));
3064 free_ifield(&tice
->ifield
);
3067 tice
= copy_ice(tice
);
3069 if(space_left
>= 3){
3073 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
3074 status
= status_symbol_for_thread(idata
->stream
, thrd
, iStatus
);
3076 if((p
-buffer
)+3 < sizeof(buffer
)){
3085 ifield
= new_ifield(&tice
->ifield
);
3086 ifield
->ctype
= iStatus
;
3088 ifield
->leftadj
= 1;
3089 for(i
= 0; i
< 3; i
++){
3090 ielem
= new_ielem(&ifield
->ielem
);
3091 ielem
->freedata
= 1;
3092 ielem
->data
= (char *) fs_get(2 * sizeof(char));
3093 ielem
->data
[0] = p
[i
];
3094 ielem
->data
[1] = '\0';
3096 set_print_format(ielem
, 1, ifield
->leftadj
);
3099 if(pico_usingcolor()){
3101 && VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
3102 ielem
= ifield
->ielem
;
3103 ielem
->freecolor
= 1;
3104 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3105 VAR_IND_IMP_BACK_COLOR
);
3106 if(F_ON(F_COLOR_LINE_IMPORTANT
, ps_global
))
3107 tice
->linecolor
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3108 VAR_IND_IMP_BACK_COLOR
);
3110 else if((to_us
== '+' || to_us
== '-')
3111 && VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
3112 ielem
= ifield
->ielem
;
3113 ielem
->freecolor
= 1;
3114 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
,
3115 VAR_IND_PLUS_BACK_COLOR
);
3119 && VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
3120 ielem
= ifield
->ielem
->next
->next
;
3121 ielem
->freecolor
= 1;
3122 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
,
3123 VAR_IND_DEL_BACK_COLOR
);
3125 else if(status
== 'N'
3126 && VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
3127 ielem
= ifield
->ielem
->next
->next
;
3128 ielem
->freecolor
= 1;
3129 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
,
3130 VAR_IND_NEW_BACK_COLOR
);
3135 if(space_left
>= thdlen
+1){
3139 snprintf(p
, sizeof(buffer
), "%*.*s", thdlen
, thdlen
, "");
3140 space_left
-= thdlen
;
3142 ifield
= new_ifield(&tice
->ifield
);
3143 ifield
->ctype
= iMessNo
;
3144 ifield
->width
= thdlen
;
3145 ifield
->leftadj
= 0;
3146 ielem
= new_ielem(&ifield
->ielem
);
3147 ielem
->freedata
= 1;
3148 ielem
->data
= cpystr(p
);
3149 ielem
->datalen
= strlen(p
);
3150 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3153 if(space_left
>= 7){
3158 date_str(fetch_date(idata
), iDate
, 0, p
, sizeof(buffer
), 0);
3159 if(sizeof(buffer
) > 6)
3162 if(strlen(p
) < 6 && (sizeof(buffer
)) > 6){
3165 for(q
= p
+ strlen(p
); q
< p
+ 6; q
++)
3171 ifield
= new_ifield(&tice
->ifield
);
3172 ifield
->ctype
= iDate
;
3174 ifield
->leftadj
= 1;
3175 ielem
= new_ielem(&ifield
->ielem
);
3176 ielem
->freedata
= 1;
3177 ielem
->data
= cpystr(p
);
3178 ielem
->datalen
= ifield
->width
;
3179 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3184 int from_width
, subj_width
, bigthread_adjust
;
3186 char from
[BIGWIDTH
+1];
3191 in_thread
= count_lflags_in_thread(idata
->stream
, thrd
,
3192 ps_global
->msgmap
, MN_NONE
);
3195 if(in_thread
== 1 && THRD_AUTO_VIEW())
3196 snprintf(tcnt
, sizeof(tcnt
), " ");
3198 snprintf(tcnt
, sizeof(tcnt
), "(%ld)", in_thread
);
3200 bigthread_adjust
= MAX(0, strlen(tcnt
) - 3);
3202 /* third of the rest */
3203 from_width
= MAX((space_left
-1)/3 - bigthread_adjust
, 1);
3206 subj_width
= space_left
- from_width
- 1;
3208 if(strlen(tcnt
) > subj_width
)
3209 tcnt
[subj_width
] = '\0';
3212 save_sfstr_func
= pith_opt_truncate_sfstr
;
3213 pith_opt_truncate_sfstr
= NULL
;
3214 from_str(iFromTo
, idata
, from
, sizeof(from
), tice
);
3215 pith_opt_truncate_sfstr
= save_sfstr_func
;
3217 ifield
= new_ifield(&tice
->ifield
);
3218 ifield
->leftadj
= 1;
3219 ielem
= new_ielem(&ifield
->ielem
);
3220 ielem
->freedata
= 1;
3221 ielem
->type
= eTypeCol
;
3222 ielem
->data
= cpystr(from
);
3223 ielem
->datalen
= strlen(from
);
3224 ifield
->width
= from_width
;
3225 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3226 ifield
->ctype
= iFrom
;
3227 if(from_width
> 0 && pico_usingcolor()
3228 && VAR_IND_FROM_FORE_COLOR
&& VAR_IND_FROM_BACK_COLOR
){
3229 ielem
->freecolor
= 1;
3230 ielem
->color
= new_color_pair(VAR_IND_FROM_FORE_COLOR
,
3231 VAR_IND_FROM_BACK_COLOR
);
3234 ifield
= new_ifield(&tice
->ifield
);
3235 ifield
->leftadj
= 0;
3236 ielem
= new_ielem(&ifield
->ielem
);
3237 ielem
->freedata
= 1;
3238 ielem
->data
= cpystr(tcnt
);
3239 ielem
->datalen
= strlen(tcnt
);
3240 ifield
->width
= ielem
->datalen
;
3241 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3242 ifield
->ctype
= iAtt
; /* not used, except that it isn't special */
3244 subj_width
-= strlen(tcnt
);
3251 if(idata
->bogus
< 2)
3252 snprintf(buffer
, sizeof(buffer
), "%-.*s", BIGWIDTH
,
3253 _("[ No Message Text Available ]"));
3257 save_sfstr_func
= pith_opt_truncate_sfstr
;
3258 pith_opt_truncate_sfstr
= NULL
;
3259 subj_str(idata
, buffer
, sizeof(buffer
), NoKW
, 0, NULL
);
3260 pith_opt_truncate_sfstr
= save_sfstr_func
;
3263 ifield
= new_ifield(&tice
->ifield
);
3264 ifield
->leftadj
= 1;
3265 ielem
= new_ielem(&ifield
->ielem
);
3266 ielem
->freedata
= 1;
3267 ielem
->type
= eTypeCol
;
3268 ielem
->data
= cpystr(buffer
);
3269 ielem
->datalen
= strlen(buffer
);
3270 ifield
->width
= subj_width
;
3271 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3272 ifield
->ctype
= iSubject
;
3273 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR
&& VAR_IND_SUBJ_BACK_COLOR
){
3274 ielem
->freecolor
= 1;
3275 ielem
->color
= new_color_pair(VAR_IND_SUBJ_FORE_COLOR
,
3276 VAR_IND_SUBJ_BACK_COLOR
);
3280 else if(space_left
> 1){
3281 snprintf(p
, sizeof(buffer
)-(p
-buffer
), "%-.*s", space_left
-1, " ");
3282 ifield
= new_ifield(&tice
->ifield
);
3283 ifield
->leftadj
= 1;
3284 ielem
= new_ielem(&ifield
->ielem
);
3285 ielem
->freedata
= 1;
3286 ielem
->data
= cpystr(p
);
3287 ielem
->datalen
= strlen(p
);
3288 ifield
->width
= space_left
-1;
3289 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3290 ifield
->ctype
= iSubject
;
3293 tice
->widths_done
= 1;
3294 tice
->id
= ice_hash(tice
);
3297 free_ice(ticep
); /* free what is already there */
3306 * Print the fields of ice in buf with a single space between fields.
3308 * Args buf -- place to put the line
3309 * ice -- the data for the line
3310 * msgno -- this is the msgno to be used, blanks if <= 0
3312 * Returns a pointer to buf.
3315 simple_index_line(char *buf
, size_t buflen
, ICE_S
*ice
, long int msgno
)
3318 IFIELD_S
*ifield
, *previfield
= NULL
;
3322 panic("NULL buf in simple_index_line()");
3331 for(ifield
= ice
->ifield
; ifield
&& p
-buf
< buflen
; ifield
= ifield
->next
){
3333 /* space between fields */
3334 if(ifield
!= ice
->ifield
&& !(previfield
&& previfield
->ctype
== iText
))
3337 /* message number string is generated on the fly */
3338 if(ifield
->ctype
== iMessNo
){
3339 ielem
= ifield
->ielem
;
3340 if(ielem
&& ielem
->datalen
>= ifield
->width
){
3342 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.ld", ifield
->width
, msgno
);
3344 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.*s", ifield
->width
, ifield
->width
, "");
3348 for(ielem
= ifield
->ielem
;
3349 ielem
&& ielem
->print_format
&& p
-buf
< buflen
;
3350 ielem
= ielem
->next
){
3355 bytes_added
= utf8_pad_to_width(p
, src
,
3356 buflen
-(p
-buf
) * sizeof(char),
3357 ielem
->wid
, ifield
->leftadj
);
3361 previfield
= ifield
;
3368 buf
[buflen
-1] = '\0';
3375 * Look in current mail_stream for matches for messages in the searchset
3376 * which match a color rule pattern. Return the color.
3377 * The searched bit will be set for all of the messages which match the
3378 * first pattern which has a match.
3380 * Args stream -- the mail stream
3381 * searchset -- restrict attention to this set of messages
3382 * pstate -- The pattern state. On the first call it will be Null.
3383 * Null means start over with a new first_pattern.
3384 * After that it will be pointing to our local PAT_STATE
3385 * so that next_pattern goes to the next one after the
3386 * ones we've already checked.
3388 * Returns 0 if no match, 1 if a match.
3389 * The color that goes with the matched rule in returned_color.
3390 * It may be NULL, which indicates default.
3393 get_index_line_color(MAILSTREAM
*stream
, SEARCHSET
*searchset
,
3394 PAT_STATE
**pstate
, COLOR_PAIR
**returned_color
)
3397 long rflags
= ROLE_INCOL
;
3398 COLOR_PAIR
*color
= NULL
;
3400 static PAT_STATE localpstate
;
3402 dprint((7, "get_index_line_color\n"));
3405 *returned_color
= NULL
;
3408 pat
= next_pattern(*pstate
);
3410 *pstate
= &localpstate
;
3411 if(!nonempty_patterns(rflags
, *pstate
))
3415 pat
= first_pattern(*pstate
);
3420 /* Go through the possible roles one at a time until we get a match. */
3421 while(!match
&& pat
){
3422 if(match_pattern(pat
->patgrp
, stream
, searchset
, NULL
,
3423 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
3424 if(!pat
->action
|| pat
->action
->bogus
)
3428 if(pat
->action
&& pat
->action
->incol
)
3429 color
= new_color_pair(pat
->action
->incol
->fg
,
3430 pat
->action
->incol
->bg
);
3433 pat
= next_pattern(*pstate
);
3437 if(match
&& returned_color
)
3438 *returned_color
= color
;
3448 index_in_overview(MAILSTREAM
*stream
)
3450 INDEX_COL_S
*cdesc
= NULL
;
3452 if(!(stream
->mailbox
&& IS_REMOTE(stream
->mailbox
)))
3453 return(FALSE
); /* no point! */
3455 if(stream
->dtb
&& stream
->dtb
->name
&& !strcmp(stream
->dtb
->name
, "nntp")){
3460 for(cdesc
= ps_global
->index_disp_format
;
3461 cdesc
->ctype
!= iNothing
;
3463 switch(cdesc
->ctype
){
3464 case iTo
: /* can't be satisfied by XOVER */
3465 case iSender
: /* ... or specifically handled */
3466 case iDescripSize
: /* ... in news case */
3481 * fetch_from - called to get a the index entry's "From:" field
3484 resent_to_us(INDEXDATA_S
*idata
)
3486 if(!idata
->valid_resent_to
){
3487 static char *fields
[] = {"Resent-To", NULL
};
3490 if(idata
->no_fetch
){
3491 idata
->bogus
= 1; /* don't do this */
3495 if((h
= pine_fetchheader_lines(idata
->stream
,idata
->rawno
,NULL
,fields
)) != NULL
){
3496 idata
->resent_to_us
= parsed_resent_to_us(h
);
3497 fs_give((void **) &h
);
3500 idata
->valid_resent_to
= 1;
3503 return(idata
->resent_to_us
);
3508 parsed_resent_to_us(char *h
)
3511 ADDRESS
*addr
= NULL
;
3514 if((p
= strindex(h
, ':')) != NULL
){
3515 for(q
= ++p
; (q
= strpbrk(q
, "\015\012")) != NULL
; q
++)
3516 *q
= ' '; /* quash junk */
3518 rfc822_parse_adrlist(&addr
, p
, ps_global
->maildomain
);
3520 rv
= address_is_us(addr
, ps_global
);
3521 mail_free_address(&addr
);
3531 * fetch_from - called to get a the index entry's "From:" field
3534 fetch_from(INDEXDATA_S
*idata
)
3536 if(idata
->no_fetch
) /* implies from is valid */
3537 return(idata
->from
);
3538 else if(idata
->bogus
)
3543 /* c-client call's just cache access at this point */
3544 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3555 * fetch_to - called to get a the index entry's "To:" field
3558 fetch_to(INDEXDATA_S
*idata
)
3560 if(idata
->no_fetch
){ /* check for specific validity */
3564 idata
->bogus
= 1; /* can't give 'em what they want */
3566 else if(idata
->bogus
){
3567 idata
->bogus
= 2; /* elevate bogosity */
3572 /* c-client call's just cache access at this point */
3573 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3584 * fetch_cc - called to get a the index entry's "Cc:" field
3587 fetch_cc(INDEXDATA_S
*idata
)
3589 if(idata
->no_fetch
){ /* check for specific validity */
3593 idata
->bogus
= 1; /* can't give 'em what they want */
3595 else if(idata
->bogus
){
3596 idata
->bogus
= 2; /* elevate bogosity */
3601 /* c-client call's just cache access at this point */
3602 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3614 * fetch_sender - called to get a the index entry's "Sender:" field
3617 fetch_sender(INDEXDATA_S
*idata
)
3619 if(idata
->no_fetch
){ /* check for specific validity */
3620 if(idata
->valid_sender
)
3621 return(idata
->sender
);
3623 idata
->bogus
= 1; /* can't give 'em what they want */
3625 else if(idata
->bogus
){
3626 idata
->bogus
= 2; /* elevate bogosity */
3631 /* c-client call's just cache access at this point */
3632 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3633 return(env
->sender
);
3643 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3646 fetch_newsgroups(INDEXDATA_S
*idata
)
3648 if(idata
->no_fetch
){ /* check for specific validity */
3649 if(idata
->valid_news
)
3650 return(idata
->newsgroups
);
3652 idata
->bogus
= 1; /* can't give 'em what they want */
3654 else if(idata
->bogus
){
3655 idata
->bogus
= 2; /* elevate bogosity */
3660 /* c-client call's just cache access at this point */
3661 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3662 return(env
->newsgroups
);
3672 * fetch_subject - called to get at the index entry's "Subject:" field
3675 fetch_subject(INDEXDATA_S
*idata
)
3677 if(idata
->no_fetch
) /* implies subject is valid */
3678 return(idata
->subject
);
3679 else if(idata
->bogus
)
3684 /* c-client call's just cache access at this point */
3685 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3686 return(env
->subject
);
3696 * Return an allocated copy of the first few characters from the body
3697 * of the message for possible use in the index screen.
3699 * Maybe we could figure out some way to do aggregate calls to get
3700 * this info for all the lines in view instead of all the one at a
3701 * time calls we're doing now.
3704 fetch_firsttext(INDEXDATA_S
*idata
, int delete_quotes
)
3708 char *firsttext
= NULL
;
3711 long partial_fetch_len
= 0L;
3712 SEARCHSET
*ss
, **sset
;
3717 * Prevent wild prefetch, just get the one we're after.
3718 * Can we get this somehow in the overview call in build_header_work?
3720 ss
= mail_newsearchset();
3721 ss
->first
= idata
->rawno
;
3722 sset
= (SEARCHSET
**) mail_parameters(idata
->stream
,
3724 (void *) idata
->stream
);
3728 if((env
= pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
)) != NULL
){
3730 char *subtype
= NULL
;
3733 if((body
->type
== TYPETEXT
3734 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3736 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3737 && body
->nested
.part
->body
.type
== TYPETEXT
3738 && (subtype
=body
->nested
.part
->body
.subtype
)
3739 && ALLOWED_SUBTYPE(subtype
))
3741 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3742 && body
->nested
.part
->body
.type
== TYPEMULTIPART
3743 && body
->nested
.part
->body
.nested
.part
3744 && body
->nested
.part
->body
.nested
.part
->body
.type
== TYPETEXT
3745 && (subtype
=body
->nested
.part
->body
.nested
.part
->body
.subtype
)
3746 && ALLOWED_SUBTYPE(subtype
))){
3748 if((so
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
3752 int one_space_done
= 0;
3754 if(partial_fetch_len
== 0L){
3755 if(subtype
&& !strucmp(subtype
, "html"))
3756 partial_fetch_len
= 1024L;
3757 else if(subtype
&& !strucmp(subtype
, "plain"))
3758 partial_fetch_len
= delete_quotes
? 128L : 64L;
3760 partial_fetch_len
= 256L;
3763 if((body
->type
== TYPETEXT
3764 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3766 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3767 && body
->nested
.part
->body
.type
== TYPETEXT
3768 && (subtype
=body
->nested
.part
->body
.subtype
)
3769 && ALLOWED_SUBTYPE(subtype
)))
3774 gf_set_so_writec(&pc
, so
);
3775 success
= get_body_part_text(idata
->stream
, body
, idata
->rawno
,
3776 partno
, partial_fetch_len
, pc
,
3778 GBPT_NOINTR
| GBPT_PEEK
|
3779 (delete_quotes
? GBPT_DELQUOTES
: 0));
3780 gf_clear_so_writec(so
);
3785 while(p
-buf
< sizeof(buf
)-1 && so_readc(&c
, so
)){
3786 /* delete leading whitespace */
3787 if(p
== buf
&& isspace(c
))
3789 /* and include just one space per run of whitespace */
3790 else if(isspace(c
)){
3791 if(!one_space_done
){
3809 firsttext
= fs_get((l
+1) * sizeof(char));
3810 firsttext
[0] = '\0';
3811 iutf8ncpy(firsttext
, buf
, l
);
3812 firsttext
[l
] = '\0';
3813 removing_trailing_white_space(firsttext
);
3819 /* first if means we didn't fetch all of the data */
3820 if(!(success
> 1 && success
< partial_fetch_len
)){
3821 if(partial_fetch_len
< 4096L
3822 && (!firsttext
|| utf8_width(firsttext
) < 50)){
3824 fs_give((void **) &firsttext
);
3826 partial_fetch_len
= 4096L;
3836 mail_free_searchset(&ss
);
3843 * fetch_date - called to get at the index entry's "Date:" field
3846 fetch_date(INDEXDATA_S
*idata
)
3848 if(idata
->no_fetch
) /* implies date is valid */
3849 return(idata
->date
);
3850 else if(idata
->bogus
)
3855 /* c-client call's just cache access at this point */
3856 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3857 return((char *) env
->date
);
3867 * fetch_header - called to get at the index entry's "Hdrname:" field
3870 fetch_header(INDEXDATA_S
*idata
, char *hdrname
)
3874 else if(idata
->bogus
)
3877 char *h
, *p
, *q
, *decoded
, *fields
[2];
3878 size_t retsize
, decsize
;
3880 unsigned char *decode_buf
= NULL
;
3882 fields
[0] = hdrname
;
3884 if(hdrname
&& hdrname
[0]
3885 && (h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
3888 if(strlen(h
) < strlen(hdrname
) + 1){
3889 fs_give((void **) &h
);
3893 /* skip "hdrname:" */
3894 for(p
= h
+ strlen(hdrname
) + 1;
3895 *p
&& isspace((unsigned char)*p
); p
++)
3898 decsize
= (4 * strlen(p
)) + 1;
3899 decode_buf
= (unsigned char *) fs_get(decsize
* sizeof(unsigned char));
3900 decoded
= (char *) rfc1522_decode_to_utf8(decode_buf
, decsize
, p
);
3903 retsize
= strlen(decoded
);
3904 q
= ret
= (char *) fs_get((retsize
+1) * sizeof(char));
3907 while(q
-ret
< retsize
&& *p
){
3908 if(*p
== '\015' || *p
== '\012')
3910 else if(*p
== '\t'){
3920 fs_give((void **) &h
);
3922 fs_give((void **) &decode_buf
);
3935 * fetch_size - called to get at the index entry's "size" field
3938 fetch_size(INDEXDATA_S
*idata
)
3940 if(idata
->no_fetch
) /* implies size is valid */
3941 return(idata
->size
);
3942 else if(idata
->bogus
)
3947 if(idata
->stream
&& idata
->rawno
> 0L
3948 && idata
->rawno
<= idata
->stream
->nmsgs
3949 && (mc
= mail_elt(idata
->stream
, idata
->rawno
)))
3950 return(mc
->rfc822_size
);
3960 * fetch_body - called to get a the index entry's body structure
3963 fetch_body(INDEXDATA_S
*idata
)
3967 if(idata
->bogus
|| idata
->no_fetch
){
3972 if(pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
))
3981 * s is at least size width+1
3984 set_index_addr(INDEXDATA_S
*idata
,
3986 struct mail_address
*addr
,
3992 char *p
, *stmp
= NULL
, *sptr
;
3993 char *save_personal
= NULL
;
3998 for(atmp
= addr
; idata
->stream
&& atmp
; atmp
= atmp
->next
)
3999 if(atmp
->host
&& atmp
->host
[0] == '.'){
4000 char *pref
, *h
, *fields
[2];
4002 if(idata
->no_fetch
){
4009 if((h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
4010 NULL
, fields
)) != NULL
){
4011 if(strlen(h
) < strlen(field
) + 1){
4016 for(p
= h
+ strlen(field
) + 1;
4017 *p
&& isspace((unsigned char)*p
); p
++)
4022 sptr
= stmp
= (char *) fs_get((orig_width
+1) * sizeof(char));
4025 for(pref
= prefix
; pref
&& *pref
; pref
++)
4034 if(*p
== '\015' || *p
== '\012')
4035 p
++; /* skip CR LF */
4038 else if(*p
== '\t'){
4045 *sptr
= '\0'; /* tie off return string */
4048 iutf8ncpy(s
, stmp
, orig_width
+1);
4049 s
[orig_width
] = '\0';
4050 fs_give((void **) &stmp
);
4053 fs_give((void **) &h
);
4056 /* else fall thru and display what c-client gave us */
4059 if(addr
&& !addr
->next
/* only one address */
4060 && addr
->host
/* not group syntax */
4061 && addr
->personal
&& addr
->personal
[0]){ /* there is a personal name */
4062 char buftmp
[MAILTMPLEN
];
4065 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4066 strncpy(s
, prefix
, width
+1);
4068 snprintf(buftmp
, sizeof(buftmp
), "%s", addr
->personal
);
4069 p
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
4070 SIZEOF_20KBUF
, buftmp
);
4071 removing_leading_and_trailing_white_space(p
);
4073 iutf8ncpy(s
+ l
, p
, width
- l
);
4080 save_personal
= addr
->personal
;
4081 addr
->personal
= NULL
;
4089 a_string
= addr_list_string(addr
, NULL
, 0);
4091 addr
->personal
= save_personal
;
4093 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4094 strncpy(s
, prefix
, width
+1);
4096 iutf8ncpy(s
+ l
, a_string
, width
- l
);
4099 fs_give((void **)&a_string
);
4105 addr
->personal
= save_personal
;
4112 index_data_env(INDEXDATA_S
*idata
, ENVELOPE
*env
)
4119 idata
->from
= env
->from
;
4120 idata
->to
= env
->to
;
4121 idata
->cc
= env
->cc
;
4122 idata
->sender
= env
->sender
;
4123 idata
->subject
= env
->subject
;
4124 idata
->date
= (char *) env
->date
;
4125 idata
->newsgroups
= env
->newsgroups
;
4127 idata
->valid_to
= 1; /* signal that everythings here */
4128 idata
->valid_cc
= 1;
4129 idata
->valid_sender
= 1;
4130 idata
->valid_news
= 1;
4135 * Put a string representing the date into str. The source date is
4136 * in the string datesrc. The format to be used is in type.
4137 * Notice that type is an IndexColType, but really only a subset of
4138 * IndexColType types are allowed.
4140 * Args datesrc -- The source date string
4141 * type -- What type of output we want
4142 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4143 * str -- Put the answer here.
4144 * str_len -- Length of str
4145 * monabb_width -- This is a hack to get dates to line up right. For
4146 * example, in French (but without accents here)
4150 * For this monabb_width would be 5.
4153 date_str(char *datesrc
, IndexColType type
, int v
, char *str
, size_t str_len
,
4156 char year4
[5], /* 4 digit year */
4157 yearzero
[3], /* zero padded, 2-digit year */
4158 monzero
[3], /* zero padded, 2-digit month */
4159 mon
[3], /* 1 or 2-digit month, no pad */
4160 dayzero
[3], /* zero padded, 2-digit day */
4161 day
[3], /* 1 or 2-digit day, no pad */
4162 dayord
[3], /* 2-letter ordinal label */
4163 monabb
[10], /* 3-letter month abbrev */
4164 /* actually maybe not 3 if localized */
4165 hour24
[3], /* 2-digit, 24 hour clock hour */
4166 hour12
[3], /* 12 hour clock hour, no pad */
4167 minzero
[3], /* zero padded, 2-digit minutes */
4168 timezone
[6]; /* timezone, like -0800 or +... */
4170 int curtype
, lastmonthtype
, lastyeartype
, preftype
;
4171 int sdatetimetype
, sdatetime24type
;
4173 #define TODAYSTR N_("Today")
4175 curtype
= (type
== iCurDate
||
4176 type
== iCurDateIso
||
4177 type
== iCurDateIsoS
||
4178 type
== iCurPrefDate
||
4179 type
== iCurPrefDateTime
||
4180 type
== iCurPrefTime
||
4181 type
== iCurTime24
||
4182 type
== iCurTime12
||
4184 type
== iCurDay2Digit
||
4185 type
== iCurDayOfWeek
||
4186 type
== iCurDayOfWeekAbb
||
4188 type
== iCurMon2Digit
||
4189 type
== iCurMonLong
||
4190 type
== iCurMonAbb
||
4192 type
== iCurYear2Digit
);
4193 lastmonthtype
= (type
== iLstMon
||
4194 type
== iLstMon2Digit
||
4195 type
== iLstMonLong
||
4196 type
== iLstMonAbb
||
4197 type
== iLstMonYear
||
4198 type
== iLstMonYear2Digit
);
4199 lastyeartype
= (type
== iLstYear
||
4200 type
== iLstYear2Digit
);
4201 sdatetimetype
= (type
== iSDateTime
||
4202 type
== iSDateTimeIso
||
4203 type
== iSDateTimeIsoS
||
4204 type
== iSDateTimeS1
||
4205 type
== iSDateTimeS2
||
4206 type
== iSDateTimeS3
||
4207 type
== iSDateTimeS4
||
4208 type
== iSDateTime24
||
4209 type
== iSDateTimeIso24
||
4210 type
== iSDateTimeIsoS24
||
4211 type
== iSDateTimeS124
||
4212 type
== iSDateTimeS224
||
4213 type
== iSDateTimeS324
||
4214 type
== iSDateTimeS424
);
4215 sdatetime24type
= (type
== iSDateTime24
||
4216 type
== iSDateTimeIso24
||
4217 type
== iSDateTimeIsoS24
||
4218 type
== iSDateTimeS124
||
4219 type
== iSDateTimeS224
||
4220 type
== iSDateTimeS324
||
4221 type
== iSDateTimeS424
);
4222 preftype
= (type
== iPrefDate
||
4223 type
== iPrefDateTime
||
4224 type
== iPrefTime
||
4225 type
== iCurPrefDate
||
4226 type
== iCurPrefDateTime
||
4227 type
== iCurPrefTime
);
4231 if(!(datesrc
&& datesrc
[0]) && !(curtype
|| lastmonthtype
|| lastyeartype
))
4234 if(curtype
|| lastmonthtype
|| lastyeartype
){
4238 parse_date(dbuf
, &d
);
4242 else if(lastmonthtype
){
4251 parse_date(F_ON(F_DATES_TO_LOCAL
,ps_global
)
4252 ? convert_date_to_local(datesrc
) : datesrc
, &d
);
4253 if(d
.year
== -1 || d
.month
== -1 || d
.day
== -1){
4255 sdatetime24type
= 0;
4258 case iSDate
: case iSDateTime
: case iSDateTime24
:
4262 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4263 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
4267 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4271 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4275 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4279 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4283 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4293 /* some special ones to start with */
4295 struct tm tm
, *tmptr
= NULL
;
4299 * Make sure we get the right one if we're using current time.
4302 now
= time((time_t *) 0);
4303 if(now
!= (time_t) -1)
4304 tmptr
= localtime(&now
);
4308 memset(&tm
, 0, sizeof(tm
));
4309 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4310 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4311 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4312 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4313 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4314 tm
.tm_wday
= MIN(MAX(d
.wkday
, 0), 6);
4321 our_strftime(str
, str_len
, "%x", tmptr
);
4325 our_strftime(str
, str_len
, "%X", tmptr
);
4328 case iCurPrefDateTime
:
4329 our_strftime(str
, str_len
, "%c", tmptr
);
4339 strncpy(monabb
, (d
.month
> 0 && d
.month
< 13)
4340 ? month_abbrev_locale(d
.month
) : "", sizeof(monabb
));
4341 monabb
[sizeof(monabb
)-1] = '\0';
4343 strncpy(mon
, (d
.month
> 0 && d
.month
< 13)
4344 ? int2string(d
.month
) : "", sizeof(mon
));
4345 mon
[sizeof(mon
)-1] = '\0';
4347 strncpy(day
, (d
.day
> 0 && d
.day
< 32)
4348 ? int2string(d
.day
) : "", sizeof(day
));
4349 day
[sizeof(day
)-1] = '\0';
4352 (d
.day
<= 0 || d
.day
> 31) ? "" :
4353 (d
.day
== 1 || d
.day
== 21 || d
.day
== 31) ? "st" :
4354 (d
.day
== 2 || d
.day
== 22 ) ? "nd" :
4355 (d
.day
== 3 || d
.day
== 23 ) ? "rd" : "th", sizeof(dayord
));
4357 dayord
[sizeof(dayord
)-1] = '\0';
4359 strncpy(year4
, (d
.year
>= 1000 && d
.year
< 10000)
4360 ? int2string(d
.year
) : "????", sizeof(year4
));
4361 year4
[sizeof(year4
)-1] = '\0';
4364 if((d
.year
% 100) < 10){
4366 strncpy(yearzero
+1, int2string(d
.year
% 100), sizeof(yearzero
)-1);
4369 strncpy(yearzero
, int2string(d
.year
% 100), sizeof(yearzero
));
4372 strncpy(yearzero
, "??", sizeof(yearzero
));
4374 yearzero
[sizeof(yearzero
)-1] = '\0';
4376 if(d
.month
> 0 && d
.month
< 10){
4378 strncpy(monzero
+1, int2string(d
.month
), sizeof(monzero
)-1);
4380 else if(d
.month
>= 10 && d
.month
<= 12)
4381 strncpy(monzero
, int2string(d
.month
), sizeof(monzero
));
4383 strncpy(monzero
, "??", sizeof(monzero
));
4385 monzero
[sizeof(monzero
)-1] = '\0';
4387 if(d
.day
> 0 && d
.day
< 10){
4389 strncpy(dayzero
+1, int2string(d
.day
), sizeof(dayzero
)-1);
4391 else if(d
.day
>= 10 && d
.day
<= 31)
4392 strncpy(dayzero
, int2string(d
.day
), sizeof(dayzero
));
4394 strncpy(dayzero
, "??", sizeof(dayzero
));
4396 dayzero
[sizeof(dayzero
)-1] = '\0';
4398 hr12
= (d
.hour
== 0) ? 12 :
4399 (d
.hour
> 12) ? (d
.hour
- 12) : d
.hour
;
4401 if(hr12
> 0 && hr12
<= 12)
4402 strncpy(hour12
, int2string(hr12
), sizeof(hour12
));
4404 hour12
[sizeof(hour12
)-1] = '\0';
4407 if(d
.hour
>= 0 && d
.hour
< 10){
4409 strncpy(hour24
+1, int2string(d
.hour
), sizeof(hour24
)-1);
4411 else if(d
.hour
>= 10 && d
.hour
< 24)
4412 strncpy(hour24
, int2string(d
.hour
), sizeof(hour24
));
4414 hour24
[sizeof(hour24
)-1] = '\0';
4417 if(d
.minute
>= 0 && d
.minute
< 10){
4419 strncpy(minzero
+1, int2string(d
.minute
), sizeof(minzero
)-1);
4421 else if(d
.minute
>= 10 && d
.minute
<= 60)
4422 strncpy(minzero
, int2string(d
.minute
), sizeof(minzero
));
4424 minzero
[sizeof(minzero
)-1] = '\0';
4426 if(sizeof(timezone
) > 5){
4427 if(d
.hours_off_gmt
<= 0){
4429 d
.hours_off_gmt
*= -1;
4430 d
.min_off_gmt
*= -1;
4436 if(d
.hours_off_gmt
>= 0 && d
.hours_off_gmt
< 10){
4438 strncpy(timezone
+2, int2string(d
.hours_off_gmt
), sizeof(timezone
)-2);
4440 else if(d
.hours_off_gmt
>= 10 && d
.hours_off_gmt
< 24)
4441 strncpy(timezone
+1, int2string(d
.hours_off_gmt
), sizeof(timezone
)-1);
4448 if(d
.min_off_gmt
>= 0 && d
.min_off_gmt
< 10){
4450 strncpy(timezone
+4, int2string(d
.min_off_gmt
), sizeof(timezone
)-4);
4452 else if(d
.min_off_gmt
>= 10 && d
.min_off_gmt
<= 60)
4453 strncpy(timezone
+3, int2string(d
.min_off_gmt
), sizeof(timezone
)-3);
4460 timezone
[sizeof(timezone
)-1] = '\0';
4465 /* this one is not locale-specific */
4466 snprintf(str
, str_len
, "%s%s%s %s %s",
4467 (d
.wkday
!= -1) ? day_abbrev(d
.wkday
) : "",
4468 (d
.wkday
!= -1) ? ", " : "",
4470 (d
.month
> 0 && d
.month
< 13) ? month_abbrev(d
.month
) : "",
4474 case iCurDayOfWeekAbb
:
4475 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_abbrev_locale(d
.wkday
) : "", str_len
);
4476 str
[str_len
-1] = '\0';
4480 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_name_locale(d
.wkday
) : "", str_len
);
4481 str
[str_len
-1] = '\0';
4487 strncpy(str
, year4
, str_len
);
4491 strncpy(str
, dayzero
, str_len
);
4496 strncpy(str
, monzero
, str_len
);
4499 case iCurYear2Digit
:
4500 case iLstYear2Digit
:
4501 case iLstMonYear2Digit
:
4502 strncpy(str
, yearzero
, str_len
);
4505 strncpy(str
, timezone
, str_len
);
4509 strncpy(str
, day
, str_len
);
4512 snprintf(str
, str_len
, "%s%s", day
, dayord
);
4517 if(d
.month
> 0 && d
.month
<= 12)
4518 strncpy(str
, int2string(d
.month
), str_len
);
4524 strncpy(str
, monabb
, str_len
);
4529 strncpy(str
, (d
.month
> 0 && d
.month
< 13)
4530 ? month_name_locale(d
.month
) : "", str_len
);
4535 snprintf(str
, str_len
, "%s%s%s", monabb
, (monabb
[0] && day
[0]) ? " " : "", day
);
4537 if(monabb_width
> 0)
4538 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4539 monabb_width
, monabb_width
, monabb
, day
);
4541 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4547 snprintf(str
, str_len
, "%s%s%s%s%s", monabb
,
4548 (monabb
[0] && day
[0]) ? " " : "", day
,
4549 ((monabb
[0] || day
[0]) && year4
[0]) ? ", " : "",
4552 if(monabb_width
> 0)
4553 utf8_snprintf(str
, str_len
, "%-*.*w %2s%c %4s",
4554 monabb_width
, monabb_width
,
4556 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ', year4
);
4558 snprintf(str
, str_len
, "%s %2s%c %4s", monabb
, day
,
4559 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ',
4572 if(monzero
[0] == '?' && dayzero
[0] == '?' &&
4574 snprintf(str
, str_len
, "%8s", "");
4578 snprintf(str
, str_len
, "%2s/%2s/%2s",
4579 monzero
, dayzero
, yearzero
);
4582 snprintf(str
, str_len
, "%2s/%2s/%2s",
4583 dayzero
, monzero
, yearzero
);
4586 snprintf(str
, str_len
, "%2s.%2s.%2s",
4587 dayzero
, monzero
, yearzero
);
4590 snprintf(str
, str_len
, "%2s.%2s.%2s",
4591 yearzero
, monzero
, dayzero
);
4595 snprintf(str
, str_len
, "%2s-%2s-%2s",
4596 yearzero
, monzero
, dayzero
);
4600 snprintf(str
, str_len
, "%4s-%2s-%2s",
4601 year4
, monzero
, dayzero
);
4611 snprintf(str
, str_len
, "%2s%c%2s",
4612 (hour24
[0] && minzero
[0]) ? hour24
: "",
4613 (hour24
[0] && minzero
[0]) ? ':' : ' ',
4614 (hour24
[0] && minzero
[0]) ? minzero
: "");
4618 snprintf(str
, str_len
, "%s%c%2s%s",
4619 (hour12
[0] && minzero
[0]) ? hour12
: "",
4620 (hour12
[0] && minzero
[0]) ? ':' : ' ',
4621 (hour12
[0] && minzero
[0]) ? minzero
: "",
4622 (hour12
[0] && minzero
[0] && d
.hour
< 12) ? "am" :
4623 (hour12
[0] && minzero
[0] && d
.hour
>= 12) ? "pm" :
4626 case iSDate
: case iSDateIso
: case iSDateIsoS
:
4627 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
4628 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
4629 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
4630 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
4631 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
4632 { struct date now
, last_day
;
4634 int msg_day_of_year
, now_day_of_year
, today
;
4635 int diff
, ydiff
, last_day_of_year
;
4638 parse_date(dbuf
, &now
);
4639 today
= day_of_week(&now
) + 7;
4641 if(today
>= 0+7 && today
<= 6+7){
4642 now_day_of_year
= day_of_year(&now
);
4643 msg_day_of_year
= day_of_year(&d
);
4644 ydiff
= now
.year
- d
.year
;
4646 if(msg_day_of_year
== -1)
4649 diff
= now_day_of_year
- msg_day_of_year
;
4650 else if(ydiff
== 1){
4652 last_day
.month
= 12;
4654 last_day_of_year
= day_of_year(&last_day
);
4656 diff
= now_day_of_year
+
4657 (last_day_of_year
- msg_day_of_year
);
4659 else if(ydiff
== -1){
4661 last_day
.month
= 12;
4663 last_day_of_year
= day_of_year(&last_day
);
4665 diff
= -1 * (msg_day_of_year
+
4666 (last_day_of_year
- now_day_of_year
));
4674 strncpy(str
, _(TODAYSTR
), str_len
);
4676 strncpy(str
, _("Yesterday"), str_len
);
4677 else if(diff
> 1 && diff
< 7)
4678 snprintf(str
, str_len
, "%s", day_name_locale((today
- diff
) % 7));
4680 strncpy(str
, _("Tomorrow"), str_len
);
4681 else if(diff
< -1 && diff
> -7)
4682 snprintf(str
, str_len
, _("Next %.3s!"),
4683 day_name_locale((today
- diff
) % 7));
4686 || (ydiff
== 1 && 12 + now
.month
- d
.month
< 6))){
4688 snprintf(str
, str_len
, "%s%s%s", monabb
,
4689 (monabb
[0] && day
[0]) ? " " : "", day
);
4691 if(monabb_width
> 0)
4692 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4693 monabb_width
, monabb_width
, monabb
, day
);
4695 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4699 if(msg_day_of_year
== -1 && (type
== iSDate
|| type
== iSDateTime
))
4700 type
= iSDateTimeIsoS
;
4703 case iSDate
: case iSDateTime
: case iSDateTime24
:
4707 memset(&tm
, 0, sizeof(tm
));
4708 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4709 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4710 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4711 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4712 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4713 our_strftime(str
, str_len
, "%x", &tm
);
4717 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4719 snprintf(str
, str_len
, "%s/%s/%s%s", mon
, day
, yearzero
,
4720 diff
< 0 ? "!" : "");
4722 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4723 (mon
[0] && mon
[1]) ? "" : " ",
4724 mon
, dayzero
, yearzero
,
4725 diff
< 0 ? "!" : "");
4727 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4729 snprintf(str
, str_len
, "%s/%s/%s%s", day
, mon
, yearzero
,
4730 diff
< 0 ? "!" : "");
4732 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4733 (day
[0] && day
[1]) ? "" : " ",
4734 day
, monzero
, yearzero
,
4735 diff
< 0 ? "!" : "");
4737 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4739 snprintf(str
, str_len
, "%s.%s.%s%s", day
, mon
, yearzero
,
4740 diff
< 0 ? "!" : "");
4742 snprintf(str
, str_len
, "%s%s.%s.%s%s",
4743 (day
[0] && day
[1]) ? "" : " ",
4744 day
, monzero
, yearzero
,
4745 diff
< 0 ? "!" : "");
4747 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4749 snprintf(str
, str_len
, "%s.%s.%s%s",
4750 yearzero
, monzero
, dayzero
,
4751 diff
< 0 ? "!" : "");
4753 snprintf(str
, str_len
, "%s.%s.%s%s",
4754 yearzero
, monzero
, dayzero
,
4755 diff
< 0 ? "!" : "");
4757 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4758 snprintf(str
, str_len
, "%2s-%2s-%2s",
4759 yearzero
, monzero
, dayzero
);
4761 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4762 snprintf(str
, str_len
, "%4s-%2s-%2s",
4763 year4
, monzero
, dayzero
);
4773 snprintf(str
, str_len
, "%s%s%s", monabb
,
4774 (monabb
[0] && day
[0]) ? " " : "", day
);
4776 if(monabb_width
> 0)
4777 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4778 monabb_width
, monabb_width
, monabb
, day
);
4780 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4791 str
[str_len
-1] = '\0';
4793 if(type
== iSTime
||
4794 (sdatetimetype
&& !strcmp(str
, _(TODAYSTR
)))){
4795 struct date now
, last_day
;
4796 char dbuf
[200], *Ddd
, *ampm
;
4801 parse_date(dbuf
, &now
);
4803 /* Figure out if message date lands in the past week */
4805 /* (if message dated this month or last month...) */
4806 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 1) ||
4807 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 1)){
4809 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4812 * If msg in end of last year (and we're in first bit of "this"
4813 * year), diff will be backwards; fix up by adding number of days
4814 * in last year (usually 365, but occasionally 366)...
4816 if(d
.year
== now
.year
- 1){
4818 last_day
.month
= 12;
4821 daydiff
+= day_of_year(&last_day
);
4825 daydiff
= -100; /* comfortably out of range (of past week) */
4827 /* Build 2-digit hour and am/pm indicator, used below */
4829 if(d
.hour
>= 0 && d
.hour
< 24){
4830 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4831 ampm
= (d
.hour
< 12) ? "am" : "pm";
4832 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4835 strncpy(hour12
, "??", sizeof(hour12
));
4836 hour12
[sizeof(hour12
)-1] = '\0';
4838 strncpy(hour24
, "??", sizeof(hour24
));
4839 hour24
[sizeof(hour24
)-1] = '\0';
4842 /* Build date/time in str, in format similar to that used by w(1) */
4844 if(daydiff
== 0){ /* If date is today, "HH:MMap" */
4845 if(d
.minute
>= 0 && d
.minute
< 60)
4846 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4848 strncpy(minzero
, "??", sizeof(minzero
));
4849 minzero
[sizeof(minzero
)-1] = '\0';
4852 snprintf(str
, str_len
, "%s:%s%s", sdatetime24type
? hour24
: hour12
,
4853 minzero
, sdatetime24type
? "" : ampm
);
4855 else if(daydiff
>= 1 && daydiff
< 6){ /* If <1wk ago, "DddHHap" */
4857 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4858 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4859 Ddd
= day_abbrev_locale(day_of_week(&d
));
4863 snprintf(str
, str_len
, "%s%s%s", Ddd
, hour12
, ampm
);
4865 else{ /* date is old or future, "ddMmmyy" */
4866 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4867 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4868 monabb
[sizeof(monabb
)-1] = '\0';
4870 if(d
.day
>= 1 && d
.day
<= 31)
4871 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
4873 strncpy(dayzero
, "??", sizeof(dayzero
));
4874 dayzero
[sizeof(dayzero
)-1] = '\0';
4877 if(d
.year
>= 0 && d
.year
<= 9999)
4878 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
4880 strncpy(yearzero
, "??", sizeof(yearzero
));
4881 yearzero
[sizeof(yearzero
)-1] = '\0';
4884 snprintf(str
, str_len
, "%s%s%s", dayzero
, monabb
, yearzero
);
4887 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4889 memmove(str
, str
+ 1, strlen(str
));
4898 * Format a string representing the keywords into ice.
4900 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
4902 * Args idata -- which message?
4903 * kwtype -- keywords or kw initials
4904 * ice -- index cache entry for message
4907 key_str(INDEXDATA_S
*idata
, SubjKW kwtype
, ICE_S
*ice
)
4912 COLOR_PAIR
*color
= NULL
;
4913 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
4914 IELEM_S
*ielem
= NULL
;
4915 IFIELD_S
*ourifield
= NULL
;
4917 if(ice
&& ice
->ifield
){
4918 /* move to last ifield, the one we're working */
4919 for(ourifield
= ice
->ifield
;
4920 ourifield
&& ourifield
->next
;
4921 ourifield
= ourifield
->next
)
4928 if(kwtype
== KWInit
){
4929 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4930 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4931 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
4932 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
4935 * Pick off the first initial. Since word is UTF-8 it may
4936 * take more than one byte for the first initial.
4939 if(word
&& word
[0]){
4941 unsigned long remaining_octets
;
4942 unsigned char *inputp
;
4944 remaining_octets
= strlen(word
);
4945 inputp
= (unsigned char *) word
;
4946 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
4947 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
4948 ielem
= new_ielem(&ourifield
->ielem
);
4949 ielem
->freedata
= 1;
4950 ielem
->datalen
= (unsigned) (inputp
- (unsigned char *) word
);
4951 ielem
->data
= (char *) fs_get((ielem
->datalen
+ 1) * sizeof(char));
4952 strncpy(ielem
->data
, word
, ielem
->datalen
);
4953 ielem
->data
[ielem
->datalen
] = '\0';
4955 if(pico_usingcolor()
4956 && ((kw
->nick
&& kw
->nick
[0]
4957 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
4958 || (kw
->kw
&& kw
->kw
[0]
4959 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
4960 ielem
->color
= color
;
4967 free_color_pair(&color
);
4971 else if(kwtype
== KW
){
4972 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4973 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4976 ielem
= new_ielem(&ourifield
->ielem
);
4977 ielem
->freedata
= 1;
4978 ielem
->data
= cpystr(" ");
4984 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
4985 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
4988 ielem
= new_ielem(&ourifield
->ielem
);
4989 ielem
->freedata
= 1;
4990 ielem
->data
= cpystr(word
);
4991 ielem
->datalen
= strlen(word
);
4993 if(pico_usingcolor()
4994 && ((kw
->nick
&& kw
->nick
[0]
4995 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
4996 || (kw
->kw
&& kw
->kw
[0]
4997 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
4998 ielem
->color
= color
;
5004 free_color_pair(&color
);
5010 * If we're coloring some of the fields then add a dummy field
5011 * at the end that can soak up the rest of the space after the last
5012 * colored keyword. Otherwise, the last one's color will extend to
5013 * the end of the field.
5015 if(pico_usingcolor()){
5016 ielem
= new_ielem(&ourifield
->ielem
);
5017 ielem
->freedata
= 1;
5018 ielem
->data
= cpystr(" ");
5022 ourifield
->leftadj
= 1;
5023 set_ielem_widths_in_field(ourifield
);
5028 prio_str(INDEXDATA_S
*idata
, IndexColType ctype
, ICE_S
*ice
)
5030 IFIELD_S
*ourifield
= NULL
;
5031 IELEM_S
*ielem
= NULL
;
5036 if(ice
&& ice
->ifield
){
5037 /* move to last ifield, the one we're working */
5038 for(ourifield
= ice
->ifield
;
5039 ourifield
&& ourifield
->next
;
5040 ourifield
= ourifield
->next
)
5047 hdrval
= fetch_header(idata
, PRIORITYNAME
);
5049 if(hdrval
&& hdrval
[0] && isdigit(hdrval
[0])){
5051 if(v
>= 1 && v
<= 5 && v
!= 3){
5053 ielem
= new_ielem(&ourifield
->ielem
);
5054 ielem
->freedata
= 1;
5058 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5059 ielem
->data
[0] = hdrval
[0];
5060 ielem
->data
[1] = '\0';
5064 for(p
= priorities
; p
&& p
->desc
; p
++)
5069 ielem
->data
= cpystr(p
->desc
);
5074 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5075 ielem
->data
[0] = '\0';
5078 ielem
->data
[0] = '!';
5083 * We could put a Unicode downarrow in here but
5084 * we have no way of knowing if the user's font
5085 * will have it (I think).
5087 ielem
->data
[0] = 'v';
5091 ielem
->data
[1] = '\0';
5096 panic("Unhandled case in prio_str");
5101 ielem
->data
= cpystr("");
5103 if(ielem
&& ielem
->data
)
5104 ielem
->datalen
= strlen(ielem
->data
);
5106 if((v
== 1 || v
== 2) && pico_usingcolor()
5107 && ps_global
->VAR_IND_HIPRI_FORE_COLOR
5108 && ps_global
->VAR_IND_HIPRI_BACK_COLOR
){
5109 ielem
->type
= eTypeCol
;
5110 ielem
->freecolor
= 1;
5111 ielem
->color
= new_color_pair(ps_global
->VAR_IND_HIPRI_FORE_COLOR
, ps_global
->VAR_IND_HIPRI_BACK_COLOR
);
5113 else if((v
== 4 || v
== 5) && pico_usingcolor()
5114 && ps_global
->VAR_IND_LOPRI_FORE_COLOR
5115 && ps_global
->VAR_IND_LOPRI_BACK_COLOR
){
5116 ielem
->type
= eTypeCol
;
5117 ielem
->freecolor
= 1;
5118 ielem
->color
= new_color_pair(ps_global
->VAR_IND_LOPRI_FORE_COLOR
, ps_global
->VAR_IND_LOPRI_BACK_COLOR
);
5121 ourifield
->leftadj
= 1;
5122 set_ielem_widths_in_field(ourifield
);
5125 fs_give((void **) &hdrval
);
5131 header_str(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
, ICE_S
*ice
)
5133 IFIELD_S
*ourifield
= NULL
;
5134 IELEM_S
*ielem
= NULL
;
5135 char *fieldval
= NULL
;
5137 if(ice
&& ice
->ifield
){
5138 /* move to last ifield, the one we're working */
5139 for(ourifield
= ice
->ifield
;
5140 ourifield
&& ourifield
->next
;
5141 ourifield
= ourifield
->next
)
5148 fieldval
= get_fieldval(idata
, hdrtok
);
5151 ielem
= new_ielem(&ourifield
->ielem
);
5152 ielem
->freedata
= 1;
5153 ielem
->data
= fieldval
;
5154 ielem
->datalen
= strlen(fieldval
);
5156 ourifield
->leftadj
= (hdrtok
->adjustment
== Left
) ? 1 : 0;
5159 set_ielem_widths_in_field(ourifield
);
5164 get_fieldval(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
)
5167 char *hdrval
= NULL
, *testval
;
5168 char *fieldval
= NULL
, *firstval
;
5169 char *retfieldval
= NULL
;
5172 return(retfieldval
);
5174 if(hdrtok
&& hdrtok
->hdrname
&& hdrtok
->hdrname
[0])
5175 hdrval
= fetch_header(idata
, hdrtok
? hdrtok
->hdrname
: "");
5177 /* find start of fieldnum'th field */
5179 for(fieldnum
= MAX(hdrtok
->fieldnum
-1, 0);
5180 fieldnum
> 0 && fieldval
&& *fieldval
; fieldnum
--){
5183 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5184 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5185 if(testval
&& (!firstval
|| testval
< firstval
))
5189 fieldval
= firstval
;
5190 if(fieldval
&& *fieldval
)
5194 /* tie off end of field */
5195 if(fieldval
&& *fieldval
&& hdrtok
->fieldnum
> 0){
5197 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5198 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5199 if(testval
&& (!firstval
|| testval
< firstval
))
5210 retfieldval
= cpystr(fieldval
);
5213 fs_give((void **) &hdrval
);
5215 return(retfieldval
);
5220 scorevalfrommsg(MAILSTREAM
*stream
, MsgNo rawno
, HEADER_TOK_S
*hdrtok
, int no_fetch
)
5224 char *fieldval
= NULL
;
5227 memset(&idata
, 0, sizeof(INDEXDATA_S
));
5228 idata
.stream
= stream
;
5229 idata
.no_fetch
= no_fetch
;
5230 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), rawno
);
5231 idata
.rawno
= rawno
;
5232 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
5233 && (mc
= mail_elt(stream
, idata
.rawno
))){
5234 idata
.size
= mc
->rfc822_size
;
5235 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
5240 fieldval
= get_fieldval(&idata
, hdrtok
);
5243 retval
= atol(fieldval
);
5244 fs_give((void **) &fieldval
);
5252 * Put a string representing the subject into str. Idata tells us which
5253 * message we are referring to.
5255 * This means we should ensure that all data ends up being UTF-8 data.
5256 * That covers the data in ice ielems and str.
5258 * Args idata -- which message?
5259 * str -- destination buffer
5260 * strsize -- size of str buffer
5261 * kwtype -- prepend keywords or kw initials before the subject
5262 * opening -- add first text from body of message if there's room
5263 * ice -- index cache entry for message
5266 subj_str(INDEXDATA_S
*idata
, char *str
, size_t strsize
, SubjKW kwtype
, int opening
, ICE_S
*ice
)
5268 char *subject
, *origsubj
, *origstr
, *rawsubj
, *sptr
= NULL
;
5269 char *p
, *border
, *q
= NULL
, *free_subj
= NULL
;
5273 int depth
= 0, mult
= 2;
5275 int do_subj
= 0, truncated_tree
= 0;
5276 PINETHRD_S
*thd
, *thdorig
;
5277 IELEM_S
*ielem
= NULL
, *subjielem
= NULL
;
5278 IFIELD_S
*ourifield
= NULL
;
5284 * If we need the data at the start of the message and we're in
5285 * a c-client callback, defer the data lookup until later.
5287 if(opening
&& idata
->no_fetch
){
5292 if(ice
&& ice
->ifield
){
5293 /* move to last ifield, the one we're working on */
5294 for(ourifield
= ice
->ifield
;
5295 ourifield
&& ourifield
->next
;
5296 ourifield
= ourifield
->next
)
5300 str
[0] = str
[strsize
-1] = '\0';
5302 rawsubj
= fetch_subject(idata
);
5307 * Before we do anything else, decode the character set in the subject and
5308 * work with the result.
5310 sp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5311 SIZEOF_20KBUF
, rawsubj
);
5314 len
+= 100; /* for possible charset, escaped characters */
5315 origsubj
= fs_get((len
+1) * sizeof(unsigned char));
5318 iutf8ncpy(origsubj
, sp
, len
);
5320 origsubj
[len
] = '\0';
5321 replace_tabs_by_space(origsubj
);
5322 removing_trailing_white_space(origsubj
);
5325 * origsubj is the original subject but it has been decoded. We need
5326 * to free it at the end of this routine.
5331 * prepend_keyword will put the keyword stuff before the subject
5332 * and split the subject up into its colored parts in subjielem.
5333 * Subjielem is a local ielem which will have to be fit into the
5334 * real ifield->ielem later. The print_format strings in subjielem will
5335 * not be filled in by prepend_keyword because of the fact that we
5336 * may have to adjust things for threading below.
5337 * We use subjielem in case we want to insert some threading information
5338 * at the front of the subject.
5340 if(kwtype
== KW
|| kwtype
== KWInit
){
5341 subject
= prepend_keyword_subject(idata
->stream
, idata
->rawno
,
5343 ourifield
? &subjielem
: NULL
,
5344 ps_global
->VAR_KW_BRACES
);
5345 free_subj
= subject
;
5350 subjielem
= new_ielem(&subjielem
);
5351 subjielem
->type
= eTypeCol
;
5352 subjielem
->freedata
= 1;
5353 subjielem
->data
= cpystr(subject
);
5354 subjielem
->datalen
= strlen(subject
);
5355 if(pico_usingcolor()
5356 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5357 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5358 subjielem
->freecolor
= 1;
5359 subjielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5365 * This space is here so that if the subject does
5366 * not extend all the way to the end of the field then
5367 * we'll switch the color back and paint the rest of the
5368 * field in the Normal color or the index line color.
5371 ielem
= new_ielem(&subjielem
);
5372 ielem
->freedata
= 1;
5373 ielem
->data
= cpystr(" ");
5381 && (ps_global
->thread_disp_style
== THREAD_STRUCT
5382 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
5383 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
5384 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ2
)){
5387 * Why do we want to truncate the subject and from strs?
5388 * It's so we can put the [5] thread count things in below when
5389 * we are threading and the thread structure runs off the right
5390 * hand edge of the screen. This routine doesn't know that it
5391 * is running off the edge unless it knows the actual width
5392 * that we have to draw in.
5394 if(pith_opt_truncate_sfstr
5395 && (*pith_opt_truncate_sfstr
)()
5397 && ourifield
->width
> 0)
5398 width
= ourifield
->width
;
5403 width
= MIN(width
, strsize
-1);
5406 * We're counting on the fact that this initial part of the
5407 * string is ascii and we have one octet per character and
5408 * characters are width 1 on the screen.
5410 border
= str
+ width
;
5412 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
5414 if(pith_opt_condense_thread_cue
)
5415 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
5417 && get_lflag(idata
->stream
,
5422 * width is < available strsize and
5423 * border points to something less than or equal
5424 * to the end of the buffer.
5430 while(thd
->parent
&&
5431 (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
5435 if(ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
)
5438 sptr
+= (mult
*depth
);
5439 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
5440 thd
&& thd
->parent
&& p
>= str
;
5441 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
5442 if(p
+ mult
>= border
&& !q
){
5443 if(width
>= 4 && depth
< 100){
5444 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
5447 else if(width
>= 5 && depth
< 1000){
5448 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
5452 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
5465 if(ps_global
->thread_disp_style
== THREAD_STRUCT
5466 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5469 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5472 if(thd
== thdorig
&& !thd
->branch
)
5473 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
5474 else if(thd
== thdorig
|| thd
->branch
)
5477 if(p
+ 1 < border
&& thd
== thdorig
)
5484 if(sptr
&& !truncated_tree
){
5486 * Look to see if the subject is the same as the previous
5487 * message in the thread, if any. If it is the same, don't
5488 * reprint the subject.
5490 * Note that when we're prepending keywords to the subject,
5491 * and the user changes a keyword, we do invalidate
5492 * the index cache for that message but we don't go to the
5493 * trouble of invalidating the index cache for the the child
5494 * of that node in the thread, so the MUTT subject line
5495 * display for the child may be wrong. That is, it may show
5496 * it is the same as this subject even though it no longer
5497 * is, or vice versa.
5499 if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5503 if(thdorig
->parent
&&
5504 (thd
= fetch_thread(idata
->stream
, thdorig
->parent
))
5506 char *this_orig
= NULL
,
5508 *free_prev_orig
= NULL
,
5509 *this_prep
= NULL
, /* includes prepend */
5513 SORTCACHE
*sc
= NULL
;
5515 /* get the stripped subject of previous message */
5516 mc
= (mailcache_t
) mail_parameters(NIL
, GET_CACHE
, NIL
);
5518 sc
= (*mc
)(idata
->stream
, thd
->rawno
, CH_SORTCACHE
);
5520 if(sc
&& sc
->subject
)
5521 prev_orig
= sc
->subject
;
5525 env
= pine_mail_fetchenvelope(idata
->stream
,
5527 stripthis
= (env
&& env
->subject
)
5528 ? env
->subject
: "";
5530 mail_strip_subject(stripthis
, &prev_orig
);
5532 free_prev_orig
= prev_orig
;
5535 mail_strip_subject(rawsubj
, &this_orig
);
5537 if(kwtype
== KW
|| kwtype
== KWInit
){
5538 prev_prep
= prepend_keyword_subject(idata
->stream
,
5542 ps_global
->VAR_KW_BRACES
);
5544 this_prep
= prepend_keyword_subject(idata
->stream
,
5548 ps_global
->VAR_KW_BRACES
);
5550 if((this_prep
|| prev_prep
)
5551 && ((this_prep
&& !prev_prep
)
5552 || (prev_prep
&& !this_prep
)
5553 || strucmp(this_prep
, prev_prep
)))
5557 if((this_orig
|| prev_orig
)
5558 && ((this_orig
&& !prev_orig
)
5559 || (prev_orig
&& !this_orig
)
5560 || strucmp(this_orig
, prev_orig
)))
5565 * If some of the thread is zoomed out of view, we
5566 * want to display the subject of the first one that
5567 * is in view. If any of the parents or grandparents
5568 * etc of this message are visible, then we don't
5569 * need to worry about it. If all of the parents have
5570 * been zoomed away, then this is the first one.
5572 * When you're looking at a particular case where
5573 * some of the messages of a thread are selected it
5574 * seems like we should look at not only our
5575 * direct parents, but the siblings of the parent
5576 * too. But that's not really correct, because those
5577 * siblings are basically the starts of different
5578 * branches, separate from our branch. They could
5579 * have their own subjects, for example. This will
5580 * give us cases where it looks like we are showing
5581 * the subject too much, but it will be correct!
5583 * In zoom_index() we clear_index_cache_ent for
5584 * some lines which have subjects which might become
5585 * visible when we zoom, and also in set_lflags
5586 * where we might change subjects by unselecting
5587 * something when zoomed.
5591 if(!msgline_hidden(idata
->stream
,
5592 sp_msgmap(idata
->stream
),
5593 mn_raw2m(sp_msgmap(idata
->stream
),
5596 break; /* found a visible parent */
5599 if(thd
&& thd
->parent
)
5600 thd
= fetch_thread(idata
->stream
,thd
->parent
);
5605 if(!thd
) /* none were visible */
5610 fs_give((void **) &this_orig
);
5613 fs_give((void **) &this_prep
);
5616 fs_give((void **) &free_prev_orig
);
5619 fs_give((void **) &prev_prep
);
5630 * We don't need to worry about truncating to width
5631 * here. If we go over the right hand edge it will be
5634 strsize
-= (sptr
- str
);
5636 strncpy(sptr
, subject
, strsize
-1);
5637 sptr
[strsize
-1] = '\0';
5639 else if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5640 strsize
-= (sptr
- str
);
5648 * We decided we don't need the subject so we'd better
5649 * eliminate subjielem.
5651 free_ielem(&subjielem
);
5655 free_ielem(&subjielem
); /* no room for actual subject */
5657 if(ourifield
&& sptr
&& sptr
> origstr
){
5658 ielem
= new_ielem(&ourifield
->ielem
);
5659 ielem
->type
= eThreadInfo
;
5660 ielem
->freedata
= 1;
5663 ielem
->data
= cpystr(origstr
);
5664 ielem
->datalen
= strlen(origstr
);
5670 * Not much to do for the non-threading case. Just copy the
5671 * subject we have so far into str and truncate it.
5673 strncpy(str
, subject
, strsize
-1);
5674 str
[strsize
-1] = '\0';
5679 * We need to add subjielem to the end of the ourifield->ielem list.
5682 if(ourifield
->ielem
){
5683 for(ielem
= ourifield
->ielem
;
5684 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5687 ielem
->next
= subjielem
;
5690 ourifield
->ielem
= subjielem
;
5693 ourifield
->leftadj
= 1;
5696 if(opening
&& ourifield
){
5697 IELEM_S
*ftielem
= NULL
;
5701 first_text
= fetch_firsttext(idata
, 0);
5707 strncpy(sep
, ps_global
->VAR_OPENING_SEP
? ps_global
->VAR_OPENING_SEP
: " - ",
5709 sep
[sizeof(sep
)-1] = '\0';
5710 removing_double_quotes(sep
);
5711 seplen
= strlen(sep
);
5713 ftielem
= new_ielem(&ftielem
);
5714 ftielem
->type
= eTypeCol
;
5715 ftielem
->freedata
= 1;
5716 len
= strlen(first_text
) + seplen
;
5717 ftielem
->data
= (char *) fs_get((len
+ 1) * sizeof(char));
5719 strncpy(ftielem
->data
, sep
, seplen
);
5720 strncpy(ftielem
->data
+seplen
, first_text
, len
+1-seplen
);
5721 ftielem
->data
[len
] = '\0';
5723 ftielem
->datalen
= strlen(ftielem
->data
);
5725 fs_give((void **) &first_text
);
5728 if(pico_usingcolor()
5729 && ps_global
->VAR_IND_OP_FORE_COLOR
5730 && ps_global
->VAR_IND_OP_BACK_COLOR
){
5731 ftielem
->freecolor
= 1;
5732 ftielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
, ps_global
->VAR_IND_OP_BACK_COLOR
);
5735 * This space is here so that if the opening text does
5736 * not extend all the way to the end of the field then
5737 * we'll switch the color back and paint the rest of the
5738 * field in the Normal color or the index line color.
5740 ielem
= new_ielem(&ftielem
);
5741 ielem
->freedata
= 1;
5742 ielem
->data
= cpystr(" ");
5746 if(ourifield
->ielem
){
5747 for(ielem
= ourifield
->ielem
;
5748 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5751 ielem
->next
= ftielem
;
5754 ourifield
->ielem
= ftielem
;
5757 ourifield
->leftadj
= 1;
5762 set_ielem_widths_in_field(ourifield
);
5765 fs_give((void **) &origsubj
);
5768 fs_give((void **) &free_subj
);
5773 * Returns an allocated string which is the passed in subject with a
5774 * list of keywords prepended.
5776 * If kwtype == KW you will end up with
5778 * {keyword1 keyword2} subject
5780 * (actually, keyword nicknames will be used instead of the actual keywords
5781 * in the case that the user defined nicknames)
5783 * If kwtype == KWInit you get
5787 * where A is the first letter of the first keyword and B is the first letter
5788 * of the second defined keyword. No space between them. There could be more
5791 * If an ielemp is passed in it will be filled out with the data and colors
5792 * of the pieces of the subject but the print_format strings will not
5796 prepend_keyword_subject(MAILSTREAM
*stream
, long int rawno
, char *subject
,
5797 SubjKW kwtype
, IELEM_S
**ielemp
, char *braces
)
5799 char *p
, *next_piece
, *retsubj
= NULL
, *str
;
5800 char *left_brace
= NULL
, *right_brace
= NULL
;
5802 int some_set
= 0, save
;
5805 COLOR_PAIR
*color
= NULL
;
5806 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5811 if(braces
&& *braces
)
5812 get_pair(braces
, &left_brace
, &right_brace
, 1, 0);
5814 len
= (left_brace
? strlen(left_brace
) : 0) +
5815 (right_brace
? strlen(right_brace
) : 0);
5817 if(stream
&& rawno
>= 0L && rawno
<= stream
->nmsgs
){
5818 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
5819 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5822 len
++; /* space between keywords */
5824 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5827 else if(kwtype
== KWInit
){
5828 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5829 /* interested in only the first UTF-8 initial */
5832 unsigned long remaining_octets
;
5833 unsigned char *inputp
;
5835 remaining_octets
= strlen(str
);
5836 inputp
= (unsigned char *) str
;
5837 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5838 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5839 len
+= (unsigned) (inputp
- (unsigned char *) str
);
5848 if((kwtype
== KW
|| kwtype
== KWInit
) && some_set
){
5849 len
+= strlen(subject
); /* subject is already UTF-8 if needed */
5850 retsubj
= (char *) fs_get((len
+ 1) * sizeof(*retsubj
));
5851 memset(retsubj
, 0, (len
+ 1) * sizeof(*retsubj
));
5852 next_piece
= p
= retsubj
;
5854 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5855 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5857 if(left_brace
&& len
> 0)
5858 sstrncpy(&p
, left_brace
, len
);
5860 else if(kwtype
== KW
)
5863 if(ielemp
&& p
> next_piece
){
5866 ielem
= new_ielem(ielemp
);
5867 ielem
->freedata
= 1;
5868 ielem
->data
= cpystr(next_piece
);
5869 ielem
->datalen
= strlen(next_piece
);
5874 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5876 if(kwtype
== KWInit
){
5879 unsigned long remaining_octets
;
5880 unsigned char *inputp
;
5882 remaining_octets
= strlen(str
);
5883 inputp
= (unsigned char *) str
;
5884 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5885 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5886 if(len
-(p
-retsubj
) > 0){
5887 sstrncpy(&p
, str
, MIN(inputp
- (unsigned char *) str
,len
-(p
-retsubj
)));
5888 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5889 && ((kw
->nick
&& kw
->nick
[0]
5890 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5891 || (kw
->kw
&& kw
->kw
[0]
5892 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5893 ielem
= new_ielem(ielemp
);
5894 ielem
->freedata
= 1;
5897 ielem
->data
= cpystr(next_piece
);
5898 ielem
->datalen
= strlen(next_piece
);
5899 ielem
->color
= color
;
5908 free_color_pair(&color
);
5912 if(len
-(p
-retsubj
) > 0)
5913 sstrncpy(&p
, str
, len
-(p
-retsubj
));
5915 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5916 && ((kw
->nick
&& kw
->nick
[0]
5917 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5918 || (kw
->kw
&& kw
->kw
[0]
5919 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5920 ielem
= new_ielem(ielemp
);
5921 ielem
->freedata
= 1;
5924 ielem
->data
= cpystr(next_piece
);
5925 ielem
->datalen
= strlen(next_piece
);
5926 ielem
->color
= color
;
5933 free_color_pair(&color
);
5938 if(len
-(p
-retsubj
) > 0 && right_brace
)
5939 sstrncpy(&p
, right_brace
, len
-(p
-retsubj
));
5941 if(ielemp
&& p
> next_piece
){
5944 ielem
= new_ielem(ielemp
);
5945 ielem
->freedata
= 1;
5946 ielem
->data
= cpystr(next_piece
);
5947 ielem
->datalen
= strlen(next_piece
);
5952 if(len
-(p
-retsubj
) > 0 && subject
)
5953 sstrncpy(&p
, subject
, len
-(p
-retsubj
));
5955 if(ielemp
&& p
> next_piece
){
5958 ielem
= new_ielem(ielemp
);
5959 ielem
->type
= eTypeCol
;
5960 ielem
->freedata
= 1;
5961 ielem
->data
= cpystr(next_piece
);
5962 ielem
->datalen
= strlen(next_piece
);
5965 if(pico_usingcolor()
5966 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5967 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5968 ielem
->freecolor
= 1;
5969 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5973 retsubj
[len
] = '\0'; /* just making sure */
5977 ielem
= new_ielem(ielemp
);
5978 ielem
->type
= eTypeCol
;
5979 ielem
->freedata
= 1;
5980 ielem
->data
= cpystr(subject
);
5981 ielem
->datalen
= strlen(subject
);
5982 if(pico_usingcolor()
5983 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5984 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5985 ielem
->freecolor
= 1;
5986 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5990 retsubj
= cpystr(subject
);
5995 fs_give((void **) &left_brace
);
5998 fs_give((void **) &right_brace
);
6006 * This means we should ensure that all data ends up being UTF-8 data.
6007 * That covers the data in ice ielems and str.
6010 from_str(IndexColType ctype
, INDEXDATA_S
*idata
, char *str
, size_t strsize
, ICE_S
*ice
)
6012 char *field
, *newsgroups
, *border
, *p
, *fptr
= NULL
, *q
= NULL
;
6015 int depth
= 0, mult
= 2;
6016 PINETHRD_S
*thd
, *thdorig
;
6019 && (ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
6020 || ps_global
->thread_disp_style
== THREAD_INDENT_FROM2
6021 || ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
)){
6023 if(pith_opt_truncate_sfstr
&& (*pith_opt_truncate_sfstr
)()){
6024 IFIELD_S
*ourifield
= NULL
;
6026 if(ice
&& ice
->ifield
){
6027 /* move to last ifield, the one we're working on */
6028 for(ourifield
= ice
->ifield
;
6029 ourifield
&& ourifield
->next
;
6030 ourifield
= ourifield
->next
)
6034 if(ourifield
&& ourifield
->width
> 0)
6035 width
= ourifield
->width
;
6041 width
= MIN(width
, strsize
-1);
6043 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
6044 border
= str
+ width
;
6045 if(pith_opt_condense_thread_cue
)
6046 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
6048 && get_lflag(idata
->stream
,
6055 while(thd
->parent
&& (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
6059 if(ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
)
6062 fptr
+= (mult
*depth
);
6063 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
6064 thd
&& thd
->parent
&& p
>= str
;
6065 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
6066 if(p
+ mult
>= border
&& !q
){
6067 if(width
>= 4 && depth
< 100){
6068 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
6071 else if(width
>= 5 && depth
< 1000){
6072 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
6076 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
6086 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6089 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6092 if(thd
== thdorig
&& !thd
->branch
)
6093 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6094 else if(thd
== thdorig
|| thd
->branch
)
6101 else if(p
< border
){
6103 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6106 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6109 if(thd
== thdorig
&& !thd
->branch
)
6110 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6111 else if(thd
== thdorig
|| thd
->branch
)
6122 strsize
-= (fptr
- str
);
6125 case iFromToNotNews
:
6126 if(!(addr
= fetch_from(idata
)) || address_is_us(addr
, ps_global
)){
6128 strncpy(fptr
, "To: ", strsize
-1);
6129 fptr
[strsize
-1] = '\0';
6133 if((field
= ((addr
= fetch_to(idata
))
6135 : (addr
= fetch_cc(idata
))
6138 && set_index_addr(idata
, field
, addr
, "To: ",
6142 if(ctype
== iFromTo
&&
6143 (newsgroups
= fetch_newsgroups(idata
)) &&
6145 snprintf(fptr
, strsize
, "To: %-*.*s", strsize
-1-4, strsize
-1-4,
6150 /* else fall thru to From: */
6153 /* else fall thru to From: */
6159 set_index_addr(idata
, "From", fetch_from(idata
), NULL
, strsize
-1, fptr
);
6164 if((addr
= fetch_from(idata
)) && addr
->mailbox
&& addr
->mailbox
[0]){
6165 char *mb
= NULL
, *hst
= NULL
, *at
= NULL
;
6169 if(ctype
== iAddress
&& addr
->host
&& addr
->host
[0]
6170 && addr
->host
[0] != '.'){
6176 if(!at
|| strsize
-1 <= len
)
6177 snprintf(fptr
, strsize
, "%-*.*s", strsize
-1, strsize
-1, mb
);
6179 snprintf(fptr
, strsize
, "%s@%-*.*s", mb
, strsize
-1-len
-1, strsize
-1-len
-1, hst
);
6188 replace_tabs_by_space(str
);
6193 * Set up the elements contained in field so that they take up the
6194 * whole field width. Data is assumed to be UTF-8.
6197 set_ielem_widths_in_field(IFIELD_S
*ifield
)
6199 IELEM_S
*ielem
= NULL
;
6200 int datawidth
, fmtwidth
;
6205 fmtwidth
= ifield
->width
;
6207 for(ielem
= ifield
->ielem
; ielem
&& fmtwidth
> 0; ielem
= ielem
->next
){
6208 if(!ifield
->leftadj
&& ielem
->next
){
6209 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield
->ctype
));
6213 datawidth
= (int) utf8_width(ielem
->data
);
6214 if(datawidth
>= fmtwidth
|| !ielem
->next
){
6215 set_print_format(ielem
, fmtwidth
, ifield
->leftadj
);
6219 set_print_format(ielem
, datawidth
, ifield
->leftadj
);
6220 fmtwidth
-= datawidth
;
6227 * Simple hash function from K&R 2nd edition, p. 144.
6229 * This one is modified to never return 0 so we can use that as a special
6230 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6231 * as a special value that can't be returned by line_hash.
6236 unsigned long hashval
;
6238 for(hashval
= 0; *s
!= '\0'; s
++)
6239 hashval
= *s
+ 31 * hashval
;
6241 hashval
= hashval
% LINE_HASH_N
;
6251 * Returns nonzero if considered hidden, 0 if not considered hidden.
6254 msgline_hidden(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
6258 if(flags
& MH_ANYTHD
){
6259 ret
= ((any_lflagged(msgmap
, MN_HIDE
) > 0)
6260 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
));
6262 else if(flags
& MH_THISTHD
&& THREADING() && sp_viewing_a_thread(stream
)){
6263 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6264 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
));
6267 if(THREADING() && sp_viewing_a_thread(stream
)){
6268 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6269 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
)
6270 || get_lflag(stream
, msgmap
, msgno
, MN_CHID
));
6272 else if(THRD_INDX()){
6274 * If this message is in the collapsed part of a thread,
6275 * it's hidden. It must be a top-level of a thread to be
6276 * considered visible. Even if it is top-level, it is only
6277 * visible if some message in the thread is not hidden.
6279 if(get_lflag(stream
, msgmap
, msgno
, MN_CHID
)) /* not top */
6282 unsigned long rawno
;
6283 PINETHRD_S
*thrd
= NULL
;
6285 rawno
= mn_m2raw(msgmap
, msgno
);
6287 thrd
= fetch_thread(stream
, rawno
);
6289 ret
= !thread_has_some_visible(stream
, thrd
);
6293 ret
= ((any_lflagged(msgmap
, MN_HIDE
| MN_CHID
) > 0)
6294 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
| MN_CHID
));
6299 "msgline_hidden(%ld): %s\n", msgno
, ret
? "HID" : "VIS"));
6306 adjust_cur_to_visible(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
6311 cur
= mn_get_cur(msgmap
);
6313 /* if current is hidden, adjust */
6314 if(cur
>= 1L && cur
<= mn_get_total(msgmap
)
6315 && msgline_hidden(stream
, msgmap
, cur
, 0)){
6317 dir
= mn_get_revsort(msgmap
) ? -1 : 1;
6320 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6321 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6322 && msgline_hidden(stream
, msgmap
, n
, 0);
6326 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6327 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6328 mn_reset_cur(msgmap
, n
);
6329 else{ /* no visible in that direction */
6331 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6332 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6333 && msgline_hidden(stream
, msgmap
, n
, 0);
6337 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6338 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6339 mn_reset_cur(msgmap
, n
);
6347 setup_for_index_index_screen(void)
6349 format_index_line
= format_index_index_line
;
6350 setup_header_widths
= setup_index_header_widths
;
6355 setup_for_thread_index_screen(void)
6357 format_index_line
= format_thread_index_line
;
6358 setup_header_widths
= setup_thread_header_widths
;
6363 ice_hash(ICE_S
*ice
)
6365 char buf
[MAX_SCREEN_COLS
+1];
6370 simple_index_line(buf
, sizeof(buf
), ice
, 0L);
6372 buf
[sizeof(buf
) - 1] = '\0';
6374 return(line_hash(buf
));
6379 left_adjust(int width
)
6381 return(format_str(width
, 1));
6386 right_adjust(int width
)
6388 return(format_str(width
, 0));
6393 * Returns allocated and filled in format string.
6396 format_str(int width
, int left
)
6401 len
= PRINT_FORMAT_LEN(width
,left
) * sizeof(char);
6402 format
= (char *) fs_get(len
+ 1);
6403 copy_format_str(width
, left
, format
, len
);
6411 * Put the left or right adjusted format string of width width into
6412 * dest. Dest is of size n+1.
6415 copy_format_str(int width
, int left
, char *dest
, int n
)
6419 p
= int2string(width
);
6421 snprintf(dest
, n
+1, "%%%s%s.%ss", left
? "-" : "", p
, p
);
6430 * Sets up the print_format string to be width wide with left or right
6431 * adjust. Takes care of memory freeing and allocation.
6434 set_print_format(IELEM_S
*ielem
, int width
, int leftadj
)
6439 if(ielem
->print_format
){
6440 /* is there enough room? */
6441 if(ielem
->freeprintf
< PRINT_FORMAT_LEN(width
,leftadj
)+1){
6442 fs_resize((void **) &ielem
->print_format
,
6443 (PRINT_FORMAT_LEN(width
,leftadj
)+1) * sizeof(char));
6444 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);
6447 copy_format_str(width
, leftadj
, ielem
->print_format
,
6448 PRINT_FORMAT_LEN(width
,leftadj
));
6451 ielem
->print_format
= leftadj
? left_adjust(width
)
6452 : right_adjust(width
);
6453 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);