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-2016 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 alpine_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(idata
->rawno
> 0L && idata
->rawno
<= idata
->stream
->nmsgs
2070 && (mc
=mail_elt(idata
->stream
,idata
->rawno
)) && mc
->flagged
)
2071 to_us
= '*'; /* simple */
2072 else if(!IS_NEWS(idata
->stream
)){
2073 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2074 if(address_is_us(addr
, ps_global
)){
2082 if(to_us
!= '+' && resent_to_us(idata
)){
2088 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2089 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2090 if(address_is_us(addr
, ps_global
)){
2097 status
= (!idata
->stream
|| !IS_NEWS(idata
->stream
)
2098 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))
2104 if(user_flag_is_set(idata
->stream
, idata
->rawno
, FORWARDED_FLAG
))
2114 snprintf(str
, sizeof(str
), "%c %c", to_us
, status
);
2116 ifield
->leftadj
= 1;
2117 for(i
= 0; i
< 3; i
++){
2118 ielem
= new_ielem(&ifield
->ielem
);
2119 ielem
->freedata
= 1;
2120 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2121 ielem
->data
[0] = str
[i
];
2122 ielem
->data
[1] = '\0';
2124 set_print_format(ielem
, 1, ifield
->leftadj
);
2127 if(pico_usingcolor()){
2130 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2131 ielem
= ifield
->ielem
;
2132 ielem
->freecolor
= 1;
2133 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2136 else if(str
[0] == '+' || str
[0] == '-'){
2137 if(VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
2138 ielem
= ifield
->ielem
;
2139 ielem
->freecolor
= 1;
2140 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2145 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2146 ielem
= ifield
->ielem
->next
->next
;
2147 ielem
->freecolor
= 1;
2148 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2151 else if(str
[2] == 'A'){
2152 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2153 ielem
= ifield
->ielem
->next
->next
;
2154 ielem
->freecolor
= 1;
2155 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2158 else if(str
[2] == 'F'){
2159 if(VAR_IND_FWD_FORE_COLOR
&& VAR_IND_FWD_BACK_COLOR
){
2160 ielem
= ifield
->ielem
->next
->next
;
2161 ielem
->freecolor
= 1;
2162 ielem
->color
= new_color_pair(VAR_IND_FWD_FORE_COLOR
, VAR_IND_FWD_BACK_COLOR
);
2165 else if(str
[2] == 'N'){
2166 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2167 ielem
= ifield
->ielem
->next
->next
;
2168 ielem
->freecolor
= 1;
2169 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2180 char new, answered
, deleted
, flagged
;
2183 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2184 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 0);
2188 if(!IS_NEWS(idata
->stream
)){
2189 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2190 if(address_is_us(addr
, ps_global
)){
2195 if(to_us
== ' ' && resent_to_us(idata
))
2198 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2199 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2200 if(address_is_us(addr
, ps_global
)){
2207 new = answered
= deleted
= flagged
= ' ';
2210 unsigned long save_branch
, cnt
, tot_in_thrd
;
2213 * Branch is a sibling, not part of the thread, so
2214 * don't consider it when displaying this line.
2216 save_branch
= thrd
->branch
;
2219 tot_in_thrd
= count_flags_in_thread(idata
->stream
, thrd
,
2222 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_DEL
);
2224 deleted
= (cnt
== tot_in_thrd
) ? 'D' : 'd';
2226 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_ANS
);
2228 answered
= (cnt
== tot_in_thrd
) ? 'A' : 'a';
2230 /* no lower case *, same thing for some or all */
2231 if(count_flags_in_thread(idata
->stream
, thrd
, F_FLAG
))
2234 new = status_symbol_for_thread(idata
->stream
, thrd
,
2237 thrd
->branch
= save_branch
;
2240 mc
= (idata
->rawno
> 0L && idata
->stream
2241 && idata
->rawno
<= idata
->stream
->nmsgs
)
2242 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2243 if(mc
&& mc
->valid
){
2244 if(cdesc
->ctype
== iIStatus
|| cdesc
->ctype
== iSIStatus
){
2246 new = mc
->seen
? 'R' : 'N';
2251 && (!IS_NEWS(idata
->stream
)
2252 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)))
2266 snprintf(str
, sizeof(str
), "%c %c%c%c%c", to_us
, flagged
, new,
2269 if(cdesc
->ctype
== iSIStatus
)
2274 ifield
->leftadj
= 1;
2275 for(i
= start
; i
< 6; i
++){
2276 ielem
= new_ielem(&ifield
->ielem
);
2277 ielem
->freedata
= 1;
2278 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2279 ielem
->data
[0] = str
[i
];
2280 ielem
->data
[1] = '\0';
2282 set_print_format(ielem
, 1, ifield
->leftadj
);
2285 if(pico_usingcolor()){
2287 if(str
[0] == '+' || str
[0] == '-'){
2289 && VAR_IND_PLUS_FORE_COLOR
2290 && VAR_IND_PLUS_BACK_COLOR
){
2291 ielem
= ifield
->ielem
;
2292 ielem
->freecolor
= 1;
2293 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2298 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2300 ielem
= ifield
->ielem
;
2302 ielem
= ifield
->ielem
->next
->next
;
2304 ielem
->freecolor
= 1;
2305 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2309 if(str
[3] == 'N' || str
[3] == 'n'){
2310 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2312 ielem
= ifield
->ielem
->next
;
2314 ielem
= ifield
->ielem
->next
->next
->next
;
2316 ielem
->freecolor
= 1;
2317 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2320 else if(str
[3] == 'R' || str
[3] == 'r'){
2321 if(VAR_IND_REC_FORE_COLOR
&& VAR_IND_REC_BACK_COLOR
){
2323 ielem
= ifield
->ielem
->next
;
2325 ielem
= ifield
->ielem
->next
->next
->next
;
2327 ielem
->freecolor
= 1;
2328 ielem
->color
= new_color_pair(VAR_IND_REC_FORE_COLOR
, VAR_IND_REC_BACK_COLOR
);
2331 else if(str
[3] == 'U' || str
[3] == 'u'){
2332 if(VAR_IND_UNS_FORE_COLOR
&& VAR_IND_UNS_BACK_COLOR
){
2334 ielem
= ifield
->ielem
->next
;
2336 ielem
= ifield
->ielem
->next
->next
->next
;
2338 ielem
->freecolor
= 1;
2339 ielem
->color
= new_color_pair(VAR_IND_UNS_FORE_COLOR
, VAR_IND_UNS_BACK_COLOR
);
2343 if(str
[4] == 'A' || str
[4] == 'a'){
2344 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2346 ielem
= ifield
->ielem
->next
->next
;
2348 ielem
= ifield
->ielem
->next
->next
->next
->next
;
2350 ielem
->freecolor
= 1;
2351 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2355 if(str
[5] == 'D' || str
[5] == 'd'){
2356 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2358 ielem
= ifield
->ielem
->next
->next
->next
;
2360 ielem
= ifield
->ielem
->next
->next
->next
->next
->next
;
2362 ielem
->freecolor
= 1;
2363 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2373 * This is a special case. The message number is
2374 * generated on the fly in the painting routine.
2375 * But the data array is allocated here in case it
2376 * is useful for the paint routine.
2378 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2382 snprintf(str
, sizeof(str
), "%-*.*s", ifield
->width
, ifield
->width
, " ");
2383 if(VAR_IND_ARR_FORE_COLOR
&& VAR_IND_ARR_BACK_COLOR
){
2384 ifield
->leftadj
= 1;
2385 ielem
= new_ielem(&ifield
->ielem
);
2386 ielem
->freedata
= 1;
2387 ielem
->data
= cpystr(str
);
2388 ielem
->datalen
= strlen(str
);
2389 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
2390 ielem
->freecolor
= 1;
2391 ielem
->color
= new_color_pair(VAR_IND_ARR_FORE_COLOR
,
2392 VAR_IND_ARR_BACK_COLOR
);
2398 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2399 if(score
== SCORE_UNDEF
){
2400 SEARCHSET
*ss
= NULL
;
2402 ss
= mail_newsearchset();
2403 ss
->first
= ss
->last
= (unsigned long) idata
->rawno
;
2406 * This looks like it might be expensive to get the
2407 * score for each message when needed but it shouldn't
2408 * be too bad because we know we have the envelope
2409 * data cached. We can't calculate all of the scores
2410 * we need for the visible messages right here in
2411 * one fell swoop because we don't have the other
2412 * envelopes yet. And we can't get the other
2413 * envelopes at this point because we may be in
2414 * the middle of a c-client callback (pine_imap_env).
2415 * (Actually we could, because we know whether or
2416 * not we're in the callback because of the no_fetch
2418 * We have another problem if the score rules depend
2419 * on something other than envelope data. I guess they
2420 * only do that if they have an alltext (search the
2421 * text of the message) definition. So, we're going
2422 * to pass no_fetch to calculate_scores so that it
2423 * can return an error if we need the text data but
2424 * can't get it because of no_fetch. Setting bogus
2425 * will cause us to do the scores calculation later
2426 * when we are no longer in the callback.
2429 (calculate_some_scores(idata
->stream
,
2430 ss
, idata
->no_fetch
) == 0)
2432 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2433 mail_free_searchset(&ss
);
2437 snprintf(str
, sizeof(str
), "%ld", score
!= SCORE_UNDEF
? score
: 0L);
2440 case iDate
: case iMonAbb
: case iLDate
:
2441 case iSDate
: case iSTime
:
2442 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
2443 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
2444 case iSDateIsoS
: case iSDateIso
:
2445 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
2447 case iSDateTimeIsoS
: case iSDateTimeIso
:
2448 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
2450 case iSDateTimeIsoS24
: case iSDateTimeIso24
:
2451 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
2452 case iTimezone
: case iYear
: case iYear2Digit
:
2453 case iRDate
: case iDay
: case iDay2Digit
: case iMon2Digit
:
2454 case iDayOrdinal
: case iMon
: case iMonLong
:
2455 case iDayOfWeekAbb
: case iDayOfWeek
:
2456 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
2457 date_str(fetch_date(idata
), cdesc
->ctype
, 0, str
, sizeof(str
), cdesc
->monabb_width
);
2461 case iFromToNotNews
:
2466 from_str(cdesc
->ctype
, idata
, str
, sizeof(str
), ice
);
2470 if(((field
= ((addr
= fetch_to(idata
))
2472 : (addr
= fetch_cc(idata
))
2475 && !set_index_addr(idata
, field
, addr
, NULL
, BIGWIDTH
, str
))
2477 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2478 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, newsgroups
);
2483 set_index_addr(idata
, "Cc", fetch_cc(idata
), NULL
, BIGWIDTH
, str
);
2487 toaddr
= fetch_to(idata
);
2488 ccaddr
= fetch_cc(idata
);
2489 for(last_to
= toaddr
;
2490 last_to
&& last_to
->next
;
2491 last_to
= last_to
->next
)
2494 /* point end of to list temporarily at cc list */
2496 last_to
->next
= ccaddr
;
2498 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2501 last_to
->next
= NULL
;
2507 if((addr
= fetch_sender(idata
)) != NULL
)
2508 set_index_addr(idata
, "Sender", addr
, NULL
, BIGWIDTH
, str
);
2515 if((addr
= fetch_from(idata
)) && addr
->personal
){
2516 char *name
, *initials
= NULL
;
2518 name
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
2519 SIZEOF_20KBUF
, addr
->personal
);
2520 if(name
== addr
->personal
){
2521 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
2522 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
2523 name
= (char *) tmp_20k_buf
;
2527 initials
= reply_quote_initials(name
);
2528 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, initials
);
2537 if((l
= fetch_size(idata
)) < 10*1000L)
2538 snprintf(str
, sizeof(str
), "(%lu)", l
);
2540 else if(l
< 1000L*1000L - 1000L/2){
2541 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2542 snprintf(str
, sizeof(str
), "(%luK)", l
);
2544 /* 1.0M ... 99.9M */
2545 else if(l
< 1000L*100L*1000L - 100L*1000L/2){
2546 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2548 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2550 /* 100M ... 2000M */
2551 else if(l
<= 2*1000L*1000L*1000L){
2552 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2554 snprintf(str
, sizeof(str
), "(%luM)", l
);
2557 snprintf(str
, sizeof(str
), "(HUGE!)");
2563 if((l
= fetch_size(idata
)) < 100*1000L)
2564 snprintf(str
, sizeof(str
), "(%s)", comatose(l
));
2565 /* 100K ... 9,999K */
2566 else if(l
< 10L*1000L*1000L - 1000L/2){
2567 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2568 snprintf(str
, sizeof(str
), "(%sK)", comatose(l
));
2570 /* 10.0M ... 999.9M */
2571 else if(l
< 1000L*1000L*1000L - 100L*1000L/2){
2572 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2574 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2576 /* 1,000M ... 2,000M */
2577 else if(l
<= 2*1000L*1000L*1000L){
2578 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2580 snprintf(str
, sizeof(str
), "(%sM)", comatose(l
));
2583 snprintf(str
, sizeof(str
), "(HUGE!)");
2589 if((l
= fetch_size(idata
)) < 1000L)
2590 snprintf(str
, sizeof(str
), "(%lu)", l
);
2592 else if(l
< 100L*1000L - 1000L/2){
2593 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2594 snprintf(str
, sizeof(str
), "(%luK)", l
);
2597 else if(l
< 1000L*1000L - 100L*1000L/2){
2598 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= 100L*1000L/2
2600 snprintf(str
, sizeof(str
), "(.%luM)", l
);
2603 else if(l
< 1000L*100L*1000L - 1000L*1000L/2){
2604 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2606 snprintf(str
, sizeof(str
), "(%luM)", l
);
2609 else if(l
< 1000L*1000L*1000L - 100L*1000L*1000L/2){
2610 l
= l
/(100L*1000L*1000L) + (l
%(100L*1000L*1000L) >=
2611 (100L*1000L*1000L/2) ? 1L : 0L);
2612 snprintf(str
, sizeof(str
), "(.%luG)", l
);
2615 else if(l
<= 2*1000L*1000L*1000L){
2616 l
= l
/(1000L*1000L*1000L) + (l
%(1000L*1000L*1000L) >=
2617 (1000L*1000L*1000L/2) ? 1L : 0L);
2618 snprintf(str
, sizeof(str
), "(%luG)", l
);
2621 snprintf(str
, sizeof(str
), "(HUGE!)");
2625 /* From Carl Jacobsen <carl@ucsd.edu> */
2627 l
= fetch_size(idata
);
2628 l
= (l
/ 1024L) + (l
% 1024L != 0 ? 1 : 0);
2630 if(l
< 1024L) { /* 0k .. 1023k */
2631 snprintf(str
, sizeof(str
), "(%luk)", l
);
2633 } else if (l
< 100L * 1024L){ /* 1.0M .. 99.9M */
2634 snprintf(str
, sizeof(str
), "(%lu.M)", (l
* 10L) / 1024L);
2635 if ((p
= strchr(str
, '.')) != NULL
) {
2636 p
--; p
[1] = p
[0]; p
[0] = '.'; /* swap last digit & . */
2638 } else if (l
<= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2639 snprintf(str
, sizeof(str
), "(%luM)", l
/ 1024L);
2641 snprintf(str
, sizeof(str
), "(HUGE!)");
2647 if((body
= fetch_body(idata
)) != NULL
)
2651 mc
= (idata
->rawno
> 0L && idata
->stream
2652 && idata
->rawno
<= idata
->stream
->nmsgs
)
2653 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2654 if(mc
&& mc
->rfc822_size
< 6000)
2655 snprintf(str
, sizeof(str
), "(short )");
2656 else if(mc
&& mc
->rfc822_size
< 25000)
2657 snprintf(str
, sizeof(str
), "(medium )");
2658 else if(mc
&& mc
->rfc822_size
< 100000)
2659 snprintf(str
, sizeof(str
), "(long )");
2661 snprintf(str
, sizeof(str
), "(huge )");
2667 if(strucmp(body
->subtype
, "MIXED") == 0){
2670 x
= body
->nested
.part
2671 ? body
->nested
.part
->body
.type
2675 if(body
->nested
.part
->body
.size
.bytes
< 6000)
2676 snprintf(str
, sizeof(str
), "(short+ )");
2677 else if(body
->nested
.part
->body
.size
.bytes
2679 snprintf(str
, sizeof(str
), "(medium+)");
2680 else if(body
->nested
.part
->body
.size
.bytes
2682 snprintf(str
, sizeof(str
), "(long+ )");
2684 snprintf(str
, sizeof(str
), "(huge+ )");
2688 snprintf(str
, sizeof(str
), "(multi )");
2692 else if(strucmp(body
->subtype
, "DIGEST") == 0)
2693 snprintf(str
, sizeof(str
), "(digest )");
2694 else if(strucmp(body
->subtype
, "ALTERNATIVE") == 0)
2695 snprintf(str
, sizeof(str
), "(mul/alt)");
2696 else if(strucmp(body
->subtype
, "PARALLEL") == 0)
2697 snprintf(str
, sizeof(str
), "(mul/par)");
2699 snprintf(str
, sizeof(str
), "(multi )");
2704 snprintf(str
, sizeof(str
), "(message)");
2707 case TYPEAPPLICATION
:
2708 snprintf(str
, sizeof(str
), "(applica)");
2712 snprintf(str
, sizeof(str
), "(audio )");
2716 snprintf(str
, sizeof(str
), "(image )");
2720 snprintf(str
, sizeof(str
), "(video )");
2724 snprintf(str
, sizeof(str
), "(other )");
2733 if((body
= fetch_body(idata
)) &&
2734 body
->type
== TYPEMULTIPART
&&
2735 strucmp(body
->subtype
, "ALTERNATIVE") != 0){
2739 part
= body
->nested
.part
; /* 1st part, don't count */
2740 while(part
&& part
->next
&& atts
< 10){
2748 str
[0] = '0' + atts
;
2754 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, ice
);
2758 subj_str(idata
, str
, sizeof(str
), NoKW
, 1, ice
);
2762 subj_str(idata
, str
, sizeof(str
), KW
, 0, ice
);
2766 subj_str(idata
, str
, sizeof(str
), KW
, 1, ice
);
2770 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, ice
);
2773 case iSubjKeyInitText
:
2774 subj_str(idata
, str
, sizeof(str
), KWInit
, 1, ice
);
2778 case iOpeningTextNQ
:
2784 first_text
= fetch_firsttext(idata
, cdesc
->ctype
== iOpeningTextNQ
);
2787 strncpy(str
, first_text
, BIGWIDTH
);
2788 str
[BIGWIDTH
] = '\0';
2789 fs_give((void **) &first_text
);
2796 key_str(idata
, KW
, ice
);
2800 key_str(idata
, KWInit
, ice
);
2804 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
){
2805 strncpy(str
, newsgroups
, BIGWIDTH
);
2806 str
[BIGWIDTH
] = '\0';
2812 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2813 strncpy(str
, newsgroups
, sizeof(str
));
2815 if((l
= strlen(str
)) < sizeof(str
)){
2816 if(sizeof(str
) - l
< 6)
2817 strncpy(str
+l
, "...", sizeof(str
)-l
);
2820 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2821 set_index_addr(idata
, "To", fetch_to(idata
),
2822 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2827 set_index_addr(idata
, "To", fetch_to(idata
),
2828 NULL
, BIGWIDTH
, str
);
2835 set_index_addr(idata
, "To", fetch_to(idata
),
2836 NULL
, BIGWIDTH
, str
);
2837 if((l
= strlen(str
)) < sizeof(str
) &&
2838 (newsgroups
= fetch_newsgroups(idata
))){
2839 if(sizeof(str
) - l
< 6)
2840 strncpy(str
+l
, "...", sizeof(str
)-l
);
2843 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2846 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2848 strncpy(str
, newsgroups
, BIGWIDTH
);
2854 case iNewsAndRecips
:
2855 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2856 strncpy(str
, newsgroups
, BIGWIDTH
);
2858 if((l
= strlen(str
)) < BIGWIDTH
){
2859 if(BIGWIDTH
- l
< 6)
2860 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2862 toaddr
= fetch_to(idata
);
2863 ccaddr
= fetch_cc(idata
);
2864 for(last_to
= toaddr
;
2865 last_to
&& last_to
->next
;
2866 last_to
= last_to
->next
)
2869 /* point end of to list temporarily at cc list */
2871 last_to
->next
= ccaddr
;
2874 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2875 set_index_addr(idata
, "To", toaddr
,
2876 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2881 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2884 last_to
->next
= NULL
;
2890 case iRecipsAndNews
:
2891 toaddr
= fetch_to(idata
);
2892 ccaddr
= fetch_cc(idata
);
2893 for(last_to
= toaddr
;
2894 last_to
&& last_to
->next
;
2895 last_to
= last_to
->next
)
2898 /* point end of to list temporarily at cc list */
2900 last_to
->next
= ccaddr
;
2902 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2905 last_to
->next
= NULL
;
2907 if((l
= strlen(str
)) < BIGWIDTH
&&
2908 (newsgroups
= fetch_newsgroups(idata
))){
2909 if(BIGWIDTH
- l
< 6)
2910 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2913 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2916 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2918 strncpy(str
, newsgroups
, BIGWIDTH
);
2927 prio_str(idata
, cdesc
->ctype
, ice
);
2931 header_str(idata
, cdesc
->hdrtok
, ice
);
2935 strncpy(str
, (cdesc
->hdrtok
&& cdesc
->hdrtok
->hdrname
) ? cdesc
->hdrtok
->hdrname
: "", sizeof(str
));
2936 str
[sizeof(str
)-1] = '\0';
2944 * If the element wasn't already filled in above, do it here.
2947 ielem
= new_ielem(&ifield
->ielem
);
2949 if((color
= hdr_color(itokens
[itokensinv
[cdesc
->ctype
].ctype
].name
, NULL
, ps_global
->index_token_colors
)) != NULL
){
2950 if(pico_usingcolor()){
2951 ielem
->color
= new_color_pair(color
->fg
, color
->bg
);
2952 ielem
->type
= eTypeCol
;
2954 free_color_pair(&color
);
2957 ielem
->freedata
= 1;
2958 ielem
->data
= cpystr(str
);
2959 ielem
->datalen
= strlen(str
);
2961 if(fromfield
&& pico_usingcolor()
2962 && ps_global
->VAR_IND_FROM_FORE_COLOR
2963 && ps_global
->VAR_IND_FROM_BACK_COLOR
){
2964 ielem
->type
= eTypeCol
;
2965 ielem
->freecolor
= 1;
2966 ielem
->color
= new_color_pair(ps_global
->VAR_IND_FROM_FORE_COLOR
,
2967 ps_global
->VAR_IND_FROM_BACK_COLOR
);
2969 * This space is here so that if the text does
2970 * not extend all the way to the end of the field then
2971 * we'll switch the color back and paint the rest of the
2972 * field in the Normal color or the index line color.
2974 ielem
= new_ielem(&ielem
);
2975 ielem
->freedata
= 1;
2976 ielem
->data
= cpystr(" ");
2979 else if((cdesc
->ctype
== iOpeningText
|| cdesc
->ctype
== iOpeningTextNQ
)
2980 && pico_usingcolor()
2981 && ps_global
->VAR_IND_OP_FORE_COLOR
2982 && ps_global
->VAR_IND_OP_BACK_COLOR
){
2983 ielem
->type
= eTypeCol
;
2984 ielem
->freecolor
= 1;
2985 ielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
,
2986 ps_global
->VAR_IND_OP_BACK_COLOR
);
2988 * This space is here so that if the text does
2989 * not extend all the way to the end of the field then
2990 * we'll switch the color back and paint the rest of the
2991 * field in the Normal color or the index line color.
2993 ielem
= new_ielem(&ielem
);
2994 ielem
->freedata
= 1;
2995 ielem
->data
= cpystr(" ");
2999 ifield
->leftadj
= (cdesc
->adjustment
== Left
) ? 1 : 0;
3000 set_ielem_widths_in_field(ifield
);
3004 ice
->widths_done
= 1;
3005 ice
->id
= ice_hash(ice
);
3008 * Now we have to put the temporary copy of ice back as the
3011 icep
= fetch_ice_ptr(idata
->stream
, idata
->rawno
);
3013 free_ice(icep
); /* free what is already there */
3022 format_thread_index_line(INDEXDATA_S
*idata
)
3024 char *p
, buffer
[BIGWIDTH
+1];
3025 int thdlen
, space_left
, i
;
3026 PINETHRD_S
*thrd
= NULL
;
3027 ICE_S
*ice
, *tice
= NULL
, **ticep
= NULL
;
3030 int (*save_sfstr_func
)(void);
3031 struct variable
*vars
= ps_global
->vars
;
3033 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3034 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
3036 space_left
= ps_global
->ttyo
->screen_cols
;
3038 if(ps_global
->msgmap
->max_thrdno
< 1000)
3040 else if(ps_global
->msgmap
->max_thrdno
< 10000)
3042 else if(ps_global
->msgmap
->max_thrdno
< 100000)
3047 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
3049 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
3051 if(!thrd
|| !ice
) /* can't happen? */
3055 tice
= (ICE_S
*) fs_get(sizeof(*tice
));
3056 memset(tice
, 0, sizeof(*tice
));
3065 free_ifield(&tice
->ifield
);
3068 tice
= copy_ice(tice
);
3070 if(space_left
>= 3){
3074 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
3075 status
= status_symbol_for_thread(idata
->stream
, thrd
, iStatus
);
3077 if((p
-buffer
)+3 < sizeof(buffer
)){
3086 ifield
= new_ifield(&tice
->ifield
);
3087 ifield
->ctype
= iStatus
;
3089 ifield
->leftadj
= 1;
3090 for(i
= 0; i
< 3; i
++){
3091 ielem
= new_ielem(&ifield
->ielem
);
3092 ielem
->freedata
= 1;
3093 ielem
->data
= (char *) fs_get(2 * sizeof(char));
3094 ielem
->data
[0] = p
[i
];
3095 ielem
->data
[1] = '\0';
3097 set_print_format(ielem
, 1, ifield
->leftadj
);
3100 if(pico_usingcolor()){
3102 && VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
3103 ielem
= ifield
->ielem
;
3104 ielem
->freecolor
= 1;
3105 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3106 VAR_IND_IMP_BACK_COLOR
);
3107 if(F_ON(F_COLOR_LINE_IMPORTANT
, ps_global
))
3108 tice
->linecolor
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3109 VAR_IND_IMP_BACK_COLOR
);
3111 else if((to_us
== '+' || to_us
== '-')
3112 && VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
3113 ielem
= ifield
->ielem
;
3114 ielem
->freecolor
= 1;
3115 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
,
3116 VAR_IND_PLUS_BACK_COLOR
);
3120 && VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
3121 ielem
= ifield
->ielem
->next
->next
;
3122 ielem
->freecolor
= 1;
3123 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
,
3124 VAR_IND_DEL_BACK_COLOR
);
3126 else if(status
== 'N'
3127 && VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
3128 ielem
= ifield
->ielem
->next
->next
;
3129 ielem
->freecolor
= 1;
3130 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
,
3131 VAR_IND_NEW_BACK_COLOR
);
3136 if(space_left
>= thdlen
+1){
3140 snprintf(p
, sizeof(buffer
), "%*.*s", thdlen
, thdlen
, "");
3141 space_left
-= thdlen
;
3143 ifield
= new_ifield(&tice
->ifield
);
3144 ifield
->ctype
= iMessNo
;
3145 ifield
->width
= thdlen
;
3146 ifield
->leftadj
= 0;
3147 ielem
= new_ielem(&ifield
->ielem
);
3148 ielem
->freedata
= 1;
3149 ielem
->data
= cpystr(p
);
3150 ielem
->datalen
= strlen(p
);
3151 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3154 if(space_left
>= 7){
3159 date_str(fetch_date(idata
), iDate
, 0, p
, sizeof(buffer
), 0);
3160 if(sizeof(buffer
) > 6)
3163 if(strlen(p
) < 6 && (sizeof(buffer
)) > 6){
3166 for(q
= p
+ strlen(p
); q
< p
+ 6; q
++)
3172 ifield
= new_ifield(&tice
->ifield
);
3173 ifield
->ctype
= iDate
;
3175 ifield
->leftadj
= 1;
3176 ielem
= new_ielem(&ifield
->ielem
);
3177 ielem
->freedata
= 1;
3178 ielem
->data
= cpystr(p
);
3179 ielem
->datalen
= ifield
->width
;
3180 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3185 int from_width
, subj_width
, bigthread_adjust
;
3187 char from
[BIGWIDTH
+1];
3192 in_thread
= count_lflags_in_thread(idata
->stream
, thrd
,
3193 ps_global
->msgmap
, MN_NONE
);
3196 if(in_thread
== 1 && THRD_AUTO_VIEW())
3197 snprintf(tcnt
, sizeof(tcnt
), " ");
3199 snprintf(tcnt
, sizeof(tcnt
), "(%ld)", in_thread
);
3201 bigthread_adjust
= MAX(0, strlen(tcnt
) - 3);
3203 /* third of the rest */
3204 from_width
= MAX((space_left
-1)/3 - bigthread_adjust
, 1);
3207 subj_width
= space_left
- from_width
- 1;
3209 if(strlen(tcnt
) > subj_width
)
3210 tcnt
[subj_width
] = '\0';
3213 save_sfstr_func
= pith_opt_truncate_sfstr
;
3214 pith_opt_truncate_sfstr
= NULL
;
3215 from_str(iFromTo
, idata
, from
, sizeof(from
), tice
);
3216 pith_opt_truncate_sfstr
= save_sfstr_func
;
3218 ifield
= new_ifield(&tice
->ifield
);
3219 ifield
->leftadj
= 1;
3220 ielem
= new_ielem(&ifield
->ielem
);
3221 ielem
->freedata
= 1;
3222 ielem
->type
= eTypeCol
;
3223 ielem
->data
= cpystr(from
);
3224 ielem
->datalen
= strlen(from
);
3225 ifield
->width
= from_width
;
3226 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3227 ifield
->ctype
= iFrom
;
3228 if(from_width
> 0 && pico_usingcolor()
3229 && VAR_IND_FROM_FORE_COLOR
&& VAR_IND_FROM_BACK_COLOR
){
3230 ielem
->freecolor
= 1;
3231 ielem
->color
= new_color_pair(VAR_IND_FROM_FORE_COLOR
,
3232 VAR_IND_FROM_BACK_COLOR
);
3235 ifield
= new_ifield(&tice
->ifield
);
3236 ifield
->leftadj
= 0;
3237 ielem
= new_ielem(&ifield
->ielem
);
3238 ielem
->freedata
= 1;
3239 ielem
->data
= cpystr(tcnt
);
3240 ielem
->datalen
= strlen(tcnt
);
3241 ifield
->width
= ielem
->datalen
;
3242 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3243 ifield
->ctype
= iAtt
; /* not used, except that it isn't special */
3245 subj_width
-= strlen(tcnt
);
3252 if(idata
->bogus
< 2)
3253 snprintf(buffer
, sizeof(buffer
), "%-.*s", BIGWIDTH
,
3254 _("[ No Message Text Available ]"));
3258 save_sfstr_func
= pith_opt_truncate_sfstr
;
3259 pith_opt_truncate_sfstr
= NULL
;
3260 subj_str(idata
, buffer
, sizeof(buffer
), NoKW
, 0, NULL
);
3261 pith_opt_truncate_sfstr
= save_sfstr_func
;
3264 ifield
= new_ifield(&tice
->ifield
);
3265 ifield
->leftadj
= 1;
3266 ielem
= new_ielem(&ifield
->ielem
);
3267 ielem
->freedata
= 1;
3268 ielem
->type
= eTypeCol
;
3269 ielem
->data
= cpystr(buffer
);
3270 ielem
->datalen
= strlen(buffer
);
3271 ifield
->width
= subj_width
;
3272 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3273 ifield
->ctype
= iSubject
;
3274 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR
&& VAR_IND_SUBJ_BACK_COLOR
){
3275 ielem
->freecolor
= 1;
3276 ielem
->color
= new_color_pair(VAR_IND_SUBJ_FORE_COLOR
,
3277 VAR_IND_SUBJ_BACK_COLOR
);
3281 else if(space_left
> 1){
3282 snprintf(p
, sizeof(buffer
)-(p
-buffer
), "%-.*s", space_left
-1, " ");
3283 ifield
= new_ifield(&tice
->ifield
);
3284 ifield
->leftadj
= 1;
3285 ielem
= new_ielem(&ifield
->ielem
);
3286 ielem
->freedata
= 1;
3287 ielem
->data
= cpystr(p
);
3288 ielem
->datalen
= strlen(p
);
3289 ifield
->width
= space_left
-1;
3290 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3291 ifield
->ctype
= iSubject
;
3294 tice
->widths_done
= 1;
3295 tice
->id
= ice_hash(tice
);
3298 free_ice(ticep
); /* free what is already there */
3307 * Print the fields of ice in buf with a single space between fields.
3309 * Args buf -- place to put the line
3310 * ice -- the data for the line
3311 * msgno -- this is the msgno to be used, blanks if <= 0
3313 * Returns a pointer to buf.
3316 simple_index_line(char *buf
, size_t buflen
, ICE_S
*ice
, long int msgno
)
3319 IFIELD_S
*ifield
, *previfield
= NULL
;
3323 alpine_panic("NULL buf in simple_index_line()");
3332 for(ifield
= ice
->ifield
; ifield
&& p
-buf
< buflen
; ifield
= ifield
->next
){
3334 /* space between fields */
3335 if(ifield
!= ice
->ifield
&& !(previfield
&& previfield
->ctype
== iText
))
3338 /* message number string is generated on the fly */
3339 if(ifield
->ctype
== iMessNo
){
3340 ielem
= ifield
->ielem
;
3341 if(ielem
&& ielem
->datalen
>= ifield
->width
){
3343 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.ld", ifield
->width
, msgno
);
3345 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.*s", ifield
->width
, ifield
->width
, "");
3349 for(ielem
= ifield
->ielem
;
3350 ielem
&& ielem
->print_format
&& p
-buf
< buflen
;
3351 ielem
= ielem
->next
){
3356 bytes_added
= utf8_pad_to_width(p
, src
,
3357 buflen
-(p
-buf
) * sizeof(char),
3358 ielem
->wid
, ifield
->leftadj
);
3362 previfield
= ifield
;
3369 buf
[buflen
-1] = '\0';
3376 * Look in current mail_stream for matches for messages in the searchset
3377 * which match a color rule pattern. Return the color.
3378 * The searched bit will be set for all of the messages which match the
3379 * first pattern which has a match.
3381 * Args stream -- the mail stream
3382 * searchset -- restrict attention to this set of messages
3383 * pstate -- The pattern state. On the first call it will be Null.
3384 * Null means start over with a new first_pattern.
3385 * After that it will be pointing to our local PAT_STATE
3386 * so that next_pattern goes to the next one after the
3387 * ones we've already checked.
3389 * Returns 0 if no match, 1 if a match.
3390 * The color that goes with the matched rule in returned_color.
3391 * It may be NULL, which indicates default.
3394 get_index_line_color(MAILSTREAM
*stream
, SEARCHSET
*searchset
,
3395 PAT_STATE
**pstate
, COLOR_PAIR
**returned_color
)
3398 long rflags
= ROLE_INCOL
;
3399 COLOR_PAIR
*color
= NULL
;
3401 static PAT_STATE localpstate
;
3403 dprint((7, "get_index_line_color\n"));
3406 *returned_color
= NULL
;
3409 pat
= next_pattern(*pstate
);
3411 *pstate
= &localpstate
;
3412 if(!nonempty_patterns(rflags
, *pstate
))
3416 pat
= first_pattern(*pstate
);
3421 /* Go through the possible roles one at a time until we get a match. */
3422 while(!match
&& pat
){
3423 if(match_pattern(pat
->patgrp
, stream
, searchset
, NULL
,
3424 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
3425 if(!pat
->action
|| pat
->action
->bogus
)
3429 if(pat
->action
&& pat
->action
->incol
)
3430 color
= new_color_pair(pat
->action
->incol
->fg
,
3431 pat
->action
->incol
->bg
);
3434 pat
= next_pattern(*pstate
);
3438 if(match
&& returned_color
)
3439 *returned_color
= color
;
3449 index_in_overview(MAILSTREAM
*stream
)
3451 INDEX_COL_S
*cdesc
= NULL
;
3453 if(!(stream
->mailbox
&& IS_REMOTE(stream
->mailbox
)))
3454 return(FALSE
); /* no point! */
3456 if(stream
->dtb
&& stream
->dtb
->name
&& !strcmp(stream
->dtb
->name
, "nntp")){
3461 for(cdesc
= ps_global
->index_disp_format
;
3462 cdesc
->ctype
!= iNothing
;
3464 switch(cdesc
->ctype
){
3465 case iTo
: /* can't be satisfied by XOVER */
3466 case iSender
: /* ... or specifically handled */
3467 case iDescripSize
: /* ... in news case */
3482 * fetch_from - called to get a the index entry's "From:" field
3485 resent_to_us(INDEXDATA_S
*idata
)
3487 if(!idata
->valid_resent_to
){
3488 static char *fields
[] = {"Resent-To", NULL
};
3491 if(idata
->no_fetch
){
3492 idata
->bogus
= 1; /* don't do this */
3496 if((h
= pine_fetchheader_lines(idata
->stream
,idata
->rawno
,NULL
,fields
)) != NULL
){
3497 idata
->resent_to_us
= parsed_resent_to_us(h
);
3498 fs_give((void **) &h
);
3501 idata
->valid_resent_to
= 1;
3504 return(idata
->resent_to_us
);
3509 parsed_resent_to_us(char *h
)
3512 ADDRESS
*addr
= NULL
;
3515 if((p
= strindex(h
, ':')) != NULL
){
3516 for(q
= ++p
; (q
= strpbrk(q
, "\015\012")) != NULL
; q
++)
3517 *q
= ' '; /* quash junk */
3519 rfc822_parse_adrlist(&addr
, p
, ps_global
->maildomain
);
3521 rv
= address_is_us(addr
, ps_global
);
3522 mail_free_address(&addr
);
3532 * fetch_from - called to get a the index entry's "From:" field
3535 fetch_from(INDEXDATA_S
*idata
)
3537 if(idata
->no_fetch
) /* implies from is valid */
3538 return(idata
->from
);
3539 else if(idata
->bogus
)
3544 /* c-client call's just cache access at this point */
3545 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3556 * fetch_to - called to get a the index entry's "To:" field
3559 fetch_to(INDEXDATA_S
*idata
)
3561 if(idata
->no_fetch
){ /* check for specific validity */
3565 idata
->bogus
= 1; /* can't give 'em what they want */
3567 else if(idata
->bogus
){
3568 idata
->bogus
= 2; /* elevate bogosity */
3573 /* c-client call's just cache access at this point */
3574 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3585 * fetch_cc - called to get a the index entry's "Cc:" field
3588 fetch_cc(INDEXDATA_S
*idata
)
3590 if(idata
->no_fetch
){ /* check for specific validity */
3594 idata
->bogus
= 1; /* can't give 'em what they want */
3596 else if(idata
->bogus
){
3597 idata
->bogus
= 2; /* elevate bogosity */
3602 /* c-client call's just cache access at this point */
3603 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3615 * fetch_sender - called to get a the index entry's "Sender:" field
3618 fetch_sender(INDEXDATA_S
*idata
)
3620 if(idata
->no_fetch
){ /* check for specific validity */
3621 if(idata
->valid_sender
)
3622 return(idata
->sender
);
3624 idata
->bogus
= 1; /* can't give 'em what they want */
3626 else if(idata
->bogus
){
3627 idata
->bogus
= 2; /* elevate bogosity */
3632 /* c-client call's just cache access at this point */
3633 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3634 return(env
->sender
);
3644 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3647 fetch_newsgroups(INDEXDATA_S
*idata
)
3649 if(idata
->no_fetch
){ /* check for specific validity */
3650 if(idata
->valid_news
)
3651 return(idata
->newsgroups
);
3653 idata
->bogus
= 1; /* can't give 'em what they want */
3655 else if(idata
->bogus
){
3656 idata
->bogus
= 2; /* elevate bogosity */
3661 /* c-client call's just cache access at this point */
3662 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3663 return(env
->newsgroups
);
3673 * fetch_subject - called to get at the index entry's "Subject:" field
3676 fetch_subject(INDEXDATA_S
*idata
)
3678 if(idata
->no_fetch
) /* implies subject is valid */
3679 return(idata
->subject
);
3680 else if(idata
->bogus
)
3685 /* c-client call's just cache access at this point */
3686 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3687 return(env
->subject
);
3697 * Return an allocated copy of the first few characters from the body
3698 * of the message for possible use in the index screen.
3700 * Maybe we could figure out some way to do aggregate calls to get
3701 * this info for all the lines in view instead of all the one at a
3702 * time calls we're doing now.
3705 fetch_firsttext(INDEXDATA_S
*idata
, int delete_quotes
)
3709 char *firsttext
= NULL
;
3712 long partial_fetch_len
= 0L;
3713 SEARCHSET
*ss
, **sset
;
3718 * Prevent wild prefetch, just get the one we're after.
3719 * Can we get this somehow in the overview call in build_header_work?
3721 ss
= mail_newsearchset();
3722 ss
->first
= idata
->rawno
;
3723 sset
= (SEARCHSET
**) mail_parameters(idata
->stream
,
3725 (void *) idata
->stream
);
3729 if((env
= pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
)) != NULL
){
3731 char *subtype
= NULL
;
3734 if((body
->type
== TYPETEXT
3735 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3737 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3738 && body
->nested
.part
->body
.type
== TYPETEXT
3739 && (subtype
=body
->nested
.part
->body
.subtype
)
3740 && ALLOWED_SUBTYPE(subtype
))
3742 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3743 && body
->nested
.part
->body
.type
== TYPEMULTIPART
3744 && body
->nested
.part
->body
.nested
.part
3745 && body
->nested
.part
->body
.nested
.part
->body
.type
== TYPETEXT
3746 && (subtype
=body
->nested
.part
->body
.nested
.part
->body
.subtype
)
3747 && ALLOWED_SUBTYPE(subtype
))){
3749 if((so
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
3753 int one_space_done
= 0;
3755 if(partial_fetch_len
== 0L){
3756 if(subtype
&& !strucmp(subtype
, "html"))
3757 partial_fetch_len
= 1024L;
3758 else if(subtype
&& !strucmp(subtype
, "plain"))
3759 partial_fetch_len
= delete_quotes
? 128L : 64L;
3761 partial_fetch_len
= 256L;
3764 if((body
->type
== TYPETEXT
3765 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3767 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3768 && body
->nested
.part
->body
.type
== TYPETEXT
3769 && (subtype
=body
->nested
.part
->body
.subtype
)
3770 && ALLOWED_SUBTYPE(subtype
)))
3775 gf_set_so_writec(&pc
, so
);
3776 success
= get_body_part_text(idata
->stream
, body
, idata
->rawno
,
3777 partno
, partial_fetch_len
, pc
,
3779 GBPT_NOINTR
| GBPT_PEEK
|
3780 (delete_quotes
? GBPT_DELQUOTES
: 0));
3781 gf_clear_so_writec(so
);
3786 while(p
-buf
< sizeof(buf
)-1 && so_readc(&c
, so
)){
3787 /* delete leading whitespace */
3788 if(p
== buf
&& isspace(c
))
3790 /* and include just one space per run of whitespace */
3791 else if(isspace(c
)){
3792 if(!one_space_done
){
3810 firsttext
= fs_get((l
+1) * sizeof(char));
3811 firsttext
[0] = '\0';
3812 iutf8ncpy(firsttext
, buf
, l
);
3813 firsttext
[l
] = '\0';
3814 removing_trailing_white_space(firsttext
);
3820 /* first if means we didn't fetch all of the data */
3821 if(!(success
> 1 && success
< partial_fetch_len
)){
3822 if(partial_fetch_len
< 4096L
3823 && (!firsttext
|| utf8_width(firsttext
) < 50)){
3825 fs_give((void **) &firsttext
);
3827 partial_fetch_len
= 4096L;
3837 mail_free_searchset(&ss
);
3844 * fetch_date - called to get at the index entry's "Date:" field
3847 fetch_date(INDEXDATA_S
*idata
)
3849 if(idata
->no_fetch
) /* implies date is valid */
3850 return(idata
->date
);
3851 else if(idata
->bogus
)
3856 /* c-client call's just cache access at this point */
3857 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3858 return((char *) env
->date
);
3868 * fetch_header - called to get at the index entry's "Hdrname:" field
3871 fetch_header(INDEXDATA_S
*idata
, char *hdrname
)
3875 else if(idata
->bogus
)
3878 char *h
, *p
, *q
, *decoded
, *fields
[2];
3879 size_t retsize
, decsize
;
3881 unsigned char *decode_buf
= NULL
;
3883 fields
[0] = hdrname
;
3885 if(hdrname
&& hdrname
[0]
3886 && (h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
3889 if(strlen(h
) < strlen(hdrname
) + 1){
3890 fs_give((void **) &h
);
3894 /* skip "hdrname:" */
3895 for(p
= h
+ strlen(hdrname
) + 1;
3896 *p
&& isspace((unsigned char)*p
); p
++)
3899 decsize
= (4 * strlen(p
)) + 1;
3900 decode_buf
= (unsigned char *) fs_get(decsize
* sizeof(unsigned char));
3901 decoded
= (char *) rfc1522_decode_to_utf8(decode_buf
, decsize
, p
);
3904 retsize
= strlen(decoded
);
3905 q
= ret
= (char *) fs_get((retsize
+1) * sizeof(char));
3908 while(q
-ret
< retsize
&& *p
){
3909 if(*p
== '\015' || *p
== '\012')
3911 else if(*p
== '\t'){
3921 fs_give((void **) &h
);
3923 fs_give((void **) &decode_buf
);
3936 * fetch_size - called to get at the index entry's "size" field
3939 fetch_size(INDEXDATA_S
*idata
)
3941 if(idata
->no_fetch
) /* implies size is valid */
3942 return(idata
->size
);
3943 else if(idata
->bogus
)
3948 if(idata
->stream
&& idata
->rawno
> 0L
3949 && idata
->rawno
<= idata
->stream
->nmsgs
3950 && (mc
= mail_elt(idata
->stream
, idata
->rawno
)))
3951 return(mc
->rfc822_size
);
3961 * fetch_body - called to get a the index entry's body structure
3964 fetch_body(INDEXDATA_S
*idata
)
3968 if(idata
->bogus
|| idata
->no_fetch
){
3973 if(pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
))
3982 * s is at least size width+1
3985 set_index_addr(INDEXDATA_S
*idata
,
3987 struct mail_address
*addr
,
3993 char *p
, *stmp
= NULL
, *sptr
;
3994 char *save_personal
= NULL
;
3999 for(atmp
= addr
; idata
->stream
&& atmp
; atmp
= atmp
->next
)
4000 if(atmp
->host
&& atmp
->host
[0] == '.'){
4001 char *pref
, *h
, *fields
[2];
4003 if(idata
->no_fetch
){
4010 if((h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
4011 NULL
, fields
)) != NULL
){
4012 if(strlen(h
) < strlen(field
) + 1){
4017 for(p
= h
+ strlen(field
) + 1;
4018 *p
&& isspace((unsigned char)*p
); p
++)
4023 sptr
= stmp
= (char *) fs_get((orig_width
+1) * sizeof(char));
4026 for(pref
= prefix
; pref
&& *pref
; pref
++)
4035 if(*p
== '\015' || *p
== '\012')
4036 p
++; /* skip CR LF */
4039 else if(*p
== '\t'){
4046 *sptr
= '\0'; /* tie off return string */
4049 iutf8ncpy(s
, stmp
, orig_width
+1);
4050 s
[orig_width
] = '\0';
4051 fs_give((void **) &stmp
);
4054 fs_give((void **) &h
);
4057 /* else fall thru and display what c-client gave us */
4060 if(addr
&& !addr
->next
/* only one address */
4061 && addr
->host
/* not group syntax */
4062 && addr
->personal
&& addr
->personal
[0]){ /* there is a personal name */
4063 char buftmp
[MAILTMPLEN
];
4066 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4067 strncpy(s
, prefix
, width
+1);
4069 snprintf(buftmp
, sizeof(buftmp
), "%s", addr
->personal
);
4070 p
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
4071 SIZEOF_20KBUF
, buftmp
);
4072 removing_leading_and_trailing_white_space(p
);
4074 iutf8ncpy(s
+ l
, p
, width
- l
);
4081 save_personal
= addr
->personal
;
4082 addr
->personal
= NULL
;
4090 a_string
= addr_list_string(addr
, NULL
, 0);
4092 addr
->personal
= save_personal
;
4094 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4095 strncpy(s
, prefix
, width
+1);
4097 iutf8ncpy(s
+ l
, a_string
, width
- l
);
4100 fs_give((void **)&a_string
);
4106 addr
->personal
= save_personal
;
4113 index_data_env(INDEXDATA_S
*idata
, ENVELOPE
*env
)
4120 idata
->from
= env
->from
;
4121 idata
->to
= env
->to
;
4122 idata
->cc
= env
->cc
;
4123 idata
->sender
= env
->sender
;
4124 idata
->subject
= env
->subject
;
4125 idata
->date
= (char *) env
->date
;
4126 idata
->newsgroups
= env
->newsgroups
;
4128 idata
->valid_to
= 1; /* signal that everythings here */
4129 idata
->valid_cc
= 1;
4130 idata
->valid_sender
= 1;
4131 idata
->valid_news
= 1;
4136 * Put a string representing the date into str. The source date is
4137 * in the string datesrc. The format to be used is in type.
4138 * Notice that type is an IndexColType, but really only a subset of
4139 * IndexColType types are allowed.
4141 * Args datesrc -- The source date string
4142 * type -- What type of output we want
4143 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4144 * str -- Put the answer here.
4145 * str_len -- Length of str
4146 * monabb_width -- This is a hack to get dates to line up right. For
4147 * example, in French (but without accents here)
4151 * For this monabb_width would be 5.
4154 date_str(char *datesrc
, IndexColType type
, int v
, char *str
, size_t str_len
,
4157 char year4
[5], /* 4 digit year */
4158 yearzero
[3], /* zero padded, 2-digit year */
4159 monzero
[3], /* zero padded, 2-digit month */
4160 mon
[3], /* 1 or 2-digit month, no pad */
4161 dayzero
[3], /* zero padded, 2-digit day */
4162 day
[3], /* 1 or 2-digit day, no pad */
4163 dayord
[3], /* 2-letter ordinal label */
4164 monabb
[10], /* 3-letter month abbrev */
4165 /* actually maybe not 3 if localized */
4166 hour24
[3], /* 2-digit, 24 hour clock hour */
4167 hour12
[3], /* 12 hour clock hour, no pad */
4168 minzero
[3], /* zero padded, 2-digit minutes */
4169 timezone
[6]; /* timezone, like -0800 or +... */
4171 int curtype
, lastmonthtype
, lastyeartype
, preftype
;
4172 int sdatetimetype
, sdatetime24type
;
4174 #define TODAYSTR N_("Today")
4176 curtype
= (type
== iCurDate
||
4177 type
== iCurDateIso
||
4178 type
== iCurDateIsoS
||
4179 type
== iCurPrefDate
||
4180 type
== iCurPrefDateTime
||
4181 type
== iCurPrefTime
||
4182 type
== iCurTime24
||
4183 type
== iCurTime12
||
4185 type
== iCurDay2Digit
||
4186 type
== iCurDayOfWeek
||
4187 type
== iCurDayOfWeekAbb
||
4189 type
== iCurMon2Digit
||
4190 type
== iCurMonLong
||
4191 type
== iCurMonAbb
||
4193 type
== iCurYear2Digit
);
4194 lastmonthtype
= (type
== iLstMon
||
4195 type
== iLstMon2Digit
||
4196 type
== iLstMonLong
||
4197 type
== iLstMonAbb
||
4198 type
== iLstMonYear
||
4199 type
== iLstMonYear2Digit
);
4200 lastyeartype
= (type
== iLstYear
||
4201 type
== iLstYear2Digit
);
4202 sdatetimetype
= (type
== iSDateTime
||
4203 type
== iSDateTimeIso
||
4204 type
== iSDateTimeIsoS
||
4205 type
== iSDateTimeS1
||
4206 type
== iSDateTimeS2
||
4207 type
== iSDateTimeS3
||
4208 type
== iSDateTimeS4
||
4209 type
== iSDateTime24
||
4210 type
== iSDateTimeIso24
||
4211 type
== iSDateTimeIsoS24
||
4212 type
== iSDateTimeS124
||
4213 type
== iSDateTimeS224
||
4214 type
== iSDateTimeS324
||
4215 type
== iSDateTimeS424
);
4216 sdatetime24type
= (type
== iSDateTime24
||
4217 type
== iSDateTimeIso24
||
4218 type
== iSDateTimeIsoS24
||
4219 type
== iSDateTimeS124
||
4220 type
== iSDateTimeS224
||
4221 type
== iSDateTimeS324
||
4222 type
== iSDateTimeS424
);
4223 preftype
= (type
== iPrefDate
||
4224 type
== iPrefDateTime
||
4225 type
== iPrefTime
||
4226 type
== iCurPrefDate
||
4227 type
== iCurPrefDateTime
||
4228 type
== iCurPrefTime
);
4232 if(!(datesrc
&& datesrc
[0]) && !(curtype
|| lastmonthtype
|| lastyeartype
))
4235 if(curtype
|| lastmonthtype
|| lastyeartype
){
4239 parse_date(dbuf
, &d
);
4243 else if(lastmonthtype
){
4252 parse_date(F_ON(F_DATES_TO_LOCAL
,ps_global
)
4253 ? convert_date_to_local(datesrc
) : datesrc
, &d
);
4254 if(d
.year
== -1 || d
.month
== -1 || d
.day
== -1){
4256 sdatetime24type
= 0;
4259 case iSDate
: case iSDateTime
: case iSDateTime24
:
4263 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4264 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
4268 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4272 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4276 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4280 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4284 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4294 /* some special ones to start with */
4296 struct tm tm
, *tmptr
= NULL
;
4300 * Make sure we get the right one if we're using current time.
4303 now
= time((time_t *) 0);
4304 if(now
!= (time_t) -1)
4305 tmptr
= localtime(&now
);
4309 memset(&tm
, 0, sizeof(tm
));
4310 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4311 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4312 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4313 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4314 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4315 tm
.tm_wday
= MIN(MAX(d
.wkday
, 0), 6);
4322 our_strftime(str
, str_len
, "%x", tmptr
);
4326 our_strftime(str
, str_len
, "%X", tmptr
);
4329 case iCurPrefDateTime
:
4330 our_strftime(str
, str_len
, "%c", tmptr
);
4340 strncpy(monabb
, (d
.month
> 0 && d
.month
< 13)
4341 ? month_abbrev_locale(d
.month
) : "", sizeof(monabb
));
4342 monabb
[sizeof(monabb
)-1] = '\0';
4344 strncpy(mon
, (d
.month
> 0 && d
.month
< 13)
4345 ? int2string(d
.month
) : "", sizeof(mon
));
4346 mon
[sizeof(mon
)-1] = '\0';
4348 strncpy(day
, (d
.day
> 0 && d
.day
< 32)
4349 ? int2string(d
.day
) : "", sizeof(day
));
4350 day
[sizeof(day
)-1] = '\0';
4353 (d
.day
<= 0 || d
.day
> 31) ? "" :
4354 (d
.day
== 1 || d
.day
== 21 || d
.day
== 31) ? "st" :
4355 (d
.day
== 2 || d
.day
== 22 ) ? "nd" :
4356 (d
.day
== 3 || d
.day
== 23 ) ? "rd" : "th", sizeof(dayord
));
4358 dayord
[sizeof(dayord
)-1] = '\0';
4360 strncpy(year4
, (d
.year
>= 1000 && d
.year
< 10000)
4361 ? int2string(d
.year
) : "????", sizeof(year4
));
4362 year4
[sizeof(year4
)-1] = '\0';
4365 if((d
.year
% 100) < 10){
4367 strncpy(yearzero
+1, int2string(d
.year
% 100), sizeof(yearzero
)-1);
4370 strncpy(yearzero
, int2string(d
.year
% 100), sizeof(yearzero
));
4373 strncpy(yearzero
, "??", sizeof(yearzero
));
4375 yearzero
[sizeof(yearzero
)-1] = '\0';
4377 if(d
.month
> 0 && d
.month
< 10){
4379 strncpy(monzero
+1, int2string(d
.month
), sizeof(monzero
)-1);
4381 else if(d
.month
>= 10 && d
.month
<= 12)
4382 strncpy(monzero
, int2string(d
.month
), sizeof(monzero
));
4384 strncpy(monzero
, "??", sizeof(monzero
));
4386 monzero
[sizeof(monzero
)-1] = '\0';
4388 if(d
.day
> 0 && d
.day
< 10){
4390 strncpy(dayzero
+1, int2string(d
.day
), sizeof(dayzero
)-1);
4392 else if(d
.day
>= 10 && d
.day
<= 31)
4393 strncpy(dayzero
, int2string(d
.day
), sizeof(dayzero
));
4395 strncpy(dayzero
, "??", sizeof(dayzero
));
4397 dayzero
[sizeof(dayzero
)-1] = '\0';
4399 hr12
= (d
.hour
== 0) ? 12 :
4400 (d
.hour
> 12) ? (d
.hour
- 12) : d
.hour
;
4402 if(hr12
> 0 && hr12
<= 12)
4403 strncpy(hour12
, int2string(hr12
), sizeof(hour12
));
4405 hour12
[sizeof(hour12
)-1] = '\0';
4408 if(d
.hour
>= 0 && d
.hour
< 10){
4410 strncpy(hour24
+1, int2string(d
.hour
), sizeof(hour24
)-1);
4412 else if(d
.hour
>= 10 && d
.hour
< 24)
4413 strncpy(hour24
, int2string(d
.hour
), sizeof(hour24
));
4415 hour24
[sizeof(hour24
)-1] = '\0';
4418 if(d
.minute
>= 0 && d
.minute
< 10){
4420 strncpy(minzero
+1, int2string(d
.minute
), sizeof(minzero
)-1);
4422 else if(d
.minute
>= 10 && d
.minute
<= 60)
4423 strncpy(minzero
, int2string(d
.minute
), sizeof(minzero
));
4425 minzero
[sizeof(minzero
)-1] = '\0';
4427 if(sizeof(timezone
) > 5){
4428 if(d
.hours_off_gmt
<= 0){
4430 d
.hours_off_gmt
*= -1;
4431 d
.min_off_gmt
*= -1;
4437 if(d
.hours_off_gmt
>= 0 && d
.hours_off_gmt
< 10){
4439 strncpy(timezone
+2, int2string(d
.hours_off_gmt
), sizeof(timezone
)-2);
4441 else if(d
.hours_off_gmt
>= 10 && d
.hours_off_gmt
< 24)
4442 strncpy(timezone
+1, int2string(d
.hours_off_gmt
), sizeof(timezone
)-1);
4449 if(d
.min_off_gmt
>= 0 && d
.min_off_gmt
< 10){
4451 strncpy(timezone
+4, int2string(d
.min_off_gmt
), sizeof(timezone
)-4);
4453 else if(d
.min_off_gmt
>= 10 && d
.min_off_gmt
<= 60)
4454 strncpy(timezone
+3, int2string(d
.min_off_gmt
), sizeof(timezone
)-3);
4461 timezone
[sizeof(timezone
)-1] = '\0';
4466 /* this one is not locale-specific */
4467 snprintf(str
, str_len
, "%s%s%s %s %s",
4468 (d
.wkday
!= -1) ? day_abbrev(d
.wkday
) : "",
4469 (d
.wkday
!= -1) ? ", " : "",
4471 (d
.month
> 0 && d
.month
< 13) ? month_abbrev(d
.month
) : "",
4475 case iCurDayOfWeekAbb
:
4476 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_abbrev_locale(d
.wkday
) : "", str_len
);
4477 str
[str_len
-1] = '\0';
4481 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_name_locale(d
.wkday
) : "", str_len
);
4482 str
[str_len
-1] = '\0';
4488 strncpy(str
, year4
, str_len
);
4492 strncpy(str
, dayzero
, str_len
);
4497 strncpy(str
, monzero
, str_len
);
4500 case iCurYear2Digit
:
4501 case iLstYear2Digit
:
4502 case iLstMonYear2Digit
:
4503 strncpy(str
, yearzero
, str_len
);
4506 strncpy(str
, timezone
, str_len
);
4510 strncpy(str
, day
, str_len
);
4513 snprintf(str
, str_len
, "%s%s", day
, dayord
);
4518 if(d
.month
> 0 && d
.month
<= 12)
4519 strncpy(str
, int2string(d
.month
), str_len
);
4525 strncpy(str
, monabb
, str_len
);
4530 strncpy(str
, (d
.month
> 0 && d
.month
< 13)
4531 ? month_name_locale(d
.month
) : "", str_len
);
4536 snprintf(str
, str_len
, "%s%s%s", monabb
, (monabb
[0] && day
[0]) ? " " : "", day
);
4538 if(monabb_width
> 0)
4539 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4540 monabb_width
, monabb_width
, monabb
, day
);
4542 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4548 snprintf(str
, str_len
, "%s%s%s%s%s", monabb
,
4549 (monabb
[0] && day
[0]) ? " " : "", day
,
4550 ((monabb
[0] || day
[0]) && year4
[0]) ? ", " : "",
4553 if(monabb_width
> 0)
4554 utf8_snprintf(str
, str_len
, "%-*.*w %2s%c %4s",
4555 monabb_width
, monabb_width
,
4557 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ', year4
);
4559 snprintf(str
, str_len
, "%s %2s%c %4s", monabb
, day
,
4560 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ',
4573 if(monzero
[0] == '?' && dayzero
[0] == '?' &&
4575 snprintf(str
, str_len
, "%8s", "");
4579 snprintf(str
, str_len
, "%2s/%2s/%2s",
4580 monzero
, dayzero
, yearzero
);
4583 snprintf(str
, str_len
, "%2s/%2s/%2s",
4584 dayzero
, monzero
, yearzero
);
4587 snprintf(str
, str_len
, "%2s.%2s.%2s",
4588 dayzero
, monzero
, yearzero
);
4591 snprintf(str
, str_len
, "%2s.%2s.%2s",
4592 yearzero
, monzero
, dayzero
);
4596 snprintf(str
, str_len
, "%2s-%2s-%2s",
4597 yearzero
, monzero
, dayzero
);
4601 snprintf(str
, str_len
, "%4s-%2s-%2s",
4602 year4
, monzero
, dayzero
);
4612 snprintf(str
, str_len
, "%2s%c%2s",
4613 (hour24
[0] && minzero
[0]) ? hour24
: "",
4614 (hour24
[0] && minzero
[0]) ? ':' : ' ',
4615 (hour24
[0] && minzero
[0]) ? minzero
: "");
4619 snprintf(str
, str_len
, "%s%c%2s%s",
4620 (hour12
[0] && minzero
[0]) ? hour12
: "",
4621 (hour12
[0] && minzero
[0]) ? ':' : ' ',
4622 (hour12
[0] && minzero
[0]) ? minzero
: "",
4623 (hour12
[0] && minzero
[0] && d
.hour
< 12) ? "am" :
4624 (hour12
[0] && minzero
[0] && d
.hour
>= 12) ? "pm" :
4627 case iSDate
: case iSDateIso
: case iSDateIsoS
:
4628 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
4629 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
4630 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
4631 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
4632 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
4633 { struct date now
, last_day
;
4635 int msg_day_of_year
, now_day_of_year
, today
;
4636 int diff
, ydiff
, last_day_of_year
;
4639 parse_date(dbuf
, &now
);
4640 today
= day_of_week(&now
) + 7;
4642 if(today
>= 0+7 && today
<= 6+7){
4643 now_day_of_year
= day_of_year(&now
);
4644 msg_day_of_year
= day_of_year(&d
);
4645 ydiff
= now
.year
- d
.year
;
4647 if(msg_day_of_year
== -1)
4650 diff
= now_day_of_year
- msg_day_of_year
;
4651 else if(ydiff
== 1){
4653 last_day
.month
= 12;
4655 last_day_of_year
= day_of_year(&last_day
);
4657 diff
= now_day_of_year
+
4658 (last_day_of_year
- msg_day_of_year
);
4660 else if(ydiff
== -1){
4662 last_day
.month
= 12;
4664 last_day_of_year
= day_of_year(&last_day
);
4666 diff
= -1 * (msg_day_of_year
+
4667 (last_day_of_year
- now_day_of_year
));
4675 strncpy(str
, _(TODAYSTR
), str_len
);
4677 strncpy(str
, _("Yesterday"), str_len
);
4678 else if(diff
> 1 && diff
< 7)
4679 snprintf(str
, str_len
, "%s", day_name_locale((today
- diff
) % 7));
4681 strncpy(str
, _("Tomorrow"), str_len
);
4682 else if(diff
< -1 && diff
> -7)
4683 snprintf(str
, str_len
, _("Next %.3s!"),
4684 day_name_locale((today
- diff
) % 7));
4687 || (ydiff
== 1 && 12 + now
.month
- d
.month
< 6))){
4689 snprintf(str
, str_len
, "%s%s%s", monabb
,
4690 (monabb
[0] && day
[0]) ? " " : "", day
);
4692 if(monabb_width
> 0)
4693 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4694 monabb_width
, monabb_width
, monabb
, day
);
4696 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4700 if(msg_day_of_year
== -1 && (type
== iSDate
|| type
== iSDateTime
))
4701 type
= iSDateTimeIsoS
;
4704 case iSDate
: case iSDateTime
: case iSDateTime24
:
4708 memset(&tm
, 0, sizeof(tm
));
4709 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4710 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4711 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4712 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4713 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4714 our_strftime(str
, str_len
, "%x", &tm
);
4718 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4720 snprintf(str
, str_len
, "%s/%s/%s%s", mon
, day
, yearzero
,
4721 diff
< 0 ? "!" : "");
4723 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4724 (mon
[0] && mon
[1]) ? "" : " ",
4725 mon
, dayzero
, yearzero
,
4726 diff
< 0 ? "!" : "");
4728 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4730 snprintf(str
, str_len
, "%s/%s/%s%s", day
, mon
, yearzero
,
4731 diff
< 0 ? "!" : "");
4733 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4734 (day
[0] && day
[1]) ? "" : " ",
4735 day
, monzero
, yearzero
,
4736 diff
< 0 ? "!" : "");
4738 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4740 snprintf(str
, str_len
, "%s.%s.%s%s", day
, mon
, yearzero
,
4741 diff
< 0 ? "!" : "");
4743 snprintf(str
, str_len
, "%s%s.%s.%s%s",
4744 (day
[0] && day
[1]) ? "" : " ",
4745 day
, monzero
, yearzero
,
4746 diff
< 0 ? "!" : "");
4748 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4750 snprintf(str
, str_len
, "%s.%s.%s%s",
4751 yearzero
, monzero
, dayzero
,
4752 diff
< 0 ? "!" : "");
4754 snprintf(str
, str_len
, "%s.%s.%s%s",
4755 yearzero
, monzero
, dayzero
,
4756 diff
< 0 ? "!" : "");
4758 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4759 snprintf(str
, str_len
, "%2s-%2s-%2s",
4760 yearzero
, monzero
, dayzero
);
4762 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4763 snprintf(str
, str_len
, "%4s-%2s-%2s",
4764 year4
, monzero
, dayzero
);
4774 snprintf(str
, str_len
, "%s%s%s", monabb
,
4775 (monabb
[0] && day
[0]) ? " " : "", day
);
4777 if(monabb_width
> 0)
4778 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4779 monabb_width
, monabb_width
, monabb
, day
);
4781 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4792 str
[str_len
-1] = '\0';
4794 if(type
== iSTime
||
4795 (sdatetimetype
&& !strcmp(str
, _(TODAYSTR
)))){
4796 struct date now
, last_day
;
4797 char dbuf
[200], *Ddd
, *ampm
;
4802 parse_date(dbuf
, &now
);
4804 /* Figure out if message date lands in the past week */
4806 /* (if message dated this month or last month...) */
4807 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 1) ||
4808 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 1)){
4810 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4813 * If msg in end of last year (and we're in first bit of "this"
4814 * year), diff will be backwards; fix up by adding number of days
4815 * in last year (usually 365, but occasionally 366)...
4817 if(d
.year
== now
.year
- 1){
4819 last_day
.month
= 12;
4822 daydiff
+= day_of_year(&last_day
);
4826 daydiff
= -100; /* comfortably out of range (of past week) */
4828 /* Build 2-digit hour and am/pm indicator, used below */
4830 if(d
.hour
>= 0 && d
.hour
< 24){
4831 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4832 ampm
= (d
.hour
< 12) ? "am" : "pm";
4833 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4836 strncpy(hour12
, "??", sizeof(hour12
));
4837 hour12
[sizeof(hour12
)-1] = '\0';
4839 strncpy(hour24
, "??", sizeof(hour24
));
4840 hour24
[sizeof(hour24
)-1] = '\0';
4843 /* Build date/time in str, in format similar to that used by w(1) */
4845 if(daydiff
== 0){ /* If date is today, "HH:MMap" */
4846 if(d
.minute
>= 0 && d
.minute
< 60)
4847 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4849 strncpy(minzero
, "??", sizeof(minzero
));
4850 minzero
[sizeof(minzero
)-1] = '\0';
4853 snprintf(str
, str_len
, "%s:%s%s", sdatetime24type
? hour24
: hour12
,
4854 minzero
, sdatetime24type
? "" : ampm
);
4856 else if(daydiff
>= 1 && daydiff
< 6){ /* If <1wk ago, "DddHHap" */
4858 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4859 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4860 Ddd
= day_abbrev_locale(day_of_week(&d
));
4864 snprintf(str
, str_len
, "%s%s%s", Ddd
, hour12
, ampm
);
4866 else{ /* date is old or future, "ddMmmyy" */
4867 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4868 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4869 monabb
[sizeof(monabb
)-1] = '\0';
4871 if(d
.day
>= 1 && d
.day
<= 31)
4872 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
4874 strncpy(dayzero
, "??", sizeof(dayzero
));
4875 dayzero
[sizeof(dayzero
)-1] = '\0';
4878 if(d
.year
>= 0 && d
.year
<= 9999)
4879 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
4881 strncpy(yearzero
, "??", sizeof(yearzero
));
4882 yearzero
[sizeof(yearzero
)-1] = '\0';
4885 snprintf(str
, str_len
, "%s%s%s", dayzero
, monabb
, yearzero
);
4888 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4890 memmove(str
, str
+ 1, strlen(str
));
4899 * Format a string representing the keywords into ice.
4901 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
4903 * Args idata -- which message?
4904 * kwtype -- keywords or kw initials
4905 * ice -- index cache entry for message
4908 key_str(INDEXDATA_S
*idata
, SubjKW kwtype
, ICE_S
*ice
)
4913 COLOR_PAIR
*color
= NULL
;
4914 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
4915 IELEM_S
*ielem
= NULL
;
4916 IFIELD_S
*ourifield
= NULL
;
4918 if(ice
&& ice
->ifield
){
4919 /* move to last ifield, the one we're working */
4920 for(ourifield
= ice
->ifield
;
4921 ourifield
&& ourifield
->next
;
4922 ourifield
= ourifield
->next
)
4929 if(kwtype
== KWInit
){
4930 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4931 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4932 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
4933 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
4936 * Pick off the first initial. Since word is UTF-8 it may
4937 * take more than one byte for the first initial.
4940 if(word
&& word
[0]){
4942 unsigned long remaining_octets
;
4943 unsigned char *inputp
;
4945 remaining_octets
= strlen(word
);
4946 inputp
= (unsigned char *) word
;
4947 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
4948 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
4949 ielem
= new_ielem(&ourifield
->ielem
);
4950 ielem
->freedata
= 1;
4951 ielem
->datalen
= (unsigned) (inputp
- (unsigned char *) word
);
4952 ielem
->data
= (char *) fs_get((ielem
->datalen
+ 1) * sizeof(char));
4953 strncpy(ielem
->data
, word
, ielem
->datalen
);
4954 ielem
->data
[ielem
->datalen
] = '\0';
4956 if(pico_usingcolor()
4957 && ((kw
->nick
&& kw
->nick
[0]
4958 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
4959 || (kw
->kw
&& kw
->kw
[0]
4960 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
4961 ielem
->color
= color
;
4968 free_color_pair(&color
);
4972 else if(kwtype
== KW
){
4973 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
4974 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
4977 ielem
= new_ielem(&ourifield
->ielem
);
4978 ielem
->freedata
= 1;
4979 ielem
->data
= cpystr(" ");
4985 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
4986 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
4989 ielem
= new_ielem(&ourifield
->ielem
);
4990 ielem
->freedata
= 1;
4991 ielem
->data
= cpystr(word
);
4992 ielem
->datalen
= strlen(word
);
4994 if(pico_usingcolor()
4995 && ((kw
->nick
&& kw
->nick
[0]
4996 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
4997 || (kw
->kw
&& kw
->kw
[0]
4998 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
4999 ielem
->color
= color
;
5005 free_color_pair(&color
);
5011 * If we're coloring some of the fields then add a dummy field
5012 * at the end that can soak up the rest of the space after the last
5013 * colored keyword. Otherwise, the last one's color will extend to
5014 * the end of the field.
5016 if(pico_usingcolor()){
5017 ielem
= new_ielem(&ourifield
->ielem
);
5018 ielem
->freedata
= 1;
5019 ielem
->data
= cpystr(" ");
5023 ourifield
->leftadj
= 1;
5024 set_ielem_widths_in_field(ourifield
);
5029 prio_str(INDEXDATA_S
*idata
, IndexColType ctype
, ICE_S
*ice
)
5031 IFIELD_S
*ourifield
= NULL
;
5032 IELEM_S
*ielem
= NULL
;
5037 if(ice
&& ice
->ifield
){
5038 /* move to last ifield, the one we're working */
5039 for(ourifield
= ice
->ifield
;
5040 ourifield
&& ourifield
->next
;
5041 ourifield
= ourifield
->next
)
5048 hdrval
= fetch_header(idata
, PRIORITYNAME
);
5050 if(hdrval
&& hdrval
[0] && isdigit(hdrval
[0])){
5052 if(v
>= 1 && v
<= 5 && v
!= 3){
5054 ielem
= new_ielem(&ourifield
->ielem
);
5055 ielem
->freedata
= 1;
5059 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5060 ielem
->data
[0] = hdrval
[0];
5061 ielem
->data
[1] = '\0';
5065 for(p
= priorities
; p
&& p
->desc
; p
++)
5070 ielem
->data
= cpystr(p
->desc
);
5075 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5076 ielem
->data
[0] = '\0';
5079 ielem
->data
[0] = '!';
5084 * We could put a Unicode downarrow in here but
5085 * we have no way of knowing if the user's font
5086 * will have it (I think).
5088 ielem
->data
[0] = 'v';
5092 ielem
->data
[1] = '\0';
5097 alpine_panic("Unhandled case in prio_str");
5102 ielem
->data
= cpystr("");
5104 if(ielem
&& ielem
->data
)
5105 ielem
->datalen
= strlen(ielem
->data
);
5107 if((v
== 1 || v
== 2) && pico_usingcolor()
5108 && ps_global
->VAR_IND_HIPRI_FORE_COLOR
5109 && ps_global
->VAR_IND_HIPRI_BACK_COLOR
){
5110 ielem
->type
= eTypeCol
;
5111 ielem
->freecolor
= 1;
5112 ielem
->color
= new_color_pair(ps_global
->VAR_IND_HIPRI_FORE_COLOR
, ps_global
->VAR_IND_HIPRI_BACK_COLOR
);
5114 else if((v
== 4 || v
== 5) && pico_usingcolor()
5115 && ps_global
->VAR_IND_LOPRI_FORE_COLOR
5116 && ps_global
->VAR_IND_LOPRI_BACK_COLOR
){
5117 ielem
->type
= eTypeCol
;
5118 ielem
->freecolor
= 1;
5119 ielem
->color
= new_color_pair(ps_global
->VAR_IND_LOPRI_FORE_COLOR
, ps_global
->VAR_IND_LOPRI_BACK_COLOR
);
5122 ourifield
->leftadj
= 1;
5123 set_ielem_widths_in_field(ourifield
);
5126 fs_give((void **) &hdrval
);
5132 header_str(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
, ICE_S
*ice
)
5134 IFIELD_S
*ourifield
= NULL
;
5135 IELEM_S
*ielem
= NULL
;
5136 char *fieldval
= NULL
;
5138 if(ice
&& ice
->ifield
){
5139 /* move to last ifield, the one we're working */
5140 for(ourifield
= ice
->ifield
;
5141 ourifield
&& ourifield
->next
;
5142 ourifield
= ourifield
->next
)
5149 fieldval
= get_fieldval(idata
, hdrtok
);
5152 ielem
= new_ielem(&ourifield
->ielem
);
5153 ielem
->freedata
= 1;
5154 ielem
->data
= fieldval
;
5155 ielem
->datalen
= strlen(fieldval
);
5157 ourifield
->leftadj
= (hdrtok
->adjustment
== Left
) ? 1 : 0;
5160 set_ielem_widths_in_field(ourifield
);
5165 get_fieldval(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
)
5168 char *hdrval
= NULL
, *testval
;
5169 char *fieldval
= NULL
, *firstval
;
5170 char *retfieldval
= NULL
;
5173 return(retfieldval
);
5175 if(hdrtok
&& hdrtok
->hdrname
&& hdrtok
->hdrname
[0])
5176 hdrval
= fetch_header(idata
, hdrtok
? hdrtok
->hdrname
: "");
5178 /* find start of fieldnum'th field */
5180 for(fieldnum
= MAX(hdrtok
->fieldnum
-1, 0);
5181 fieldnum
> 0 && fieldval
&& *fieldval
; fieldnum
--){
5184 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5185 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5186 if(testval
&& (!firstval
|| testval
< firstval
))
5190 fieldval
= firstval
;
5191 if(fieldval
&& *fieldval
)
5195 /* tie off end of field */
5196 if(fieldval
&& *fieldval
&& hdrtok
->fieldnum
> 0){
5198 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5199 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5200 if(testval
&& (!firstval
|| testval
< firstval
))
5211 retfieldval
= cpystr(fieldval
);
5214 fs_give((void **) &hdrval
);
5216 return(retfieldval
);
5221 scorevalfrommsg(MAILSTREAM
*stream
, MsgNo rawno
, HEADER_TOK_S
*hdrtok
, int no_fetch
)
5225 char *fieldval
= NULL
;
5228 memset(&idata
, 0, sizeof(INDEXDATA_S
));
5229 idata
.stream
= stream
;
5230 idata
.no_fetch
= no_fetch
;
5231 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), rawno
);
5232 idata
.rawno
= rawno
;
5233 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
5234 && (mc
= mail_elt(stream
, idata
.rawno
))){
5235 idata
.size
= mc
->rfc822_size
;
5236 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
5241 fieldval
= get_fieldval(&idata
, hdrtok
);
5244 retval
= atol(fieldval
);
5245 fs_give((void **) &fieldval
);
5253 * Put a string representing the subject into str. Idata tells us which
5254 * message we are referring to.
5256 * This means we should ensure that all data ends up being UTF-8 data.
5257 * That covers the data in ice ielems and str.
5259 * Args idata -- which message?
5260 * str -- destination buffer
5261 * strsize -- size of str buffer
5262 * kwtype -- prepend keywords or kw initials before the subject
5263 * opening -- add first text from body of message if there's room
5264 * ice -- index cache entry for message
5267 subj_str(INDEXDATA_S
*idata
, char *str
, size_t strsize
, SubjKW kwtype
, int opening
, ICE_S
*ice
)
5269 char *subject
, *origsubj
, *origstr
, *rawsubj
, *sptr
= NULL
;
5270 char *p
, *border
, *q
= NULL
, *free_subj
= NULL
;
5274 int depth
= 0, mult
= 2;
5276 int do_subj
= 0, truncated_tree
= 0;
5277 PINETHRD_S
*thd
, *thdorig
;
5278 IELEM_S
*ielem
= NULL
, *subjielem
= NULL
;
5279 IFIELD_S
*ourifield
= NULL
;
5285 * If we need the data at the start of the message and we're in
5286 * a c-client callback, defer the data lookup until later.
5288 if(opening
&& idata
->no_fetch
){
5293 if(ice
&& ice
->ifield
){
5294 /* move to last ifield, the one we're working on */
5295 for(ourifield
= ice
->ifield
;
5296 ourifield
&& ourifield
->next
;
5297 ourifield
= ourifield
->next
)
5301 str
[0] = str
[strsize
-1] = '\0';
5303 rawsubj
= fetch_subject(idata
);
5308 * Before we do anything else, decode the character set in the subject and
5309 * work with the result.
5311 sp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5312 SIZEOF_20KBUF
, rawsubj
);
5315 len
+= 100; /* for possible charset, escaped characters */
5316 origsubj
= fs_get((len
+1) * sizeof(unsigned char));
5319 iutf8ncpy(origsubj
, sp
, len
);
5321 origsubj
[len
] = '\0';
5322 replace_tabs_by_space(origsubj
);
5323 removing_trailing_white_space(origsubj
);
5326 * origsubj is the original subject but it has been decoded. We need
5327 * to free it at the end of this routine.
5332 * prepend_keyword will put the keyword stuff before the subject
5333 * and split the subject up into its colored parts in subjielem.
5334 * Subjielem is a local ielem which will have to be fit into the
5335 * real ifield->ielem later. The print_format strings in subjielem will
5336 * not be filled in by prepend_keyword because of the fact that we
5337 * may have to adjust things for threading below.
5338 * We use subjielem in case we want to insert some threading information
5339 * at the front of the subject.
5341 if(kwtype
== KW
|| kwtype
== KWInit
){
5342 subject
= prepend_keyword_subject(idata
->stream
, idata
->rawno
,
5344 ourifield
? &subjielem
: NULL
,
5345 ps_global
->VAR_KW_BRACES
);
5346 free_subj
= subject
;
5351 subjielem
= new_ielem(&subjielem
);
5352 subjielem
->type
= eTypeCol
;
5353 subjielem
->freedata
= 1;
5354 subjielem
->data
= cpystr(subject
);
5355 subjielem
->datalen
= strlen(subject
);
5356 if(pico_usingcolor()
5357 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5358 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5359 subjielem
->freecolor
= 1;
5360 subjielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5366 * This space is here so that if the subject does
5367 * not extend all the way to the end of the field then
5368 * we'll switch the color back and paint the rest of the
5369 * field in the Normal color or the index line color.
5372 ielem
= new_ielem(&subjielem
);
5373 ielem
->freedata
= 1;
5374 ielem
->data
= cpystr(" ");
5382 && (ps_global
->thread_disp_style
== THREAD_STRUCT
5383 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
5384 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
5385 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ2
)){
5388 * Why do we want to truncate the subject and from strs?
5389 * It's so we can put the [5] thread count things in below when
5390 * we are threading and the thread structure runs off the right
5391 * hand edge of the screen. This routine doesn't know that it
5392 * is running off the edge unless it knows the actual width
5393 * that we have to draw in.
5395 if(pith_opt_truncate_sfstr
5396 && (*pith_opt_truncate_sfstr
)()
5398 && ourifield
->width
> 0)
5399 width
= ourifield
->width
;
5404 width
= MIN(width
, strsize
-1);
5407 * We're counting on the fact that this initial part of the
5408 * string is ascii and we have one octet per character and
5409 * characters are width 1 on the screen.
5411 border
= str
+ width
;
5413 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
5415 if(pith_opt_condense_thread_cue
)
5416 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
5418 && get_lflag(idata
->stream
,
5423 * width is < available strsize and
5424 * border points to something less than or equal
5425 * to the end of the buffer.
5431 while(thd
->parent
&&
5432 (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
5436 if(ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
)
5439 sptr
+= (mult
*depth
);
5440 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
5441 thd
&& thd
->parent
&& p
>= str
;
5442 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
5443 if(p
+ mult
>= border
&& !q
){
5444 if(width
>= 4 && depth
< 100){
5445 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
5448 else if(width
>= 5 && depth
< 1000){
5449 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
5453 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
5466 if(ps_global
->thread_disp_style
== THREAD_STRUCT
5467 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5470 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5473 if(thd
== thdorig
&& !thd
->branch
)
5474 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
5475 else if(thd
== thdorig
|| thd
->branch
)
5478 if(p
+ 1 < border
&& thd
== thdorig
)
5485 if(sptr
&& !truncated_tree
){
5487 * Look to see if the subject is the same as the previous
5488 * message in the thread, if any. If it is the same, don't
5489 * reprint the subject.
5491 * Note that when we're prepending keywords to the subject,
5492 * and the user changes a keyword, we do invalidate
5493 * the index cache for that message but we don't go to the
5494 * trouble of invalidating the index cache for the the child
5495 * of that node in the thread, so the MUTT subject line
5496 * display for the child may be wrong. That is, it may show
5497 * it is the same as this subject even though it no longer
5498 * is, or vice versa.
5500 if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5504 if(thdorig
->parent
&&
5505 (thd
= fetch_thread(idata
->stream
, thdorig
->parent
))
5507 char *this_orig
= NULL
,
5509 *free_prev_orig
= NULL
,
5510 *this_prep
= NULL
, /* includes prepend */
5514 SORTCACHE
*sc
= NULL
;
5516 /* get the stripped subject of previous message */
5517 mc
= (mailcache_t
) mail_parameters(NIL
, GET_CACHE
, NIL
);
5519 sc
= (*mc
)(idata
->stream
, thd
->rawno
, CH_SORTCACHE
);
5521 if(sc
&& sc
->subject
)
5522 prev_orig
= sc
->subject
;
5526 env
= pine_mail_fetchenvelope(idata
->stream
,
5528 stripthis
= (env
&& env
->subject
)
5529 ? env
->subject
: "";
5531 mail_strip_subject(stripthis
, &prev_orig
);
5533 free_prev_orig
= prev_orig
;
5536 mail_strip_subject(rawsubj
, &this_orig
);
5538 if(kwtype
== KW
|| kwtype
== KWInit
){
5539 prev_prep
= prepend_keyword_subject(idata
->stream
,
5543 ps_global
->VAR_KW_BRACES
);
5545 this_prep
= prepend_keyword_subject(idata
->stream
,
5549 ps_global
->VAR_KW_BRACES
);
5551 if((this_prep
|| prev_prep
)
5552 && ((this_prep
&& !prev_prep
)
5553 || (prev_prep
&& !this_prep
)
5554 || strucmp(this_prep
, prev_prep
)))
5558 if((this_orig
|| prev_orig
)
5559 && ((this_orig
&& !prev_orig
)
5560 || (prev_orig
&& !this_orig
)
5561 || strucmp(this_orig
, prev_orig
)))
5566 * If some of the thread is zoomed out of view, we
5567 * want to display the subject of the first one that
5568 * is in view. If any of the parents or grandparents
5569 * etc of this message are visible, then we don't
5570 * need to worry about it. If all of the parents have
5571 * been zoomed away, then this is the first one.
5573 * When you're looking at a particular case where
5574 * some of the messages of a thread are selected it
5575 * seems like we should look at not only our
5576 * direct parents, but the siblings of the parent
5577 * too. But that's not really correct, because those
5578 * siblings are basically the starts of different
5579 * branches, separate from our branch. They could
5580 * have their own subjects, for example. This will
5581 * give us cases where it looks like we are showing
5582 * the subject too much, but it will be correct!
5584 * In zoom_index() we clear_index_cache_ent for
5585 * some lines which have subjects which might become
5586 * visible when we zoom, and also in set_lflags
5587 * where we might change subjects by unselecting
5588 * something when zoomed.
5592 if(!msgline_hidden(idata
->stream
,
5593 sp_msgmap(idata
->stream
),
5594 mn_raw2m(sp_msgmap(idata
->stream
),
5597 break; /* found a visible parent */
5600 if(thd
&& thd
->parent
)
5601 thd
= fetch_thread(idata
->stream
,thd
->parent
);
5606 if(!thd
) /* none were visible */
5611 fs_give((void **) &this_orig
);
5614 fs_give((void **) &this_prep
);
5617 fs_give((void **) &free_prev_orig
);
5620 fs_give((void **) &prev_prep
);
5631 * We don't need to worry about truncating to width
5632 * here. If we go over the right hand edge it will be
5635 strsize
-= (sptr
- str
);
5637 strncpy(sptr
, subject
, strsize
-1);
5638 sptr
[strsize
-1] = '\0';
5640 else if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5641 strsize
-= (sptr
- str
);
5649 * We decided we don't need the subject so we'd better
5650 * eliminate subjielem.
5652 free_ielem(&subjielem
);
5656 free_ielem(&subjielem
); /* no room for actual subject */
5658 if(ourifield
&& sptr
&& sptr
> origstr
){
5659 ielem
= new_ielem(&ourifield
->ielem
);
5660 ielem
->type
= eThreadInfo
;
5661 ielem
->freedata
= 1;
5664 ielem
->data
= cpystr(origstr
);
5665 ielem
->datalen
= strlen(origstr
);
5671 * Not much to do for the non-threading case. Just copy the
5672 * subject we have so far into str and truncate it.
5674 strncpy(str
, subject
, strsize
-1);
5675 str
[strsize
-1] = '\0';
5680 * We need to add subjielem to the end of the ourifield->ielem list.
5683 if(ourifield
->ielem
){
5684 for(ielem
= ourifield
->ielem
;
5685 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5688 ielem
->next
= subjielem
;
5691 ourifield
->ielem
= subjielem
;
5694 ourifield
->leftadj
= 1;
5697 if(opening
&& ourifield
){
5698 IELEM_S
*ftielem
= NULL
;
5702 first_text
= fetch_firsttext(idata
, 0);
5708 strncpy(sep
, ps_global
->VAR_OPENING_SEP
? ps_global
->VAR_OPENING_SEP
: " - ",
5710 sep
[sizeof(sep
)-1] = '\0';
5711 removing_double_quotes(sep
);
5712 seplen
= strlen(sep
);
5714 ftielem
= new_ielem(&ftielem
);
5715 ftielem
->type
= eTypeCol
;
5716 ftielem
->freedata
= 1;
5717 len
= strlen(first_text
) + seplen
;
5718 ftielem
->data
= (char *) fs_get((len
+ 1) * sizeof(char));
5720 strncpy(ftielem
->data
, sep
, seplen
);
5721 strncpy(ftielem
->data
+seplen
, first_text
, len
+1-seplen
);
5722 ftielem
->data
[len
] = '\0';
5724 ftielem
->datalen
= strlen(ftielem
->data
);
5726 fs_give((void **) &first_text
);
5729 if(pico_usingcolor()
5730 && ps_global
->VAR_IND_OP_FORE_COLOR
5731 && ps_global
->VAR_IND_OP_BACK_COLOR
){
5732 ftielem
->freecolor
= 1;
5733 ftielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
, ps_global
->VAR_IND_OP_BACK_COLOR
);
5736 * This space is here so that if the opening text does
5737 * not extend all the way to the end of the field then
5738 * we'll switch the color back and paint the rest of the
5739 * field in the Normal color or the index line color.
5741 ielem
= new_ielem(&ftielem
);
5742 ielem
->freedata
= 1;
5743 ielem
->data
= cpystr(" ");
5747 if(ourifield
->ielem
){
5748 for(ielem
= ourifield
->ielem
;
5749 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5752 ielem
->next
= ftielem
;
5755 ourifield
->ielem
= ftielem
;
5758 ourifield
->leftadj
= 1;
5763 set_ielem_widths_in_field(ourifield
);
5766 fs_give((void **) &origsubj
);
5769 fs_give((void **) &free_subj
);
5774 * Returns an allocated string which is the passed in subject with a
5775 * list of keywords prepended.
5777 * If kwtype == KW you will end up with
5779 * {keyword1 keyword2} subject
5781 * (actually, keyword nicknames will be used instead of the actual keywords
5782 * in the case that the user defined nicknames)
5784 * If kwtype == KWInit you get
5788 * where A is the first letter of the first keyword and B is the first letter
5789 * of the second defined keyword. No space between them. There could be more
5792 * If an ielemp is passed in it will be filled out with the data and colors
5793 * of the pieces of the subject but the print_format strings will not
5797 prepend_keyword_subject(MAILSTREAM
*stream
, long int rawno
, char *subject
,
5798 SubjKW kwtype
, IELEM_S
**ielemp
, char *braces
)
5800 char *p
, *next_piece
, *retsubj
= NULL
, *str
;
5801 char *left_brace
= NULL
, *right_brace
= NULL
;
5803 int some_set
= 0, save
;
5806 COLOR_PAIR
*color
= NULL
;
5807 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5812 if(braces
&& *braces
)
5813 get_pair(braces
, &left_brace
, &right_brace
, 1, 0);
5815 len
= (left_brace
? strlen(left_brace
) : 0) +
5816 (right_brace
? strlen(right_brace
) : 0);
5818 if(stream
&& rawno
>= 0L && rawno
<= stream
->nmsgs
){
5819 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
5820 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5823 len
++; /* space between keywords */
5825 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5828 else if(kwtype
== KWInit
){
5829 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5830 /* interested in only the first UTF-8 initial */
5833 unsigned long remaining_octets
;
5834 unsigned char *inputp
;
5836 remaining_octets
= strlen(str
);
5837 inputp
= (unsigned char *) str
;
5838 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5839 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5840 len
+= (unsigned) (inputp
- (unsigned char *) str
);
5849 if((kwtype
== KW
|| kwtype
== KWInit
) && some_set
){
5850 len
+= strlen(subject
); /* subject is already UTF-8 if needed */
5851 retsubj
= (char *) fs_get((len
+ 1) * sizeof(*retsubj
));
5852 memset(retsubj
, 0, (len
+ 1) * sizeof(*retsubj
));
5853 next_piece
= p
= retsubj
;
5855 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5856 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5858 if(left_brace
&& len
> 0)
5859 sstrncpy(&p
, left_brace
, len
);
5861 else if(kwtype
== KW
)
5864 if(ielemp
&& p
> next_piece
){
5867 ielem
= new_ielem(ielemp
);
5868 ielem
->freedata
= 1;
5869 ielem
->data
= cpystr(next_piece
);
5870 ielem
->datalen
= strlen(next_piece
);
5875 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5877 if(kwtype
== KWInit
){
5880 unsigned long remaining_octets
;
5881 unsigned char *inputp
;
5883 remaining_octets
= strlen(str
);
5884 inputp
= (unsigned char *) str
;
5885 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5886 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5887 if(len
-(p
-retsubj
) > 0){
5888 sstrncpy(&p
, str
, MIN(inputp
- (unsigned char *) str
,len
-(p
-retsubj
)));
5889 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5890 && ((kw
->nick
&& kw
->nick
[0]
5891 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5892 || (kw
->kw
&& kw
->kw
[0]
5893 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5894 ielem
= new_ielem(ielemp
);
5895 ielem
->freedata
= 1;
5898 ielem
->data
= cpystr(next_piece
);
5899 ielem
->datalen
= strlen(next_piece
);
5900 ielem
->color
= color
;
5909 free_color_pair(&color
);
5913 if(len
-(p
-retsubj
) > 0)
5914 sstrncpy(&p
, str
, len
-(p
-retsubj
));
5916 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
5917 && ((kw
->nick
&& kw
->nick
[0]
5918 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5919 || (kw
->kw
&& kw
->kw
[0]
5920 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5921 ielem
= new_ielem(ielemp
);
5922 ielem
->freedata
= 1;
5925 ielem
->data
= cpystr(next_piece
);
5926 ielem
->datalen
= strlen(next_piece
);
5927 ielem
->color
= color
;
5934 free_color_pair(&color
);
5939 if(len
-(p
-retsubj
) > 0 && right_brace
)
5940 sstrncpy(&p
, right_brace
, len
-(p
-retsubj
));
5942 if(ielemp
&& p
> next_piece
){
5945 ielem
= new_ielem(ielemp
);
5946 ielem
->freedata
= 1;
5947 ielem
->data
= cpystr(next_piece
);
5948 ielem
->datalen
= strlen(next_piece
);
5953 if(len
-(p
-retsubj
) > 0 && subject
)
5954 sstrncpy(&p
, subject
, len
-(p
-retsubj
));
5956 if(ielemp
&& p
> next_piece
){
5959 ielem
= new_ielem(ielemp
);
5960 ielem
->type
= eTypeCol
;
5961 ielem
->freedata
= 1;
5962 ielem
->data
= cpystr(next_piece
);
5963 ielem
->datalen
= strlen(next_piece
);
5966 if(pico_usingcolor()
5967 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5968 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5969 ielem
->freecolor
= 1;
5970 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5974 retsubj
[len
] = '\0'; /* just making sure */
5978 ielem
= new_ielem(ielemp
);
5979 ielem
->type
= eTypeCol
;
5980 ielem
->freedata
= 1;
5981 ielem
->data
= cpystr(subject
);
5982 ielem
->datalen
= strlen(subject
);
5983 if(pico_usingcolor()
5984 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5985 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5986 ielem
->freecolor
= 1;
5987 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5991 retsubj
= cpystr(subject
);
5996 fs_give((void **) &left_brace
);
5999 fs_give((void **) &right_brace
);
6007 * This means we should ensure that all data ends up being UTF-8 data.
6008 * That covers the data in ice ielems and str.
6011 from_str(IndexColType ctype
, INDEXDATA_S
*idata
, char *str
, size_t strsize
, ICE_S
*ice
)
6013 char *field
, *newsgroups
, *border
, *p
, *fptr
= NULL
, *q
= NULL
;
6016 int depth
= 0, mult
= 2;
6017 PINETHRD_S
*thd
, *thdorig
;
6020 && (ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
6021 || ps_global
->thread_disp_style
== THREAD_INDENT_FROM2
6022 || ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
)){
6024 if(pith_opt_truncate_sfstr
&& (*pith_opt_truncate_sfstr
)()){
6025 IFIELD_S
*ourifield
= NULL
;
6027 if(ice
&& ice
->ifield
){
6028 /* move to last ifield, the one we're working on */
6029 for(ourifield
= ice
->ifield
;
6030 ourifield
&& ourifield
->next
;
6031 ourifield
= ourifield
->next
)
6035 if(ourifield
&& ourifield
->width
> 0)
6036 width
= ourifield
->width
;
6042 width
= MIN(width
, strsize
-1);
6044 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
6045 border
= str
+ width
;
6046 if(pith_opt_condense_thread_cue
)
6047 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
6049 && get_lflag(idata
->stream
,
6056 while(thd
->parent
&& (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
6060 if(ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
)
6063 fptr
+= (mult
*depth
);
6064 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
6065 thd
&& thd
->parent
&& p
>= str
;
6066 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
6067 if(p
+ mult
>= border
&& !q
){
6068 if(width
>= 4 && depth
< 100){
6069 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
6072 else if(width
>= 5 && depth
< 1000){
6073 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
6077 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
6087 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6090 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6093 if(thd
== thdorig
&& !thd
->branch
)
6094 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6095 else if(thd
== thdorig
|| thd
->branch
)
6102 else if(p
< border
){
6104 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6107 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6110 if(thd
== thdorig
&& !thd
->branch
)
6111 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6112 else if(thd
== thdorig
|| thd
->branch
)
6123 strsize
-= (fptr
- str
);
6126 case iFromToNotNews
:
6127 if(!(addr
= fetch_from(idata
)) || address_is_us(addr
, ps_global
)){
6129 strncpy(fptr
, "To: ", strsize
-1);
6130 fptr
[strsize
-1] = '\0';
6134 if((field
= ((addr
= fetch_to(idata
))
6136 : (addr
= fetch_cc(idata
))
6139 && set_index_addr(idata
, field
, addr
, "To: ",
6143 if(ctype
== iFromTo
&&
6144 (newsgroups
= fetch_newsgroups(idata
)) &&
6146 snprintf(fptr
, strsize
, "To: %-*.*s", (int)(strsize
-1-4), (int)(strsize
-1-4),
6151 /* else fall thru to From: */
6154 /* else fall thru to From: */
6160 set_index_addr(idata
, "From", fetch_from(idata
), NULL
, strsize
-1, fptr
);
6165 if((addr
= fetch_from(idata
)) && addr
->mailbox
&& addr
->mailbox
[0]){
6166 char *mb
= NULL
, *hst
= NULL
, *at
= NULL
;
6170 if(ctype
== iAddress
&& addr
->host
&& addr
->host
[0]
6171 && addr
->host
[0] != '.'){
6177 if(!at
|| strsize
-1 <= len
)
6178 snprintf(fptr
, strsize
, "%-*.*s", (int)(strsize
-1), (int)(strsize
-1), mb
);
6180 snprintf(fptr
, strsize
, "%s@%-*.*s", mb
, (int)(strsize
-1-len
-1), (int)(strsize
-1-len
-1), hst
);
6189 replace_tabs_by_space(str
);
6194 * Set up the elements contained in field so that they take up the
6195 * whole field width. Data is assumed to be UTF-8.
6198 set_ielem_widths_in_field(IFIELD_S
*ifield
)
6200 IELEM_S
*ielem
= NULL
;
6201 int datawidth
, fmtwidth
;
6206 fmtwidth
= ifield
->width
;
6208 for(ielem
= ifield
->ielem
; ielem
&& fmtwidth
> 0; ielem
= ielem
->next
){
6209 if(!ifield
->leftadj
&& ielem
->next
){
6210 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield
->ctype
));
6214 datawidth
= (int) utf8_width(ielem
->data
);
6215 if(datawidth
>= fmtwidth
|| !ielem
->next
){
6216 set_print_format(ielem
, fmtwidth
, ifield
->leftadj
);
6220 set_print_format(ielem
, datawidth
, ifield
->leftadj
);
6221 fmtwidth
-= datawidth
;
6228 * Simple hash function from K&R 2nd edition, p. 144.
6230 * This one is modified to never return 0 so we can use that as a special
6231 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6232 * as a special value that can't be returned by line_hash.
6237 unsigned long hashval
;
6239 for(hashval
= 0; *s
!= '\0'; s
++)
6240 hashval
= *s
+ 31 * hashval
;
6242 hashval
= hashval
% LINE_HASH_N
;
6252 * Returns nonzero if considered hidden, 0 if not considered hidden.
6255 msgline_hidden(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
6259 if(flags
& MH_ANYTHD
){
6260 ret
= ((any_lflagged(msgmap
, MN_HIDE
) > 0)
6261 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
));
6263 else if(flags
& MH_THISTHD
&& THREADING() && sp_viewing_a_thread(stream
)){
6264 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6265 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
));
6268 if(THREADING() && sp_viewing_a_thread(stream
)){
6269 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6270 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
)
6271 || get_lflag(stream
, msgmap
, msgno
, MN_CHID
));
6273 else if(THRD_INDX()){
6275 * If this message is in the collapsed part of a thread,
6276 * it's hidden. It must be a top-level of a thread to be
6277 * considered visible. Even if it is top-level, it is only
6278 * visible if some message in the thread is not hidden.
6280 if(get_lflag(stream
, msgmap
, msgno
, MN_CHID
)) /* not top */
6283 unsigned long rawno
;
6284 PINETHRD_S
*thrd
= NULL
;
6286 rawno
= mn_m2raw(msgmap
, msgno
);
6288 thrd
= fetch_thread(stream
, rawno
);
6290 ret
= !thread_has_some_visible(stream
, thrd
);
6294 ret
= ((any_lflagged(msgmap
, MN_HIDE
| MN_CHID
) > 0)
6295 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
| MN_CHID
));
6300 "msgline_hidden(%ld): %s\n", msgno
, ret
? "HID" : "VIS"));
6307 adjust_cur_to_visible(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
6312 cur
= mn_get_cur(msgmap
);
6314 /* if current is hidden, adjust */
6315 if(cur
>= 1L && cur
<= mn_get_total(msgmap
)
6316 && msgline_hidden(stream
, msgmap
, cur
, 0)){
6318 dir
= mn_get_revsort(msgmap
) ? -1 : 1;
6321 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6322 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6323 && msgline_hidden(stream
, msgmap
, n
, 0);
6327 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6328 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6329 mn_reset_cur(msgmap
, n
);
6330 else{ /* no visible in that direction */
6332 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6333 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6334 && msgline_hidden(stream
, msgmap
, n
, 0);
6338 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6339 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6340 mn_reset_cur(msgmap
, n
);
6348 setup_for_index_index_screen(void)
6350 format_index_line
= format_index_index_line
;
6351 setup_header_widths
= setup_index_header_widths
;
6356 setup_for_thread_index_screen(void)
6358 format_index_line
= format_thread_index_line
;
6359 setup_header_widths
= setup_thread_header_widths
;
6364 ice_hash(ICE_S
*ice
)
6366 char buf
[MAX_SCREEN_COLS
+1];
6371 simple_index_line(buf
, sizeof(buf
), ice
, 0L);
6373 buf
[sizeof(buf
) - 1] = '\0';
6375 return(line_hash(buf
));
6380 left_adjust(int width
)
6382 return(format_str(width
, 1));
6387 right_adjust(int width
)
6389 return(format_str(width
, 0));
6394 * Returns allocated and filled in format string.
6397 format_str(int width
, int left
)
6402 len
= PRINT_FORMAT_LEN(width
,left
) * sizeof(char);
6403 format
= (char *) fs_get(len
+ 1);
6404 copy_format_str(width
, left
, format
, len
);
6412 * Put the left or right adjusted format string of width width into
6413 * dest. Dest is of size n+1.
6416 copy_format_str(int width
, int left
, char *dest
, int n
)
6420 p
= int2string(width
);
6422 snprintf(dest
, n
+1, "%%%s%s.%ss", left
? "-" : "", p
, p
);
6431 * Sets up the print_format string to be width wide with left or right
6432 * adjust. Takes care of memory freeing and allocation.
6435 set_print_format(IELEM_S
*ielem
, int width
, int leftadj
)
6440 if(ielem
->print_format
){
6441 /* is there enough room? */
6442 if(ielem
->freeprintf
< PRINT_FORMAT_LEN(width
,leftadj
)+1){
6443 fs_resize((void **) &ielem
->print_format
,
6444 (PRINT_FORMAT_LEN(width
,leftadj
)+1) * sizeof(char));
6445 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);
6448 copy_format_str(width
, leftadj
, ielem
->print_format
,
6449 PRINT_FORMAT_LEN(width
,leftadj
));
6452 ielem
->print_format
= leftadj
? left_adjust(width
)
6453 : right_adjust(width
);
6454 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);