1 /* ========================================================================
2 * Copyright 2013-2022 Eduardo Chappa
3 * Copyright 2006-2008 University of Washington
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
14 #include "../pith/headers.h"
15 #include "../pith/mailindx.h"
16 #include "../pith/pineelt.h"
17 #include "../pith/mailview.h"
18 #include "../pith/flag.h"
19 #include "../pith/icache.h"
20 #include "../pith/msgno.h"
21 #include "../pith/thread.h"
22 #include "../pith/strlst.h"
23 #include "../pith/status.h"
24 #include "../pith/mailcmd.h"
25 #include "../pith/search.h"
26 #include "../pith/charset.h"
27 #include "../pith/reply.h"
28 #include "../pith/bldaddr.h"
29 #include "../pith/addrstring.h"
30 #include "../pith/news.h"
31 #include "../pith/util.h"
32 #include "../pith/pattern.h"
33 #include "../pith/sequence.h"
34 #include "../pith/color.h"
35 #include "../pith/stream.h"
36 #include "../pith/string.h"
37 #include "../pith/send.h"
38 #include "../pith/options.h"
39 #include "../pith/ablookup.h"
41 #include "../pico/osdep/mswin.h"
45 * pointers to formatting functions
47 ICE_S
*(*format_index_line
)(INDEXDATA_S
*);
48 void (*setup_header_widths
)(MAILSTREAM
*);
51 * pointer to optional load_overview functionality
53 void (*pith_opt_paint_index_hline
)(MAILSTREAM
*, long, ICE_S
*);
56 * pointer to hook for saving index format state
58 void (*pith_opt_save_index_state
)(int);
61 * hook to allow caller to insert cue that indicates a condensed
62 * thread relationship cue
64 int (*pith_opt_condense_thread_cue
)(PINETHRD_S
*, ICE_S
*, char **, size_t *, int, int);
65 int (*pith_opt_truncate_sfstr
)(void);
71 void setup_for_thread_index_screen(void);
72 ICE_S
*format_index_index_line(INDEXDATA_S
*);
73 ICE_S
*format_thread_index_line(INDEXDATA_S
*);
74 int set_index_addr(INDEXDATA_S
*, char *, ADDRESS
*, char *, int, char *);
75 int ctype_is_fixed_length(IndexColType
);
76 void setup_index_header_widths(MAILSTREAM
*);
77 void setup_thread_header_widths(MAILSTREAM
*);
78 int parse_index_format(char *, INDEX_COL_S
**);
79 int index_in_overview(MAILSTREAM
*);
80 ADDRESS
*fetch_from(INDEXDATA_S
*);
81 ADDRESS
*fetch_sender(INDEXDATA_S
*);
82 char *fetch_newsgroups(INDEXDATA_S
*);
83 char *fetch_subject(INDEXDATA_S
*);
84 char *fetch_date(INDEXDATA_S
*);
85 long fetch_size(INDEXDATA_S
*);
86 BODY
*fetch_body(INDEXDATA_S
*);
87 char *fetch_firsttext(INDEXDATA_S
*idata
, int);
88 char *fetch_header(INDEXDATA_S
*idata
, char *hdrname
);
89 void subj_str(INDEXDATA_S
*, char *, size_t, SubjKW
, int, int, ICE_S
*);
90 void key_str(INDEXDATA_S
*, SubjKW
, ICE_S
*);
91 void header_str(INDEXDATA_S
*, HEADER_TOK_S
*, ICE_S
*);
92 void prio_str(INDEXDATA_S
*, IndexColType
, ICE_S
*);
93 void from_str(IndexColType
, INDEXDATA_S
*, char *, size_t, ICE_S
*);
94 int day_of_week(struct date
*);
95 int day_of_year(struct date
*);
96 unsigned long ice_hash(ICE_S
*);
97 char *left_adjust(int);
98 char *right_adjust(int);
99 char *format_str(int, int);
100 char *copy_format_str(int, int, char *, int);
101 void set_print_format(IELEM_S
*, int, int);
102 void set_ielem_widths_in_field(IFIELD_S
*);
105 #define BIGWIDTH 2047
108 /*----------------------------------------------------------------------
109 Initialize the index_disp_format array in ps_global from this
112 Args: format -- the string containing the format tokens
113 answer -- put the answer here, free first if there was a previous
117 init_index_format(char *format
, INDEX_COL_S
**answer
)
120 int i
, w
, monabb_width
= 0, column
= 0;
123 * Record the fact that SCORE appears in some index format. This
124 * is a heavy-handed approach. It will stick at 1 if any format ever
125 * contains score during this session. This is ok since it will just
126 * cause recalculation if wrong and these things rarely change much.
128 if(!ps_global
->a_format_contains_score
&& format
129 && strstr(format
, "SCORE")){
130 ps_global
->a_format_contains_score
= 1;
131 /* recalculate need for scores */
132 scores_are_used(SCOREUSE_INVALID
);
135 set_need_format_setup(ps_global
->mail_stream
);
136 /* if custom format is specified, try it, else go with default */
137 if(!(format
&& *format
&& parse_index_format(format
, answer
))){
138 static INDEX_COL_S answer_default
[] = {
140 {iMessNo
, WeCalculate
},
141 {iSDateTime24
, WeCalculate
},
142 {iFromTo
, Percent
, 33}, /* percent of rest */
143 {iSizeNarrow
, WeCalculate
},
144 {iSubjKey
, Percent
, 67},
149 free_index_format(answer
);
151 *answer
= (INDEX_COL_S
*)fs_get(sizeof(answer_default
));
152 memcpy(*answer
, answer_default
, sizeof(answer_default
));
156 * Test to see how long the month abbreviations are.
158 for(i
= 1; i
<= 12; i
++){
159 p
= month_abbrev_locale(i
);
160 monabb_width
= MAX(utf8_width(p
), monabb_width
);
163 monabb_width
= MIN(MAX(2, monabb_width
), 5);
166 * Fill in req_width's for WeCalculate items.
168 for(column
= 0; (*answer
)[column
].ctype
!= iNothing
; column
++){
170 /* don't use strftime if we're not trying to use the LC_TIME stuff */
171 if(F_ON(F_DISABLE_INDEX_LOCALE_DATES
, ps_global
)){
172 switch((*answer
)[column
].ctype
){
174 (*answer
)[column
].ctype
= iS1Date
;
177 (*answer
)[column
].ctype
= iSDateTimeS1
;
180 (*answer
)[column
].ctype
= iSDateTimeS124
;
187 if((*answer
)[column
].wtype
== WeCalculate
){
188 switch((*answer
)[column
].ctype
){
192 (*answer
)[column
].req_width
= 1;
201 (*answer
)[column
].req_width
= 2;
206 (*answer
)[column
].req_width
= 3;
211 (*answer
)[column
].req_width
= 4;
217 (*answer
)[column
].req_width
= 5;
222 (*answer
)[column
].req_width
= 6;
229 (*answer
)[column
].req_width
= 7;
237 (*answer
)[column
].req_width
= 8;
240 (*answer
)[column
].req_width
= 9;
243 (*answer
)[column
].req_width
= monabb_width
;
244 (*answer
)[column
].monabb_width
= monabb_width
;
251 * Test to see how long it is.
253 for(i
= 0; i
< 7; i
++){
254 p
= day_abbrev_locale(i
);
255 w
= MAX(utf8_width(p
), w
);
258 (*answer
)[column
].req_width
= MIN(MAX(2, w
), 5);
262 (*answer
)[column
].req_width
= monabb_width
+ 3;
263 (*answer
)[column
].monabb_width
= monabb_width
;
270 * Test to see how long it is.
272 for(i
= 1; i
<= 12; i
++){
273 p
= month_name_locale(i
);
274 w
= MAX(utf8_width(p
), w
);
277 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
284 for(i
= 0; i
< 7; i
++){
285 p
= day_name_locale(i
);
286 w
= MAX(utf8_width(p
), w
);
289 (*answer
)[column
].req_width
= MIN(MAX(3, w
), 12);
300 * Format a date to see how long it is.
301 * Make it as least as long as "Yesterday".
302 * We should really use the width of the longest
303 * of the translated yesterdays and friends but...
309 memset(&tm
, 0, sizeof(tm
));
316 switch((*answer
)[column
].ctype
){
318 our_strftime(ss
, sizeof(ss
), "%X", &tm
);
321 our_strftime(ss
, sizeof(ss
), "%c", &tm
);
325 our_strftime(ss
, sizeof(ss
), "%x", &tm
);
328 (*answer
)[column
].req_width
= MIN(MAX(9, utf8_width(ss
)), len
);
331 (*answer
)[column
].monabb_width
= monabb_width
;
336 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
338 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
339 case iSDateTimeIsoS24
:
340 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
342 * These SDates are 8 wide but they need to be 9 for "Yesterday".
344 (*answer
)[column
].req_width
= 9;
347 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
348 (*answer
)[column
].req_width
= 10;
351 (*answer
)[column
].req_width
= 12;
352 (*answer
)[column
].monabb_width
= monabb_width
;
355 (*answer
)[column
].req_width
= 16;
365 (void) mail_parameters(NULL
, SET_IMAPEXTRAHEADERS
,
366 (void *) get_extra_hdrs());
371 reset_index_format(void)
373 long rflags
= ROLE_DO_OTHER
;
378 if(ps_global
->mail_stream
&& nonempty_patterns(rflags
, &pstate
)){
379 for(pat
= first_pattern(&pstate
); pat
; pat
= next_pattern(&pstate
)){
380 if(match_pattern(pat
->patgrp
, ps_global
->mail_stream
, NULL
,
381 NULL
, NULL
, SE_NOSERVER
|SE_NOPREFETCH
))
385 if(pat
&& pat
->action
&& !pat
->action
->bogus
386 && pat
->action
->index_format
){
388 init_index_format(pat
->action
->index_format
,
389 &ps_global
->index_disp_format
);
394 init_index_format(ps_global
->VAR_INDEX_FORMAT
,
395 &ps_global
->index_disp_format
);
400 free_index_format(INDEX_COL_S
**disp_format
)
402 INDEX_COL_S
*cdesc
= NULL
;
404 if(disp_format
&& *disp_format
){
405 for(cdesc
= (*disp_format
); cdesc
->ctype
!= iNothing
; cdesc
++)
407 free_hdrtok(&cdesc
->hdrtok
);
409 fs_give((void **) disp_format
);
415 new_hdrtok(char *hdrname
)
417 HEADER_TOK_S
*hdrtok
;
419 hdrtok
= (HEADER_TOK_S
*) fs_get(sizeof(HEADER_TOK_S
));
420 memset(hdrtok
, 0, sizeof(HEADER_TOK_S
));
421 hdrtok
->hdrname
= hdrname
? cpystr(hdrname
) : NULL
;
422 hdrtok
->fieldnum
= 0;
423 hdrtok
->adjustment
= Left
;
424 hdrtok
->fieldsepcnt
= 1;
425 hdrtok
->fieldseps
= cpystr(" ");
432 free_hdrtok(HEADER_TOK_S
**hdrtok
)
434 if(hdrtok
&& *hdrtok
){
435 if((*hdrtok
)->hdrname
)
436 fs_give((void **) &(*hdrtok
)->hdrname
);
438 if((*hdrtok
)->fieldseps
)
439 fs_give((void **) &(*hdrtok
)->fieldseps
);
441 fs_give((void **) hdrtok
);
446 /* popular ones first to make it slightly faster */
447 static INDEX_PARSE_T itokens
[] = {
448 {"STATUS", iStatus
, FOR_INDEX
},
449 {"MSGNO", iMessNo
, FOR_INDEX
},
450 {"DATE", iDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
451 {"FROMORTO", iFromTo
, FOR_INDEX
},
452 {"FROMORTONOTNEWS", iFromToNotNews
, FOR_INDEX
},
453 {"SIZE", iSize
, FOR_INDEX
},
454 {"SIZECOMMA", iSizeComma
, FOR_INDEX
},
455 {"SIZENARROW", iSizeNarrow
, FOR_INDEX
},
456 {"KSIZE", iKSize
, FOR_INDEX
},
457 {"SUBJECT", iSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
458 {"SHORTSUBJECT", iShortSubject
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
459 {"FULLSTATUS", iFStatus
, FOR_INDEX
},
460 {"IMAPSTATUS", iIStatus
, FOR_INDEX
},
461 {"SHORTIMAPSTATUS", iSIStatus
, FOR_INDEX
},
462 {"SUBJKEY", iSubjKey
, FOR_INDEX
},
463 {"SHORTSUBJKEY", iShortSubjKey
, FOR_INDEX
},
464 {"SUBJKEYINIT", iSubjKeyInit
, FOR_INDEX
},
465 {"SHORTSUBJKEYINIT",iShortSubjKeyInit
, FOR_INDEX
},
466 {"SUBJECTTEXT", iSubjectText
, FOR_INDEX
},
467 {"SUBJKEYTEXT", iSubjKeyText
, FOR_INDEX
},
468 {"SUBJKEYINITTEXT", iSubjKeyInitText
, FOR_INDEX
},
469 {"OPENINGTEXT", iOpeningText
, FOR_INDEX
},
470 {"OPENINGTEXTNQ", iOpeningTextNQ
, FOR_INDEX
},
471 {"KEY", iKey
, FOR_INDEX
},
472 {"KEYINIT", iKeyInit
, FOR_INDEX
},
473 {"DESCRIPSIZE", iDescripSize
, FOR_INDEX
},
474 {"ATT", iAtt
, FOR_INDEX
},
475 {"SCORE", iScore
, FOR_INDEX
},
476 {"PRIORITY", iPrio
, FOR_INDEX
},
477 {"PRIORITYALPHA", iPrioAlpha
, FOR_INDEX
},
478 {"PRIORITY!", iPrioBang
, FOR_INDEX
},
479 {"LONGDATE", iLDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
480 {"SHORTDATE1", iS1Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
481 {"SHORTDATE2", iS2Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
482 {"SHORTDATE3", iS3Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
483 {"SHORTDATE4", iS4Date
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
484 {"DATEISO", iDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
485 {"SHORTDATEISO", iDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
486 {"SMARTDATE", iSDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
487 {"SMARTTIME", iSTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
488 {"SMARTTIME24", iSTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
489 {"SMARTDATEISO", iSDateIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
490 {"SMARTDATESHORTISO",iSDateIsoS
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
491 {"SMARTDATES1", iSDateS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
492 {"SMARTDATES2", iSDateS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
493 {"SMARTDATES3", iSDateS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
494 {"SMARTDATES4", iSDateS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
495 {"SMARTDATETIME", iSDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
496 {"SMARTDATETIMEISO",iSDateTimeIso
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
497 {"SMARTDATETIMESHORTISO",iSDateTimeIsoS
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
498 {"SMARTDATETIMES1", iSDateTimeS1
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
499 {"SMARTDATETIMES2", iSDateTimeS2
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
500 {"SMARTDATETIMES3", iSDateTimeS3
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
501 {"SMARTDATETIMES4", iSDateTimeS4
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
502 {"SMARTDATETIME24", iSDateTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
503 {"SMARTDATETIMEISO24", iSDateTimeIso24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
504 {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24
,FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
505 {"SMARTDATETIMES124", iSDateTimeS124
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
506 {"SMARTDATETIMES224", iSDateTimeS224
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
507 {"SMARTDATETIMES324", iSDateTimeS324
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
508 {"SMARTDATETIMES424", iSDateTimeS424
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
509 {"TIME24", iTime24
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
510 {"TIME12", iTime12
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
511 {"TIMEZONE", iTimezone
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
512 {"MONTHABBREV", iMonAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
513 {"DAYOFWEEKABBREV", iDayOfWeekAbb
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
514 {"DAYOFWEEK", iDayOfWeek
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
515 {"FROM", iFrom
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
516 {"TO", iTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
517 {"SENDER", iSender
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
518 {"CC", iCc
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
519 {"RECIPS", iRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
520 {"NEWS", iNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
521 {"TOANDNEWS", iToAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
522 {"NEWSANDTO", iNewsAndTo
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
523 {"RECIPSANDNEWS", iRecipsAndNews
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
524 {"NEWSANDRECIPS", iNewsAndRecips
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
525 {"MSGID", iMsgID
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
526 {"CURNEWS", iCurNews
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
527 {"DAYDATE", iRDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
528 {"PREFDATE", iPrefDate
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
529 {"PREFTIME", iPrefTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
530 {"PREFDATETIME", iPrefDateTime
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
531 {"DAY", iDay
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
532 {"DAYORDINAL", iDayOrdinal
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
533 {"DAY2DIGIT", iDay2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
534 {"MONTHLONG", iMonLong
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
535 {"MONTH", iMon
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
536 {"MONTH2DIGIT", iMon2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
537 {"YEAR", iYear
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
538 {"YEAR2DIGIT", iYear2Digit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
539 {"ADDRESS", iAddress
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
540 {"MAILBOX", iMailbox
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
541 {"ROLENICK", iRoleNick
, FOR_REPLY_INTRO
|FOR_TEMPLATE
},
542 {"INIT", iInit
, FOR_INDEX
|FOR_REPLY_INTRO
|FOR_TEMPLATE
},
543 {"CURDATE", iCurDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
544 {"CURDATEISO", iCurDateIso
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
545 {"CURDATEISOS", iCurDateIsoS
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
546 {"CURTIME24", iCurTime24
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
547 {"CURTIME12", iCurTime12
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
548 {"CURDAY", iCurDay
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
549 {"CURDAY2DIGIT", iCurDay2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
550 {"CURDAYOFWEEK", iCurDayOfWeek
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
551 {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb
,
552 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
553 {"CURMONTH", iCurMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
554 {"CURMONTH2DIGIT", iCurMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
555 {"CURMONTHLONG", iCurMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
556 {"CURMONTHABBREV", iCurMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
557 {"CURYEAR", iCurYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
558 {"CURYEAR2DIGIT", iCurYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
559 {"CURPREFDATE", iCurPrefDate
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
560 {"CURPREFTIME", iCurPrefTime
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
561 {"CURPREFDATETIME", iCurPrefDateTime
,
562 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
563 {"LASTMONTH", iLstMon
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
564 {"LASTMONTH2DIGIT", iLstMon2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
565 {"LASTMONTHLONG", iLstMonLong
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
566 {"LASTMONTHABBREV", iLstMonAbb
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
567 {"LASTMONTHYEAR", iLstMonYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
568 {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit
,
569 FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
570 {"LASTYEAR", iLstYear
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
571 {"LASTYEAR2DIGIT", iLstYear2Digit
, FOR_REPLY_INTRO
|FOR_TEMPLATE
|FOR_FILT
},
572 {"HEADER", iHeader
, FOR_INDEX
},
573 {"TEXT", iText
, FOR_INDEX
},
574 {"ARROW", iArrow
, FOR_INDEX
},
575 {"NEWLINE", iNewLine
, FOR_REPLY_INTRO
},
576 {"CURSORPOS", iCursorPos
, FOR_TEMPLATE
},
577 {NULL
, iNothing
, FOR_NOTHING
}
580 INDEX_PARSE_T itokensinv
[sizeof(itokens
)/sizeof(itokens
[0])];
583 inverse_itokens(void)
586 for (pt
= itokens
; pt
->name
; pt
++)
587 itokensinv
[pt
->ctype
].ctype
= pt
- itokens
;
594 return((i
< sizeof(itokens
) && itokens
[i
].name
) ? &itokens
[i
] : NULL
);
599 * Args txt -- The token being checked begins at the beginning
600 * of txt. The end of the token is delimited by a null, or
601 * white space, or an underscore if DELIM_USCORE is set,
602 * or a left paren if DELIM_PAREN is set.
603 * flags -- Flags contains the what_for value, and DELIM_ values.
605 * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
608 itoktype(char *txt
, int flags
)
615 * Separate a copy of the possible token out of txt.
619 while(w
< token
+sizeof(token
)-1 &&
621 !(!(*v
& 0x80) && isspace((unsigned char)*v
)) &&
622 !(flags
& DELIM_USCORE
&& *v
== '_') &&
623 !(flags
& DELIM_PAREN
&& *v
== '(') &&
624 !(flags
& DELIM_COLON
&& *v
== ':'))
629 for(pt
= itokens
; pt
->name
; pt
++)
630 if(pt
->what_for
& flags
&& !strucmp(pt
->name
, token
))
638 parse_index_format(char *format_str
, INDEX_COL_S
**answer
)
643 INDEX_COL_S cdesc
[200]; /* plenty of temp storage for answer */
645 memset((void *)cdesc
, 0, sizeof(cdesc
));
648 while(p
&& *p
&& column
< 200-1){
649 /* skip leading white space for next word */
650 p
= skip_white_space(p
);
651 pt
= itoktype(p
, FOR_INDEX
| DELIM_PAREN
| DELIM_COLON
);
653 /* ignore unrecognized word */
656 for(q
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
663 "parse_index_format: unrecognized token: %s\n",
665 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
666 _("Unrecognized word in index-format: %s"), q
);
672 cdesc
[column
].ctype
= pt
->ctype
;
674 if(pt
->ctype
== iHeader
|| pt
->ctype
== iText
){
676 * iHeader field has special syntax.
678 * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
680 * where width is the regular width or percentage width or
681 * left out for default width, fieldnum defaults to 0 for
682 * whole thing, 1 for first field, ...
683 * and field_separators is a list of characters which separate
684 * the fields. The whole parenthesized part is optional. If used
685 * the arguments can be dropped from the right, so
688 * HEADER:hdrname(10) or
689 * HEADER:hdrname(10%) or
690 * HEADER:hdrname(10,2) or
691 * HEADER:hdrname(,2) or
692 * HEADER:hdrname(10,2, ) or
693 * HEADER:hdrname(10,2,\,:) or
694 * HEADER:hdrname(10,2,\,:,R)
696 * iText field uses the hdrtok field for convenience. It has syntax
702 * and the literal text goes into the index line. It is also special
703 * because there is no 1 column space after this field.
707 p
+= strlen(pt
->name
);
709 /* look for header name */
711 char *w
, hdrname
[200];
716 if(*p
== '\"'){ /* quoted name */
718 while(w
< hdrname
+ sizeof(hdrname
)-1 && *p
!= '\"'){
730 while(w
< hdrname
+ sizeof(hdrname
)-1 &&
731 !(!(*p
& 0x80) && isspace((unsigned char)*p
)) &&
739 cdesc
[column
].hdrtok
= new_hdrtok(hdrname
);
742 if(pt
->ctype
== iHeader
){
743 dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
744 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
747 dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
748 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
753 if(pt
->ctype
== iHeader
){
754 dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p
));
755 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token HEADER should be followed by :hdrname");
758 dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p
));
759 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "index token TEXT should be followed by :text");
762 /* skip over rest of bogus config */
763 while(!(!(*p
& 0x80) && isspace((unsigned char)*p
)) && *p
!= '(')
768 /* skip over name and look for parens */
769 p
+= strlen(pt
->name
);
775 while(p
&& *p
&& isdigit((unsigned char) *p
))
778 if(pt
->ctype
== iHeader
){
779 /* first argument is width or width percentage, like for others */
780 if(p
&& *p
&& (*p
== ')' || *p
== ',')){
782 cdesc
[column
].wtype
= Fixed
;
783 cdesc
[column
].req_width
= atoi(q
);
786 cdesc
[column
].wtype
= WeCalculate
;
787 cdesc
[column
].req_width
= 0;
790 else if(p
&& *p
&& *p
== '%' && p
> q
){
791 cdesc
[column
].wtype
= Percent
;
792 cdesc
[column
].req_width
= atoi(q
);
796 cdesc
[column
].wtype
= WeCalculate
;
797 cdesc
[column
].req_width
= 0;
800 /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
801 if(p
&& *p
&& *p
== ','){
803 /* no space allowed between arguments */
804 if(*p
&& isdigit((unsigned char) *p
)){
806 while(*p
&& isdigit((unsigned char) *p
))
809 cdesc
[column
].hdrtok
->fieldnum
= atoi(q
);
812 * Optional 3rd argument is field separators.
813 * Comma is \, and backslash is \\.
819 /* don't use default */
820 if(*p
&& *p
!= ')' && *p
!= ',' && cdesc
[column
].hdrtok
->fieldseps
)
821 cdesc
[column
].hdrtok
->fieldseps
[0] = '\0';
824 if(*p
== '\"' && strchr(p
+1, '\"')){
826 while(*p
&& *p
!= ')' && *p
!= '\"' && *p
!= ','){
827 if(cdesc
[column
].hdrtok
->fieldseps
)
828 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
830 if(*p
== '\\' && *(p
+1))
833 if(cdesc
[column
].hdrtok
->fieldseps
){
834 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
835 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
836 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
844 while(*p
&& *p
!= ')' && *p
!= ','){
845 if(cdesc
[column
].hdrtok
->fieldseps
)
846 fs_resize((void **) &cdesc
[column
].hdrtok
->fieldseps
, j
+2);
847 if(*p
== '\\' && *(p
+1))
850 if(cdesc
[column
].hdrtok
->fieldseps
){
851 cdesc
[column
].hdrtok
->fieldseps
[j
++] = *p
++;
852 cdesc
[column
].hdrtok
->fieldseps
[j
] = '\0';
853 cdesc
[column
].hdrtok
->fieldsepcnt
= j
;
858 /* optional 4th argument, left or right adjust */
861 if(*p
== 'L' || *p
== 'l')
862 cdesc
[column
].hdrtok
->adjustment
= Left
;
863 else if(*p
== 'R' || *p
== 'r')
864 cdesc
[column
].hdrtok
->adjustment
= Right
;
866 dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p
? p
: "<null>"));
867 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 4th argument should be L or R");
873 dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p
? p
: "<null>"));
874 q_status_message(SM_ORDER
| SM_DING
, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
879 if(p
&& *p
&& *p
== ')' && p
> q
){
880 cdesc
[column
].wtype
= Fixed
;
881 cdesc
[column
].req_width
= atoi(q
);
883 else if(p
&& *p
&& *p
== '%' && p
> q
){
884 cdesc
[column
].wtype
= Percent
;
885 cdesc
[column
].req_width
= atoi(q
);
888 cdesc
[column
].wtype
= WeCalculate
;
889 cdesc
[column
].req_width
= 0;
894 /* if they left out width for iText we can figure it out */
895 if(pt
->ctype
== iText
&& cdesc
[column
].hdrtok
&& cdesc
[column
].hdrtok
->hdrname
){
896 cdesc
[column
].wtype
= Fixed
;
897 cdesc
[column
].req_width
= utf8_width(cdesc
[column
].hdrtok
->hdrname
);
900 cdesc
[column
].wtype
= WeCalculate
;
901 cdesc
[column
].req_width
= 0;
906 /* skip text at end of word */
907 while(p
&& *p
&& !isspace((unsigned char)*p
))
911 /* if, after all that, we didn't find anything recognizable, bitch */
913 dprint((1, "Completely unrecognizable index-format\n"));
914 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
915 _("Configured \"index-format\" unrecognizable. Using default."));
919 /* Finish with Nothing column */
920 cdesc
[column
].ctype
= iNothing
;
922 /* free up old answer */
924 free_index_format(answer
);
926 /* allocate space for new answer */
927 *answer
= (INDEX_COL_S
*)fs_get((column
+1)*sizeof(INDEX_COL_S
));
928 memset((void *)(*answer
), 0, (column
+1)*sizeof(INDEX_COL_S
));
929 /* copy answer to real place */
930 for(i
= 0; i
<= column
; i
++)
931 (*answer
)[i
] = cdesc
[i
];
938 * These types are basically fixed in width.
939 * The order is slightly significant. The ones towards the front of the
940 * list get space allocated sooner than the ones at the end of the list.
942 static IndexColType fixed_ctypes
[] = {
943 iMessNo
, iStatus
, iFStatus
, iIStatus
, iSIStatus
,
944 iDate
, iSDate
, iSDateTime
, iSDateTime24
,
945 iSTime
, iSTime24
, iLDate
,
946 iS1Date
, iS2Date
, iS3Date
, iS4Date
, iDateIso
, iDateIsoS
,
947 iSDateIso
, iSDateIsoS
,
948 iSDateS1
, iSDateS2
, iSDateS3
, iSDateS4
,
949 iSDateTimeIso
, iSDateTimeIsoS
,
950 iSDateTimeS1
, iSDateTimeS2
, iSDateTimeS3
, iSDateTimeS4
,
951 iSDateTimeIso24
, iSDateTimeIsoS24
,
952 iSDateTimeS124
, iSDateTimeS224
, iSDateTimeS324
, iSDateTimeS424
,
953 iSize
, iSizeComma
, iSizeNarrow
, iKSize
, iDescripSize
,
954 iPrio
, iPrioBang
, iPrioAlpha
, iInit
,
955 iAtt
, iTime24
, iTime12
, iTimezone
, iMonAbb
, iYear
, iYear2Digit
,
956 iDay2Digit
, iMon2Digit
, iDayOfWeekAbb
, iScore
, iMonLong
, iDayOfWeek
961 ctype_is_fixed_length(IndexColType ctype
)
966 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
969 if(ctype
== fixed_ctypes
[j
])
977 /*----------------------------------------------------------------------
978 Setup the widths of the various columns in the index display
981 setup_index_header_widths(MAILSTREAM
*stream
)
983 int colspace
; /* for reserving space between columns */
984 int j
, some_to_calculate
;
985 int space_left
, screen_width
, fix
;
986 int keep_going
, tot_pct
, was_sl
;
991 max_msgno
= mn_get_total(ps_global
->msgmap
);
993 dprint((8, "=== setup_index_header_widths() ===\n"));
995 clear_icache_flags(stream
);
996 screen_width
= ps_global
->ttyo
->screen_cols
;
997 space_left
= screen_width
;
998 some_to_calculate
= 0;
1002 * Calculate how many fields there are so we know how many spaces
1003 * between columns to reserve. Fill in Fixed widths now. Reserve
1004 * special case WeCalculate with non-zero req_widths before doing
1005 * Percent cases below.
1007 for(cdesc
= ps_global
->index_disp_format
;
1008 cdesc
->ctype
!= iNothing
;
1011 if(cdesc
->wtype
== Fixed
){
1012 cdesc
->width
= cdesc
->req_width
;
1013 if(cdesc
->width
> 0)
1016 else if(cdesc
->wtype
== Percent
){
1017 cdesc
->width
= 0; /* calculated later */
1020 else{ /* WeCalculate */
1021 cdesc
->width
= cdesc
->req_width
; /* reserve this for now */
1022 some_to_calculate
++;
1026 /* no space after iText */
1027 if(cdesc
->ctype
== iText
)
1030 space_left
-= cdesc
->width
;
1033 colspace
= MAX(colspace
, 0);
1035 space_left
-= colspace
; /* space between columns */
1037 ps_global
->display_keywords_in_subject
= 0;
1038 ps_global
->display_keywordinits_in_subject
= 0;
1041 * Set the actual lengths for the fixed width fields and set up
1042 * the left or right adjustment for everything.
1043 * There should be a case setting actual_length for all of the types
1046 for(cdesc
= ps_global
->index_disp_format
;
1047 cdesc
->ctype
!= iNothing
;
1050 wtype
= cdesc
->wtype
;
1052 if(cdesc
->ctype
== iSubjKey
|| cdesc
->ctype
== iSubjKeyText
)
1053 ps_global
->display_keywords_in_subject
= 1;
1054 else if(cdesc
->ctype
== iSubjKeyInit
|| cdesc
->ctype
== iSubjKeyInitText
)
1055 ps_global
->display_keywordinits_in_subject
= 1;
1057 if(wtype
== WeCalculate
|| wtype
== Percent
|| cdesc
->width
!= 0){
1059 switch(cdesc
->ctype
){
1060 case iSDate
: case iSDateIso
: case iSDateIsoS
:
1061 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1062 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
1063 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1064 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
1065 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1066 case iSTime
: case iSTime24
:
1067 set_format_includes_smartdate(stream
);
1074 if(ctype_is_fixed_length(cdesc
->ctype
)){
1075 switch(cdesc
->ctype
){
1079 cdesc
->actual_length
= 1;
1080 cdesc
->adjustment
= Left
;
1086 cdesc
->actual_length
= 2;
1087 cdesc
->adjustment
= Left
;
1091 cdesc
->actual_length
= 2;
1092 cdesc
->adjustment
= Right
;
1097 cdesc
->actual_length
= 3;
1098 cdesc
->adjustment
= Left
;
1102 set_format_includes_msgno(stream
);
1103 if(max_msgno
< 1000)
1104 cdesc
->actual_length
= 3;
1105 else if(max_msgno
< 10000)
1106 cdesc
->actual_length
= 4;
1107 else if(max_msgno
< 100000)
1108 cdesc
->actual_length
= 5;
1110 cdesc
->actual_length
= 6;
1112 cdesc
->adjustment
= Right
;
1117 cdesc
->actual_length
= 4;
1118 cdesc
->adjustment
= Left
;
1123 cdesc
->actual_length
= 5;
1124 cdesc
->adjustment
= Left
;
1128 cdesc
->actual_length
= 5;
1129 cdesc
->adjustment
= Right
;
1134 cdesc
->actual_length
= 6;
1135 cdesc
->adjustment
= Left
;
1139 cdesc
->actual_length
= 6;
1140 cdesc
->adjustment
= Right
;
1146 cdesc
->actual_length
= 7;
1147 cdesc
->adjustment
= Right
;
1151 cdesc
->actual_length
= 7;
1152 cdesc
->adjustment
= Left
;
1156 cdesc
->actual_length
= 7;
1157 cdesc
->adjustment
= Left
;
1166 cdesc
->actual_length
= 8;
1167 cdesc
->adjustment
= Left
;
1171 cdesc
->actual_length
= 8;
1172 cdesc
->adjustment
= Right
;
1183 cdesc
->actual_length
= cdesc
->req_width
;
1184 cdesc
->adjustment
= Left
;
1189 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1190 case iSDateTimeIsoS
:
1191 case iSDateTimeIsoS24
:
1192 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1193 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1194 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
1195 if(cdesc
->ctype
== iSDateIso
1196 || cdesc
->ctype
== iSDateTimeIso
1197 || cdesc
->ctype
== iSDateTimeIso24
)
1198 cdesc
->actual_length
= 10;
1200 cdesc
->actual_length
= 9;
1202 cdesc
->adjustment
= Left
;
1206 cdesc
->actual_length
= 9;
1207 cdesc
->adjustment
= Right
;
1211 cdesc
->actual_length
= 10;
1212 cdesc
->adjustment
= Left
;
1216 cdesc
->actual_length
= 12;
1217 cdesc
->adjustment
= Left
;
1221 alpine_panic("Unhandled fixed case in setup_index_header");
1225 else if(cdesc
->ctype
== iHeader
)
1226 cdesc
->adjustment
= cdesc
->hdrtok
? cdesc
->hdrtok
->adjustment
: Left
;
1228 cdesc
->adjustment
= Left
;
1232 if(ps_global
->display_keywords_in_subject
)
1233 ps_global
->display_keywordinits_in_subject
= 0;
1235 /* if have reserved unneeded space for size, give it back */
1236 for(cdesc
= ps_global
->index_disp_format
;
1237 cdesc
->ctype
!= iNothing
;
1239 if(cdesc
->ctype
== iSize
|| cdesc
->ctype
== iKSize
||
1240 cdesc
->ctype
== iSizeNarrow
||
1241 cdesc
->ctype
== iSizeComma
|| cdesc
->ctype
== iDescripSize
){
1242 if(cdesc
->actual_length
== 0){
1243 if((fix
=cdesc
->width
) > 0){ /* had this reserved */
1248 space_left
++; /* +1 for space between columns */
1253 * Calculate the field widths that are basically fixed in width.
1254 * Do them in this order in case we don't have enough space to go around.
1255 * The set of fixed_ctypes here is the same as the set where we
1256 * set the actual_lengths above.
1258 for(j
= 0; space_left
> 0 && some_to_calculate
; j
++){
1260 if(j
>= sizeof(fixed_ctypes
)/sizeof(*fixed_ctypes
))
1263 for(cdesc
= ps_global
->index_disp_format
;
1264 cdesc
->ctype
!= iNothing
&& space_left
> 0 && some_to_calculate
;
1266 if(cdesc
->ctype
== fixed_ctypes
[j
] && cdesc
->wtype
== WeCalculate
){
1267 some_to_calculate
--;
1268 fix
= MIN(cdesc
->actual_length
- cdesc
->width
, space_left
);
1269 cdesc
->width
+= fix
;
1275 * Fill in widths for Percent cases. If there are no more to calculate,
1276 * use the percentages as relative numbers and use the rest of the space,
1277 * else treat them as absolute percentages of the original avail screen.
1280 if(some_to_calculate
){
1281 int tot_requested
= 0;
1284 * Requests are treated as percent of screen width. See if they
1285 * will all fit. If not, trim them back proportionately.
1287 for(cdesc
= ps_global
->index_disp_format
;
1288 cdesc
->ctype
!= iNothing
;
1290 if(cdesc
->wtype
== Percent
){
1291 /* The 2, 200, and +100 are because we're rounding */
1292 fix
= ((2*cdesc
->req_width
*
1293 (screen_width
-colspace
))+100) / 200;
1294 tot_requested
+= fix
;
1298 if(tot_requested
> space_left
){
1299 int multiplier
= (100 * space_left
) / tot_requested
;
1301 for(cdesc
= ps_global
->index_disp_format
;
1302 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1304 if(cdesc
->wtype
== Percent
){
1305 /* The 2, 200, and +100 are because we're rounding */
1306 fix
= ((2*cdesc
->req_width
*
1307 (screen_width
-colspace
))+100) / 200;
1308 fix
= (2 * fix
* multiplier
+ 100) / 200;
1309 fix
= MIN(fix
, space_left
);
1310 cdesc
->width
+= fix
;
1316 for(cdesc
= ps_global
->index_disp_format
;
1317 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1319 if(cdesc
->wtype
== Percent
){
1320 /* The 2, 200, and +100 are because we're rounding */
1321 fix
= ((2*cdesc
->req_width
*
1322 (screen_width
-colspace
))+100) / 200;
1323 fix
= MIN(fix
, space_left
);
1324 cdesc
->width
+= fix
;
1332 was_sl
= space_left
;
1333 /* add up total percentages requested */
1334 for(cdesc
= ps_global
->index_disp_format
;
1335 cdesc
->ctype
!= iNothing
;
1337 if(cdesc
->wtype
== Percent
)
1338 tot_pct
+= cdesc
->req_width
;
1340 /* give relative weight to requests */
1341 for(cdesc
= ps_global
->index_disp_format
;
1342 cdesc
->ctype
!= iNothing
&& space_left
> 0 && tot_pct
> 0;
1344 if(cdesc
->wtype
== Percent
){
1345 fix
= ((2*cdesc
->req_width
*was_sl
)+tot_pct
) / (2*tot_pct
);
1346 fix
= MIN(fix
, space_left
);
1347 cdesc
->width
+= fix
;
1354 /* split up rest, give twice as much to Subject */
1356 while(space_left
> 0 && keep_going
){
1358 for(cdesc
= ps_global
->index_disp_format
;
1359 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1361 if(cdesc
->wtype
== WeCalculate
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1365 if(space_left
> 0 && (cdesc
->ctype
== iSubject
1366 || cdesc
->ctype
== iShortSubject
1367 || cdesc
->ctype
== iSubjectText
1368 || cdesc
->ctype
== iSubjKey
1369 || cdesc
->ctype
== iShortSubjKey
1370 || cdesc
->ctype
== iSubjKeyText
1371 || cdesc
->ctype
== iSubjKeyInit
1372 || cdesc
->ctype
== iShortSubjKeyInit
1373 || cdesc
->ctype
== iSubjKeyInitText
)){
1381 /* if still more, pad out percent's */
1383 while(space_left
> 0 && keep_going
){
1385 for(cdesc
= ps_global
->index_disp_format
;
1386 cdesc
->ctype
!= iNothing
&& space_left
> 0;
1388 if(cdesc
->wtype
== Percent
&& !ctype_is_fixed_length(cdesc
->ctype
)){
1396 /* if user made Fixed fields too big, give back space */
1398 while(space_left
< 0 && keep_going
){
1400 for(cdesc
= ps_global
->index_disp_format
;
1401 cdesc
->ctype
!= iNothing
&& space_left
< 0;
1403 if(cdesc
->wtype
== Fixed
&& cdesc
->width
> 0){
1411 if(pith_opt_save_index_state
)
1412 (*pith_opt_save_index_state
)(FALSE
);
1417 setup_thread_header_widths(MAILSTREAM
*stream
)
1419 clear_icache_flags(stream
);
1420 if(pith_opt_save_index_state
)
1421 (*pith_opt_save_index_state
)(TRUE
);
1426 * load_overview - c-client call back to gather overview data
1428 * Note: if we never get called, UID represents a hole
1429 * if we're passed a zero UID, totally bogus overview data
1430 * if we're passed a zero obuf, mostly bogus overview data
1433 load_overview(MAILSTREAM
*stream
, imapuid_t uid
, OVERVIEW
*obuf
, long unsigned int rawno
)
1435 if(obuf
&& rawno
>= 1L && stream
&& rawno
<= stream
->nmsgs
){
1439 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1443 * Only really load the thing if we've got an NNTP stream
1444 * otherwise we're just using mail_fetch_overview to load the
1445 * IMAP envelope cache with the specific set of messages
1448 idata
.stream
= stream
;
1449 idata
.rawno
= rawno
;
1450 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), idata
.rawno
);
1451 idata
.size
= obuf
->optional
.octets
;
1452 idata
.from
= obuf
->from
;
1453 idata
.date
= obuf
->date
;
1454 idata
.subject
= obuf
->subject
;
1456 ice
= (*format_index_line
)(&idata
);
1457 if(idata
.bogus
&& ice
){
1460 clear_ice(&ice
->tice
);
1465 else if(F_OFF(F_QUELL_NEWS_ENV_CB
, ps_global
)
1466 && (!THRD_INDX() || (ice
&& ice
->tice
))
1467 && !msgline_hidden(stream
, sp_msgmap(stream
), idata
.msgno
, 0)
1468 && pith_opt_paint_index_hline
){
1469 (*pith_opt_paint_index_hline
)(stream
, idata
.msgno
, ice
);
1476 build_header_work(struct pine
*state
, MAILSTREAM
*stream
, MSGNO_S
*msgmap
,
1477 long int msgno
, long int top_msgno
, int msgcount
, int *fetched
)
1481 long n
, i
, cnt
, rawno
, visible
, limit
= -1L;
1483 rawno
= mn_m2raw(msgmap
, msgno
);
1487 ice
= fetch_ice(stream
, rawno
);
1491 if(ice
->tice
&& ice
->tice
->ifield
1492 && ice
->tice
->color_lookup_done
&& ice
->tice
->widths_done
){
1494 char buf
[MAX_SCREEN_COLS
+1];
1495 simple_index_line(buf
, sizeof(buf
), ice
->tice
, msgno
);
1497 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1500 buf
[0] ? strlen(buf
) : 0));
1505 ice
= fetch_ice(stream
, rawno
);
1509 if(ice
->ifield
&& ice
->color_lookup_done
&& ice
->widths_done
){
1511 char buf
[MAX_SCREEN_COLS
+1];
1512 simple_index_line(buf
, sizeof(buf
), ice
, msgno
);
1514 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1517 buf
[0] ? strlen(buf
) : 0));
1523 * If we are in THRD_INDX() and the width changed we don't currently
1524 * have a method of fixing just the widths and print_format strings.
1525 * Instead, we clear the index cache entry and start over.
1527 if(THRD_INDX() && ice
&& ice
->tice
&& ice
->tice
->ifield
1528 && !ice
->tice
->widths_done
){
1529 clear_ice(&ice
->tice
);
1533 * Fetch everything we need to start filling in the index line
1534 * explicitly via mail_fetch_overview. On an nntp stream
1535 * this has the effect of building the index lines in the
1536 * load_overview callback. Under IMAP we're either getting
1537 * the envelope data via the imap_envelope callback or
1538 * preloading the cache. Either way, we're getting exactly
1539 * what we want rather than relying on linear lookahead sort
1542 if(!(fetched
&& *fetched
) && index_in_overview(stream
)
1543 && ((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1544 || (!THRD_INDX() && !ice
->ifield
))){
1553 /* clear sequence bits */
1554 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1555 if((mc
= mail_elt(stream
, n
)) != NULL
)
1559 * Light interesting bits
1560 * NOTE: not set above because m2raw's cheaper
1561 * than raw2m for every message
1565 * Unfortunately, it is expensive to calculate visible pages
1566 * in thread index if we are zoomed, so we don't try.
1568 if(THRD_INDX() && any_lflagged(msgmap
, MN_HIDE
))
1569 visible
= msgmap
->visible_threads
;
1570 else if(THREADING() && sp_viewing_a_thread(stream
)){
1572 * We know that all visible messages in the thread are marked
1575 for(visible
= 0L, n
= top_msgno
;
1576 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1579 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1582 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1588 visible
= mn_get_total(msgmap
)
1589 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1591 limit
= MIN(visible
, msgcount
);
1597 * First add the msgno we're asking for in case it
1600 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, msgno
));
1601 if(msgno
<= mn_get_total(msgmap
)
1602 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1603 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1606 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, top_msgno
));
1609 * Loop through visible threads, marking them for fetching.
1610 * Stop at end of screen or sooner if we run out of visible
1614 n
= mn_raw2m(msgmap
, thrd
->rawno
);
1616 && n
<= mn_get_total(msgmap
)
1617 && (!(ic
=fetch_ice(stream
,thrd
->rawno
)) || !(ic
=ic
->tice
) || !ic
->ifield
)){
1618 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1624 /* find next thread which is visible */
1626 if(mn_get_revsort(msgmap
) && thrd
->prevthd
)
1627 thrd
= fetch_thread(stream
, thrd
->prevthd
);
1628 else if(!mn_get_revsort(msgmap
) && thrd
->nextthd
)
1629 thrd
= fetch_thread(stream
, thrd
->nextthd
);
1633 && msgline_hidden(stream
, msgmap
,
1634 mn_raw2m(msgmap
, thrd
->rawno
), 0));
1641 * First add the msgno we're asking for in case it
1644 if(msgno
> 0L && msgno
<= mn_get_total(msgmap
)
1645 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,msgno
)))) || !ic
->ifield
)){
1646 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1648 * If we're doing a MUTTLIKE display the index line
1649 * may depend on the thread parent, and grandparent,
1650 * and further back. So just fetch the whole thread
1654 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1656 thrd
= fetch_thread(stream
, thrd
->top
);
1658 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1660 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1661 && (mc
= mail_elt(stream
,rawno
))
1662 && !mc
->private.msg
.env
){
1671 && n
<= mn_get_total(msgmap
)
1672 && (!(ic
=fetch_ice(stream
, (rawno
=mn_m2raw(msgmap
,n
)))) || !ic
->ifield
)){
1673 if((thrd
= fetch_thread(stream
, rawno
)) != NULL
){
1675 * If we're doing a MUTTLIKE display the index line
1676 * may depend on the thread parent, and grandparent,
1677 * and further back. So just fetch the whole thread
1681 && ps_global
->thread_disp_style
== THREAD_MUTTLIKE
1683 thrd
= fetch_thread(stream
, thrd
->top
);
1685 count
+= mark_msgs_in_thread(stream
, thrd
, msgmap
);
1687 else if(rawno
> 0L && rawno
<= stream
->nmsgs
1688 && (mc
= mail_elt(stream
,rawno
))
1689 && !mc
->private.msg
.env
){
1698 /* find next n which is visible */
1699 while(++n
<= mn_get_total(msgmap
)
1700 && msgline_hidden(stream
, msgmap
, n
, 0))
1706 seq
= build_sequence(stream
, NULL
, NULL
);
1708 ps_global
->dont_count_flagchanges
= 1;
1709 mail_fetch_overview_sequence(stream
, seq
,
1710 (stream
->dtb
&& stream
->dtb
->name
1711 && !strcmp(stream
->dtb
->name
, "imap"))
1712 ? NULL
: load_overview
);
1713 ps_global
->dont_count_flagchanges
= 0;
1714 fs_give((void **) &seq
);
1719 * reassign ice from the cache as it may've been built
1720 * within the overview callback or it may have become stale
1721 * in the prior sequence bit setting loop ...
1723 rawno
= mn_m2raw(msgmap
, msgno
);
1724 ice
= fetch_ice(stream
, rawno
);
1729 if((THRD_INDX() && !(ice
->tice
&& ice
->tice
->ifield
))
1730 || (!THRD_INDX() && !ice
->ifield
)){
1734 * With pre-fetching/callback-formatting done and no success,
1735 * fall into formatting the requested line...
1737 memset(&idata
, 0, sizeof(INDEXDATA_S
));
1738 idata
.stream
= stream
;
1739 idata
.msgno
= msgno
;
1740 idata
.rawno
= mn_m2raw(msgmap
, msgno
);
1741 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
1742 && (mc
= mail_elt(stream
, idata
.rawno
))){
1743 idata
.size
= mc
->rfc822_size
;
1744 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
1749 ice
= (*format_index_line
)(&idata
);
1755 * If needed, reset the print_format strings so that they add up to
1756 * the right total width. The reset width functionality isn't implemented
1757 * for THRD_INDX() so we are just doing a complete rebuild in that
1758 * case. This is driven by the clear_ice() call in clear_index_cache_ent()
1759 * so it should never be the case that THRD_INDX() is true and only
1760 * widths_done needs to be fixed.
1762 if((!THRD_INDX() && ice
->ifield
&& !ice
->widths_done
)){
1767 if(need_format_setup(stream
))
1768 setup_header_widths(stream
);
1771 working_ice
= ice
? ice
->tice
: NULL
;
1777 * First fix the ifield widths. The cdescs with nonzero widths
1778 * should correspond to the ifields that are defined.
1780 ifield
= working_ice
->ifield
;
1781 for(cdesc
= ps_global
->index_disp_format
;
1782 cdesc
->ctype
!= iNothing
&& ifield
; cdesc
++){
1784 if(cdesc
->ctype
!= ifield
->ctype
){
1785 dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno
, (int) cdesc
->ctype
, (int) ifield
->ctype
));
1789 ifield
->width
= cdesc
->width
;
1790 ifield
= ifield
->next
;
1794 /* fix the print_format strings and widths */
1795 for(ifield
= working_ice
->ifield
; ifield
; ifield
= ifield
->next
)
1796 set_ielem_widths_in_field(ifield
);
1798 working_ice
->widths_done
= 1;
1802 if(THRD_INDX() && ice
->tice
)
1803 ice
->tice
->color_lookup_done
= 1;
1806 * Look for a color for this line (and other lines in the current
1807 * view). This does a SEARCH for each role which has a color until
1808 * it finds a match. This will be satisfied by the c-client
1809 * cache created by the mail_fetch_overview above if it is a header
1812 if(!THRD_INDX() && !ice
->color_lookup_done
){
1813 COLOR_PAIR
*linecolor
;
1816 PAT_STATE
*pstate
= NULL
;
1818 if(pico_usingcolor()){
1820 if(THREADING() && sp_viewing_a_thread(stream
)){
1821 for(visible
= 0L, n
= top_msgno
;
1822 visible
< msgcount
&& n
<= mn_get_total(msgmap
);
1825 if(!get_lflag(stream
, msgmap
, n
, MN_CHID2
))
1828 if(!msgline_hidden(stream
, msgmap
, n
, 0))
1834 visible
= mn_get_total(msgmap
)
1835 - any_lflagged(msgmap
, MN_HIDE
|MN_CHID
);
1837 limit
= MIN(visible
, msgcount
);
1839 /* clear sequence bits */
1840 for(n
= 1L; n
<= stream
->nmsgs
; n
++)
1841 if((mc
= mail_elt(stream
, n
)) != NULL
)
1848 && n
<= mn_get_total(msgmap
)
1849 && (!(ic
=fetch_ice(stream
,(rawno
= mn_m2raw(msgmap
, n
)))) || !ic
->color_lookup_done
)){
1851 if(rawno
>= 1L && rawno
<= stream
->nmsgs
1852 && (mc
= mail_elt(stream
, rawno
))){
1861 /* find next n which is visible */
1862 while(++n
<= mn_get_total(msgmap
)
1863 && msgline_hidden(stream
, msgmap
, n
, 0))
1868 * Why is there a loop here? The first call to get_index_line_color
1869 * will return a set of messages which match one of the roles.
1870 * Then, we eliminate those messages from the search set and try
1871 * again. This time we'd get past that role and into a different
1872 * role. Because of that, we hang onto the state and don't reset
1873 * to the first_pattern on the second and subsequent times
1874 * through the loop, avoiding fruitless match_pattern calls in
1875 * get_index_line_color.
1876 * Before the first call, pstate should be set to NULL.
1879 ss
= build_searchset(stream
);
1884 colormatch
= get_index_line_color(stream
, ss
, &pstate
,
1888 * Assign this color to all matched msgno's and
1889 * turn off the sequence bit so we won't check
1893 for(s
= ss
; s
; s
= s
->next
){
1894 for(n
= s
->first
; n
<= s
->last
; n
++){
1895 if(n
>= 1L && n
<= stream
->nmsgs
1896 && (mc
= mail_elt(stream
, n
))
1900 ic
= fetch_ice(stream
, n
);
1902 ic
->color_lookup_done
= 1;
1904 ic
->linecolor
= new_color_pair(linecolor
->fg
,
1912 free_color_pair(&linecolor
);
1915 /* have to mark the rest of the lookups done */
1916 for(s
= ss
; s
&& cnt
> 0; s
= s
->next
){
1917 for(n
= s
->first
; n
<= s
->last
&& cnt
> 0; n
++){
1918 if(n
>= 1L && n
<= stream
->nmsgs
1919 && (mc
= mail_elt(stream
, n
))
1922 ic
= fetch_ice(stream
, n
);
1924 ic
->color_lookup_done
= 1;
1929 /* just making sure */
1933 mail_free_searchset(&ss
);
1939 ice
= fetch_ice(stream
, mn_m2raw(msgmap
, msgno
));
1942 ice
->color_lookup_done
= 1;
1945 return(ice
); /* Return formatted index data */
1950 day_of_week(struct date
*d
)
1961 m
-= 3; /* March is month 0 */
1963 return((d
->day
+2+((7+31*m
)/12)+y
+(y
/4)+(y
/400)-(y
/100))%7);
1967 static int daytab
[2][13] = {
1968 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1969 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1973 day_of_year(struct date
*d
)
1977 if(d
->year
<= 0 || d
->month
< 1 || d
->month
> 12)
1981 leap
= (d
->year
%4 == 0 && d
->year
%100 != 0) || d
->year
%400 == 0;
1982 for(i
= 1; i
< d
->month
; i
++)
1983 doy
+= daytab
[leap
][i
];
1990 /*----------------------------------------------------------------------
1991 Format a string summarizing the message header for index on screen
1993 Args: buffer -- buffer to place formatted line
1994 idata -- snot it takes to format the line
1996 Result: returns pointer given buffer IF entry formatted
1997 else NULL if there was a problem (but the buffer is
1998 still suitable for display)
2001 format_index_index_line(INDEXDATA_S
*idata
)
2003 char str
[BIGWIDTH
+1], to_us
, status
, *field
,
2005 int i
, collapsed
= 0, start
, fromfield
;
2008 MESSAGECACHE
*mc
= NULL
;
2009 ADDRESS
*addr
, *toaddr
, *ccaddr
, *last_to
;
2010 PINETHRD_S
*thrd
= NULL
;
2011 INDEX_COL_S
*cdesc
= NULL
;
2015 COLOR_PAIR
*color
= NULL
;
2016 struct variable
*vars
= ps_global
->vars
;
2018 dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
2019 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
2022 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
2026 free_ifield(&ice
->ifield
);
2029 * Operate on a temporary copy of ice. The reason for this
2030 * is that we may end up causing a pine_mail_fetchenvelope() call
2031 * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
2032 * and mm_flags may do a clear_ice(), freeing the ice we are working
2033 * on out from under us. We try to fetch everything we need in
2034 * build_header_work() but c-client will short-circuit our request
2035 * if we already got the raw header for some reason. One possible
2036 * reason is a categorizer command in a filter. In that case
2037 * we still need a fetch fast to get the rest of the envelope data.
2039 ice
= copy_ice(ice
);
2041 /* is this a collapsed thread index line? */
2042 if(!idata
->bogus
&& THREADING()){
2043 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2044 collapsed
= thrd
&& thrd
->next
2045 && get_lflag(idata
->stream
, NULL
,
2046 idata
->rawno
, MN_COLL
);
2049 /* calculate contents of the required fields */
2050 for(cdesc
= ps_global
->index_disp_format
; cdesc
->ctype
!= iNothing
; cdesc
++)
2052 memset(str
, 0, sizeof(str
));
2053 ifield
= new_ifield(&ice
->ifield
);
2054 ifield
->ctype
= cdesc
->ctype
;
2055 ifield
->width
= cdesc
->width
;
2059 if(cdesc
->ctype
== iMessNo
)
2060 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2061 else if(idata
->bogus
< 2 && (cdesc
->ctype
== iSubject
2062 || cdesc
->ctype
== iShortSubject
2063 || cdesc
->ctype
== iSubjectText
2064 || cdesc
->ctype
== iSubjKey
2065 || cdesc
->ctype
== iShortSubjKey
2066 || cdesc
->ctype
== iSubjKeyText
2067 || cdesc
->ctype
== iSubjKeyInit
2068 || cdesc
->ctype
== iShortSubjKeyInit
2069 || cdesc
->ctype
== iSubjKeyInitText
))
2070 snprintf(str
, sizeof(str
), "%s", _("[ No Message Text Available ]"));
2073 switch(cdesc
->ctype
){
2075 to_us
= status
= ' ';
2077 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2078 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
2079 status
= status_symbol_for_thread(idata
->stream
, thrd
,
2083 if(idata
->rawno
> 0L && idata
->rawno
<= idata
->stream
->nmsgs
2084 && (mc
=mail_elt(idata
->stream
,idata
->rawno
)) && mc
->flagged
)
2085 to_us
= '*'; /* simple */
2086 else if(!IS_NEWS(idata
->stream
)){
2087 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2088 if(address_is_us(addr
, ps_global
)){
2096 if(to_us
!= '+' && resent_to_us(idata
)){
2102 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2103 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2104 if(address_is_us(addr
, ps_global
)){
2111 status
= (!idata
->stream
|| !IS_NEWS(idata
->stream
)
2112 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))
2118 if(user_flag_is_set(idata
->stream
, idata
->rawno
, FORWARDED_FLAG
))
2128 snprintf(str
, sizeof(str
), "%c %c", to_us
, status
);
2130 ifield
->leftadj
= 1;
2131 for(i
= 0; i
< 3; i
++){
2132 ielem
= new_ielem(&ifield
->ielem
);
2133 ielem
->freedata
= 1;
2134 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2135 ielem
->data
[0] = str
[i
];
2136 ielem
->data
[1] = '\0';
2138 set_print_format(ielem
, 1, ifield
->leftadj
);
2141 if(pico_usingcolor()){
2144 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2145 ielem
= ifield
->ielem
;
2146 ielem
->freecolor
= 1;
2147 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2150 else if(str
[0] == '+' || str
[0] == '-'){
2151 if(VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
2152 ielem
= ifield
->ielem
;
2153 ielem
->freecolor
= 1;
2154 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2159 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2160 ielem
= ifield
->ielem
->next
->next
;
2161 ielem
->freecolor
= 1;
2162 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2165 else if(str
[2] == 'A'){
2166 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2167 ielem
= ifield
->ielem
->next
->next
;
2168 ielem
->freecolor
= 1;
2169 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2172 else if(str
[2] == 'F'){
2173 if(VAR_IND_FWD_FORE_COLOR
&& VAR_IND_FWD_BACK_COLOR
){
2174 ielem
= ifield
->ielem
->next
->next
;
2175 ielem
->freecolor
= 1;
2176 ielem
->color
= new_color_pair(VAR_IND_FWD_FORE_COLOR
, VAR_IND_FWD_BACK_COLOR
);
2179 else if(str
[2] == 'N'){
2180 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2181 ielem
= ifield
->ielem
->next
->next
;
2182 ielem
->freecolor
= 1;
2183 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2194 char new, answered
, deleted
, flagged
;
2197 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
2198 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 0);
2202 if(!IS_NEWS(idata
->stream
)){
2203 for(addr
= fetch_to(idata
); addr
; addr
= addr
->next
)
2204 if(address_is_us(addr
, ps_global
)){
2209 if(to_us
== ' ' && resent_to_us(idata
))
2212 if(to_us
== ' ' && F_ON(F_MARK_FOR_CC
,ps_global
))
2213 for(addr
= fetch_cc(idata
); addr
; addr
= addr
->next
)
2214 if(address_is_us(addr
, ps_global
)){
2221 new = answered
= deleted
= flagged
= ' ';
2224 unsigned long save_branch
, cnt
, tot_in_thrd
;
2227 * Branch is a sibling, not part of the thread, so
2228 * don't consider it when displaying this line.
2230 save_branch
= thrd
->branch
;
2233 tot_in_thrd
= count_flags_in_thread(idata
->stream
, thrd
,
2236 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_DEL
);
2238 deleted
= (cnt
== tot_in_thrd
) ? 'D' : 'd';
2240 cnt
= count_flags_in_thread(idata
->stream
, thrd
, F_ANS
);
2242 answered
= (cnt
== tot_in_thrd
) ? 'A' : 'a';
2244 /* no lower case *, same thing for some or all */
2245 if(count_flags_in_thread(idata
->stream
, thrd
, F_FLAG
))
2248 new = status_symbol_for_thread(idata
->stream
, thrd
,
2251 thrd
->branch
= save_branch
;
2254 mc
= (idata
->rawno
> 0L && idata
->stream
2255 && idata
->rawno
<= idata
->stream
->nmsgs
)
2256 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2257 if(mc
&& mc
->valid
){
2258 if(cdesc
->ctype
== iIStatus
|| cdesc
->ctype
== iSIStatus
){
2260 new = mc
->seen
? 'R' : 'N';
2265 && (!IS_NEWS(idata
->stream
)
2266 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
)))
2280 snprintf(str
, sizeof(str
), "%c %c%c%c%c", to_us
, flagged
, new,
2283 if(cdesc
->ctype
== iSIStatus
)
2288 ifield
->leftadj
= 1;
2289 for(i
= start
; i
< 6; i
++){
2290 ielem
= new_ielem(&ifield
->ielem
);
2291 ielem
->freedata
= 1;
2292 ielem
->data
= (char *) fs_get(2 * sizeof(char));
2293 ielem
->data
[0] = str
[i
];
2294 ielem
->data
[1] = '\0';
2296 set_print_format(ielem
, 1, ifield
->leftadj
);
2299 if(pico_usingcolor()){
2301 if(str
[0] == '+' || str
[0] == '-'){
2303 && VAR_IND_PLUS_FORE_COLOR
2304 && VAR_IND_PLUS_BACK_COLOR
){
2305 ielem
= ifield
->ielem
;
2306 ielem
->freecolor
= 1;
2307 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
, VAR_IND_PLUS_BACK_COLOR
);
2312 if(VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
2314 ielem
= ifield
->ielem
;
2316 ielem
= ifield
->ielem
->next
->next
;
2318 ielem
->freecolor
= 1;
2319 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
, VAR_IND_IMP_BACK_COLOR
);
2323 if(str
[3] == 'N' || str
[3] == 'n'){
2324 if(VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
2326 ielem
= ifield
->ielem
->next
;
2328 ielem
= ifield
->ielem
->next
->next
->next
;
2330 ielem
->freecolor
= 1;
2331 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
, VAR_IND_NEW_BACK_COLOR
);
2334 else if(str
[3] == 'R' || str
[3] == 'r'){
2335 if(VAR_IND_REC_FORE_COLOR
&& VAR_IND_REC_BACK_COLOR
){
2337 ielem
= ifield
->ielem
->next
;
2339 ielem
= ifield
->ielem
->next
->next
->next
;
2341 ielem
->freecolor
= 1;
2342 ielem
->color
= new_color_pair(VAR_IND_REC_FORE_COLOR
, VAR_IND_REC_BACK_COLOR
);
2345 else if(str
[3] == 'U' || str
[3] == 'u'){
2346 if(VAR_IND_UNS_FORE_COLOR
&& VAR_IND_UNS_BACK_COLOR
){
2348 ielem
= ifield
->ielem
->next
;
2350 ielem
= ifield
->ielem
->next
->next
->next
;
2352 ielem
->freecolor
= 1;
2353 ielem
->color
= new_color_pair(VAR_IND_UNS_FORE_COLOR
, VAR_IND_UNS_BACK_COLOR
);
2357 if(str
[4] == 'A' || str
[4] == 'a'){
2358 if(VAR_IND_ANS_FORE_COLOR
&& VAR_IND_ANS_BACK_COLOR
){
2360 ielem
= ifield
->ielem
->next
->next
;
2362 ielem
= ifield
->ielem
->next
->next
->next
->next
;
2364 ielem
->freecolor
= 1;
2365 ielem
->color
= new_color_pair(VAR_IND_ANS_FORE_COLOR
, VAR_IND_ANS_BACK_COLOR
);
2369 if(str
[5] == 'D' || str
[5] == 'd'){
2370 if(VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
2372 ielem
= ifield
->ielem
->next
->next
->next
;
2374 ielem
= ifield
->ielem
->next
->next
->next
->next
->next
;
2376 ielem
->freecolor
= 1;
2377 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
, VAR_IND_DEL_BACK_COLOR
);
2387 * This is a special case. The message number is
2388 * generated on the fly in the painting routine.
2389 * But the data array is allocated here in case it
2390 * is useful for the paint routine.
2392 snprintf(str
, sizeof(str
), "%*.*s", ifield
->width
, ifield
->width
, " ");
2396 snprintf(str
, sizeof(str
), "%-*.*s", ifield
->width
, ifield
->width
, " ");
2397 if(VAR_IND_ARR_FORE_COLOR
&& VAR_IND_ARR_BACK_COLOR
){
2398 ifield
->leftadj
= 1;
2399 ielem
= new_ielem(&ifield
->ielem
);
2400 ielem
->freedata
= 1;
2401 ielem
->data
= cpystr(str
);
2402 ielem
->datalen
= strlen(str
);
2403 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
2404 ielem
->freecolor
= 1;
2405 ielem
->color
= new_color_pair(VAR_IND_ARR_FORE_COLOR
,
2406 VAR_IND_ARR_BACK_COLOR
);
2412 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2413 if(score
== SCORE_UNDEF
){
2414 SEARCHSET
*ss
= NULL
;
2416 ss
= mail_newsearchset();
2417 ss
->first
= ss
->last
= (unsigned long) idata
->rawno
;
2420 * This looks like it might be expensive to get the
2421 * score for each message when needed but it shouldn't
2422 * be too bad because we know we have the envelope
2423 * data cached. We can't calculate all of the scores
2424 * we need for the visible messages right here in
2425 * one fell swoop because we don't have the other
2426 * envelopes yet. And we can't get the other
2427 * envelopes at this point because we may be in
2428 * the middle of a c-client callback (pine_imap_env).
2429 * (Actually we could, because we know whether or
2430 * not we're in the callback because of the no_fetch
2432 * We have another problem if the score rules depend
2433 * on something other than envelope data. I guess they
2434 * only do that if they have an alltext (search the
2435 * text of the message) definition. So, we're going
2436 * to pass no_fetch to calculate_scores so that it
2437 * can return an error if we need the text data but
2438 * can't get it because of no_fetch. Setting bogus
2439 * will cause us to do the scores calculation later
2440 * when we are no longer in the callback.
2443 (calculate_some_scores(idata
->stream
,
2444 ss
, idata
->no_fetch
) == 0)
2446 score
= get_msg_score(idata
->stream
, idata
->rawno
);
2447 mail_free_searchset(&ss
);
2451 snprintf(str
, sizeof(str
), "%ld", score
!= SCORE_UNDEF
? score
: 0L);
2454 case iDate
: case iMonAbb
: case iLDate
:
2455 case iSDate
: case iSTime
: case iSTime24
:
2456 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
2457 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
2458 case iSDateIsoS
: case iSDateIso
:
2459 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
2461 case iSDateTimeIsoS
: case iSDateTimeIso
:
2462 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
2464 case iSDateTimeIsoS24
: case iSDateTimeIso24
:
2465 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
2466 case iTimezone
: case iYear
: case iYear2Digit
:
2467 case iRDate
: case iDay
: case iDay2Digit
: case iMon2Digit
:
2468 case iDayOrdinal
: case iMon
: case iMonLong
:
2469 case iDayOfWeekAbb
: case iDayOfWeek
:
2470 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
2471 date_str(fetch_date(idata
), cdesc
->ctype
, 0, str
, sizeof(str
), cdesc
->monabb_width
);
2475 case iFromToNotNews
:
2480 from_str(cdesc
->ctype
, idata
, str
, sizeof(str
), ice
);
2484 if(((field
= ((addr
= fetch_to(idata
))
2486 : (addr
= fetch_cc(idata
))
2489 && !set_index_addr(idata
, field
, addr
, NULL
, BIGWIDTH
, str
))
2491 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2492 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, newsgroups
);
2497 set_index_addr(idata
, "Cc", fetch_cc(idata
), NULL
, BIGWIDTH
, str
);
2501 toaddr
= fetch_to(idata
);
2502 ccaddr
= fetch_cc(idata
);
2503 for(last_to
= toaddr
;
2504 last_to
&& last_to
->next
;
2505 last_to
= last_to
->next
)
2508 /* point end of to list temporarily at cc list */
2510 last_to
->next
= ccaddr
;
2512 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2515 last_to
->next
= NULL
;
2521 if((addr
= fetch_sender(idata
)) != NULL
)
2522 set_index_addr(idata
, "Sender", addr
, NULL
, BIGWIDTH
, str
);
2529 if((addr
= fetch_from(idata
)) && addr
->personal
){
2530 char *name
, *initials
= NULL
;
2532 name
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
2533 SIZEOF_20KBUF
, addr
->personal
);
2534 if(name
== addr
->personal
){
2535 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
2536 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
2537 name
= (char *) tmp_20k_buf
;
2541 initials
= reply_quote_initials(name
);
2542 snprintf(str
, sizeof(str
), "%-.*s", BIGWIDTH
, initials
);
2551 if((l
= fetch_size(idata
)) < 10*1000L)
2552 snprintf(str
, sizeof(str
), "(%lu)", l
);
2554 else if(l
< 1000L*1000L - 1000L/2){
2555 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2556 snprintf(str
, sizeof(str
), "(%luK)", l
);
2558 /* 1.0M ... 99.9M */
2559 else if(l
< 1000L*100L*1000L - 100L*1000L/2){
2560 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2562 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2564 /* 100M ... 2000M */
2565 else if(l
<= 2*1000L*1000L*1000L){
2566 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2568 snprintf(str
, sizeof(str
), "(%luM)", l
);
2571 snprintf(str
, sizeof(str
), "(HUGE!)");
2577 if((l
= fetch_size(idata
)) < 100*1000L)
2578 snprintf(str
, sizeof(str
), "(%s)", comatose(l
));
2579 /* 100K ... 9,999K */
2580 else if(l
< 10L*1000L*1000L - 1000L/2){
2581 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2582 snprintf(str
, sizeof(str
), "(%sK)", comatose(l
));
2584 /* 10.0M ... 999.9M */
2585 else if(l
< 1000L*1000L*1000L - 100L*1000L/2){
2586 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= (100*1000L/2)
2588 snprintf(str
, sizeof(str
), "(%lu.%luM)", l
/10L, l
% 10L);
2590 /* 1,000M ... 2,000M */
2591 else if(l
<= 2*1000L*1000L*1000L){
2592 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2594 snprintf(str
, sizeof(str
), "(%sM)", comatose(l
));
2597 snprintf(str
, sizeof(str
), "(HUGE!)");
2603 if((l
= fetch_size(idata
)) < 1000L)
2604 snprintf(str
, sizeof(str
), "(%lu)", l
);
2606 else if(l
< 100L*1000L - 1000L/2){
2607 l
= l
/1000L + (l
%1000L >= 1000L/2 ? 1L : 0L);
2608 snprintf(str
, sizeof(str
), "(%luK)", l
);
2611 else if(l
< 1000L*1000L - 100L*1000L/2){
2612 l
= l
/(100L*1000L) + (l
%(100L*1000L) >= 100L*1000L/2
2614 snprintf(str
, sizeof(str
), "(.%luM)", l
);
2617 else if(l
< 1000L*100L*1000L - 1000L*1000L/2){
2618 l
= l
/(1000L*1000L) + (l
%(1000L*1000L) >= (1000L*1000L/2)
2620 snprintf(str
, sizeof(str
), "(%luM)", l
);
2623 else if(l
< 1000L*1000L*1000L - 100L*1000L*1000L/2){
2624 l
= l
/(100L*1000L*1000L) + (l
%(100L*1000L*1000L) >=
2625 (100L*1000L*1000L/2) ? 1L : 0L);
2626 snprintf(str
, sizeof(str
), "(.%luG)", l
);
2629 else if(l
<= 2*1000L*1000L*1000L){
2630 l
= l
/(1000L*1000L*1000L) + (l
%(1000L*1000L*1000L) >=
2631 (1000L*1000L*1000L/2) ? 1L : 0L);
2632 snprintf(str
, sizeof(str
), "(%luG)", l
);
2635 snprintf(str
, sizeof(str
), "(HUGE!)");
2639 /* From Carl Jacobsen <carl@ucsd.edu> */
2641 l
= fetch_size(idata
);
2642 l
= (l
/ 1024L) + (l
% 1024L != 0 ? 1 : 0);
2644 if(l
< 1024L) { /* 0k .. 1023k */
2645 snprintf(str
, sizeof(str
), "(%luk)", l
);
2647 } else if (l
< 100L * 1024L){ /* 1.0M .. 99.9M */
2648 snprintf(str
, sizeof(str
), "(%lu.M)", (l
* 10L) / 1024L);
2649 if ((p
= strchr(str
, '.')) != NULL
) {
2650 p
--; p
[1] = p
[0]; p
[0] = '.'; /* swap last digit & . */
2652 } else if (l
<= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2653 snprintf(str
, sizeof(str
), "(%luM)", l
/ 1024L);
2655 snprintf(str
, sizeof(str
), "(HUGE!)");
2661 if((body
= fetch_body(idata
)) != NULL
)
2665 mc
= (idata
->rawno
> 0L && idata
->stream
2666 && idata
->rawno
<= idata
->stream
->nmsgs
)
2667 ? mail_elt(idata
->stream
, idata
->rawno
) : NULL
;
2668 if(mc
&& mc
->rfc822_size
< 6000)
2669 snprintf(str
, sizeof(str
), "(short )");
2670 else if(mc
&& mc
->rfc822_size
< 25000)
2671 snprintf(str
, sizeof(str
), "(medium )");
2672 else if(mc
&& mc
->rfc822_size
< 100000)
2673 snprintf(str
, sizeof(str
), "(long )");
2675 snprintf(str
, sizeof(str
), "(huge )");
2681 if(strucmp(body
->subtype
, "MIXED") == 0){
2684 x
= body
->nested
.part
2685 ? body
->nested
.part
->body
.type
2689 if(body
->nested
.part
->body
.size
.bytes
< 6000)
2690 snprintf(str
, sizeof(str
), "(short+ )");
2691 else if(body
->nested
.part
->body
.size
.bytes
2693 snprintf(str
, sizeof(str
), "(medium+)");
2694 else if(body
->nested
.part
->body
.size
.bytes
2696 snprintf(str
, sizeof(str
), "(long+ )");
2698 snprintf(str
, sizeof(str
), "(huge+ )");
2702 snprintf(str
, sizeof(str
), "(multi )");
2706 else if(strucmp(body
->subtype
, "DIGEST") == 0)
2707 snprintf(str
, sizeof(str
), "(digest )");
2708 else if(strucmp(body
->subtype
, "ALTERNATIVE") == 0)
2709 snprintf(str
, sizeof(str
), "(mul/alt)");
2710 else if(strucmp(body
->subtype
, "PARALLEL") == 0)
2711 snprintf(str
, sizeof(str
), "(mul/par)");
2713 snprintf(str
, sizeof(str
), "(multi )");
2718 snprintf(str
, sizeof(str
), "(message)");
2721 case TYPEAPPLICATION
:
2722 snprintf(str
, sizeof(str
), "(applica)");
2726 snprintf(str
, sizeof(str
), "(audio )");
2730 snprintf(str
, sizeof(str
), "(image )");
2734 snprintf(str
, sizeof(str
), "(video )");
2738 snprintf(str
, sizeof(str
), "(other )");
2747 if((body
= fetch_body(idata
)) &&
2748 body
->type
== TYPEMULTIPART
&&
2749 strucmp(body
->subtype
, "ALTERNATIVE") != 0){
2753 part
= body
->nested
.part
; /* 1st part, don't count */
2754 while(part
&& part
->next
&& atts
< 10){
2762 str
[0] = '0' + atts
;
2768 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 0, ice
);
2772 subj_str(idata
, str
, sizeof(str
), NoKW
, 0, 1, ice
);
2776 subj_str(idata
, str
, sizeof(str
), NoKW
, 1, 0, ice
);
2780 subj_str(idata
, str
, sizeof(str
), KW
, 0, 0, ice
);
2784 subj_str(idata
, str
, sizeof(str
), KW
, 0, 1, ice
);
2788 subj_str(idata
, str
, sizeof(str
), KW
, 1, 0, ice
);
2792 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 0, ice
);
2795 case iShortSubjKeyInit
:
2796 subj_str(idata
, str
, sizeof(str
), KWInit
, 0, 1, ice
);
2799 case iSubjKeyInitText
:
2800 subj_str(idata
, str
, sizeof(str
), KWInit
, 1, 0, ice
);
2804 case iOpeningTextNQ
:
2810 first_text
= fetch_firsttext(idata
, cdesc
->ctype
== iOpeningTextNQ
);
2813 strncpy(str
, first_text
, BIGWIDTH
);
2814 str
[BIGWIDTH
] = '\0';
2821 key_str(idata
, KW
, ice
);
2825 key_str(idata
, KWInit
, ice
);
2829 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
){
2830 strncpy(str
, newsgroups
, BIGWIDTH
);
2831 str
[BIGWIDTH
] = '\0';
2837 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2838 strncpy(str
, newsgroups
, sizeof(str
));
2840 if((l
= strlen(str
)) < sizeof(str
)){
2841 if(sizeof(str
) - l
< 6)
2842 strncpy(str
+l
, "...", sizeof(str
)-l
);
2845 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2846 set_index_addr(idata
, "To", fetch_to(idata
),
2847 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2852 set_index_addr(idata
, "To", fetch_to(idata
),
2853 NULL
, BIGWIDTH
, str
);
2860 set_index_addr(idata
, "To", fetch_to(idata
),
2861 NULL
, BIGWIDTH
, str
);
2862 if((l
= strlen(str
)) < sizeof(str
) &&
2863 (newsgroups
= fetch_newsgroups(idata
))){
2864 if(sizeof(str
) - l
< 6)
2865 strncpy(str
+l
, "...", sizeof(str
)-l
);
2868 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2871 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2873 strncpy(str
, newsgroups
, BIGWIDTH
);
2879 case iNewsAndRecips
:
2880 if((newsgroups
= fetch_newsgroups(idata
)) != NULL
)
2881 strncpy(str
, newsgroups
, BIGWIDTH
);
2883 if((l
= strlen(str
)) < BIGWIDTH
){
2884 if(BIGWIDTH
- l
< 6)
2885 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2887 toaddr
= fetch_to(idata
);
2888 ccaddr
= fetch_cc(idata
);
2889 for(last_to
= toaddr
;
2890 last_to
&& last_to
->next
;
2891 last_to
= last_to
->next
)
2894 /* point end of to list temporarily at cc list */
2896 last_to
->next
= ccaddr
;
2899 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2900 set_index_addr(idata
, "To", toaddr
,
2901 NULL
, BIGWIDTH
-l
-5, str
+l
+5);
2906 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2909 last_to
->next
= NULL
;
2915 case iRecipsAndNews
:
2916 toaddr
= fetch_to(idata
);
2917 ccaddr
= fetch_cc(idata
);
2918 for(last_to
= toaddr
;
2919 last_to
&& last_to
->next
;
2920 last_to
= last_to
->next
)
2923 /* point end of to list temporarily at cc list */
2925 last_to
->next
= ccaddr
;
2927 set_index_addr(idata
, "To", toaddr
, NULL
, BIGWIDTH
, str
);
2930 last_to
->next
= NULL
;
2932 if((l
= strlen(str
)) < BIGWIDTH
&&
2933 (newsgroups
= fetch_newsgroups(idata
))){
2934 if(BIGWIDTH
- l
< 6)
2935 strncpy(str
+l
, "...", BIGWIDTH
-l
);
2938 strncpy(str
+l
, " and ", sizeof(str
)-l
);
2941 strncpy(str
+l
+5, newsgroups
, BIGWIDTH
-l
-5);
2943 strncpy(str
, newsgroups
, BIGWIDTH
);
2952 prio_str(idata
, cdesc
->ctype
, ice
);
2956 header_str(idata
, cdesc
->hdrtok
, ice
);
2960 strncpy(str
, (cdesc
->hdrtok
&& cdesc
->hdrtok
->hdrname
) ? cdesc
->hdrtok
->hdrname
: "", sizeof(str
));
2961 str
[sizeof(str
)-1] = '\0';
2969 * If the element wasn't already filled in above, do it here.
2972 ielem
= new_ielem(&ifield
->ielem
);
2974 if((color
= hdr_color(itokens
[itokensinv
[cdesc
->ctype
].ctype
].name
, NULL
, ps_global
->index_token_colors
)) != NULL
){
2975 if(pico_usingcolor()){
2976 ielem
->color
= new_color_pair(color
->fg
, color
->bg
);
2977 ielem
->type
= eTypeCol
;
2979 free_color_pair(&color
);
2982 ielem
->freedata
= 1;
2983 ielem
->data
= cpystr(str
);
2984 ielem
->datalen
= strlen(str
);
2986 if(fromfield
&& pico_usingcolor()
2987 && ps_global
->VAR_IND_FROM_FORE_COLOR
2988 && ps_global
->VAR_IND_FROM_BACK_COLOR
){
2989 ielem
->type
= eTypeCol
;
2990 ielem
->freecolor
= 1;
2991 ielem
->color
= new_color_pair(ps_global
->VAR_IND_FROM_FORE_COLOR
,
2992 ps_global
->VAR_IND_FROM_BACK_COLOR
);
2994 * This space is here so that if the text does
2995 * not extend all the way to the end of the field then
2996 * we'll switch the color back and paint the rest of the
2997 * field in the Normal color or the index line color.
2999 ielem
= new_ielem(&ielem
);
3000 ielem
->freedata
= 1;
3001 ielem
->data
= cpystr(" ");
3004 else if((cdesc
->ctype
== iOpeningText
|| cdesc
->ctype
== iOpeningTextNQ
)
3005 && pico_usingcolor()
3006 && ps_global
->VAR_IND_OP_FORE_COLOR
3007 && ps_global
->VAR_IND_OP_BACK_COLOR
){
3008 ielem
->type
= eTypeCol
;
3009 ielem
->freecolor
= 1;
3010 ielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
,
3011 ps_global
->VAR_IND_OP_BACK_COLOR
);
3013 * This space is here so that if the text does
3014 * not extend all the way to the end of the field then
3015 * we'll switch the color back and paint the rest of the
3016 * field in the Normal color or the index line color.
3018 ielem
= new_ielem(&ielem
);
3019 ielem
->freedata
= 1;
3020 ielem
->data
= cpystr(" ");
3024 ifield
->leftadj
= (cdesc
->adjustment
== Left
) ? 1 : 0;
3025 set_ielem_widths_in_field(ifield
);
3029 ice
->widths_done
= 1;
3030 ice
->id
= ice_hash(ice
);
3033 * Now we have to put the temporary copy of ice back as the
3036 icep
= fetch_ice_ptr(idata
->stream
, idata
->rawno
);
3038 free_ice(icep
); /* free what is already there */
3047 format_thread_index_line(INDEXDATA_S
*idata
)
3049 char *p
, buffer
[BIGWIDTH
+1];
3050 int thdlen
, space_left
, i
;
3051 PINETHRD_S
*thrd
= NULL
;
3052 ICE_S
*ice
, *tice
= NULL
, **ticep
= NULL
;
3055 int (*save_sfstr_func
)(void);
3056 struct variable
*vars
= ps_global
->vars
;
3058 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3059 idata
? idata
->msgno
: -1, idata
? idata
->rawno
: -1));
3061 space_left
= ps_global
->ttyo
->screen_cols
;
3063 if(ps_global
->msgmap
->max_thrdno
< 1000)
3065 else if(ps_global
->msgmap
->max_thrdno
< 10000)
3067 else if(ps_global
->msgmap
->max_thrdno
< 100000)
3072 ice
= fetch_ice(idata
->stream
, idata
->rawno
);
3074 thrd
= fetch_thread(idata
->stream
, idata
->rawno
);
3076 if(!thrd
|| !ice
) /* can't happen? */
3080 tice
= (ICE_S
*) fs_get(sizeof(*tice
));
3081 memset(tice
, 0, sizeof(*tice
));
3090 free_ifield(&tice
->ifield
);
3093 tice
= copy_ice(tice
);
3095 if(space_left
>= 3){
3099 to_us
= to_us_symbol_for_thread(idata
->stream
, thrd
, 1);
3100 status
= status_symbol_for_thread(idata
->stream
, thrd
, iStatus
);
3102 if((p
-buffer
)+3 < sizeof(buffer
)){
3111 ifield
= new_ifield(&tice
->ifield
);
3112 ifield
->ctype
= iStatus
;
3114 ifield
->leftadj
= 1;
3115 for(i
= 0; i
< 3; i
++){
3116 ielem
= new_ielem(&ifield
->ielem
);
3117 ielem
->freedata
= 1;
3118 ielem
->data
= (char *) fs_get(2 * sizeof(char));
3119 ielem
->data
[0] = p
[i
];
3120 ielem
->data
[1] = '\0';
3122 set_print_format(ielem
, 1, ifield
->leftadj
);
3125 if(pico_usingcolor()){
3127 && VAR_IND_IMP_FORE_COLOR
&& VAR_IND_IMP_BACK_COLOR
){
3128 ielem
= ifield
->ielem
;
3129 ielem
->freecolor
= 1;
3130 ielem
->color
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3131 VAR_IND_IMP_BACK_COLOR
);
3132 if(F_ON(F_COLOR_LINE_IMPORTANT
, ps_global
))
3133 tice
->linecolor
= new_color_pair(VAR_IND_IMP_FORE_COLOR
,
3134 VAR_IND_IMP_BACK_COLOR
);
3136 else if((to_us
== '+' || to_us
== '-')
3137 && VAR_IND_PLUS_FORE_COLOR
&& VAR_IND_PLUS_BACK_COLOR
){
3138 ielem
= ifield
->ielem
;
3139 ielem
->freecolor
= 1;
3140 ielem
->color
= new_color_pair(VAR_IND_PLUS_FORE_COLOR
,
3141 VAR_IND_PLUS_BACK_COLOR
);
3145 && VAR_IND_DEL_FORE_COLOR
&& VAR_IND_DEL_BACK_COLOR
){
3146 ielem
= ifield
->ielem
->next
->next
;
3147 ielem
->freecolor
= 1;
3148 ielem
->color
= new_color_pair(VAR_IND_DEL_FORE_COLOR
,
3149 VAR_IND_DEL_BACK_COLOR
);
3151 else if(status
== 'N'
3152 && VAR_IND_NEW_FORE_COLOR
&& VAR_IND_NEW_BACK_COLOR
){
3153 ielem
= ifield
->ielem
->next
->next
;
3154 ielem
->freecolor
= 1;
3155 ielem
->color
= new_color_pair(VAR_IND_NEW_FORE_COLOR
,
3156 VAR_IND_NEW_BACK_COLOR
);
3161 if(space_left
>= thdlen
+1){
3165 snprintf(p
, sizeof(buffer
), "%*.*s", thdlen
, thdlen
, "");
3166 space_left
-= thdlen
;
3168 ifield
= new_ifield(&tice
->ifield
);
3169 ifield
->ctype
= iMessNo
;
3170 ifield
->width
= thdlen
;
3171 ifield
->leftadj
= 0;
3172 ielem
= new_ielem(&ifield
->ielem
);
3173 ielem
->freedata
= 1;
3174 ielem
->data
= cpystr(p
);
3175 ielem
->datalen
= strlen(p
);
3176 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3179 if(space_left
>= 7){
3184 date_str(fetch_date(idata
), iDate
, 0, p
, sizeof(buffer
), 0);
3185 if(sizeof(buffer
) > 6)
3188 if(strlen(p
) < 6 && (sizeof(buffer
)) > 6){
3191 for(q
= p
+ strlen(p
); q
< p
+ 6; q
++)
3197 ifield
= new_ifield(&tice
->ifield
);
3198 ifield
->ctype
= iDate
;
3200 ifield
->leftadj
= 1;
3201 ielem
= new_ielem(&ifield
->ielem
);
3202 ielem
->freedata
= 1;
3203 ielem
->data
= cpystr(p
);
3204 ielem
->datalen
= ifield
->width
;
3205 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3210 int from_width
, subj_width
, bigthread_adjust
;
3212 char from
[BIGWIDTH
+1];
3217 in_thread
= count_lflags_in_thread(idata
->stream
, thrd
,
3218 ps_global
->msgmap
, MN_NONE
);
3221 if(in_thread
== 1 && THRD_AUTO_VIEW())
3222 snprintf(tcnt
, sizeof(tcnt
), " ");
3224 snprintf(tcnt
, sizeof(tcnt
), "(%ld)", in_thread
);
3226 bigthread_adjust
= MAX(0, strlen(tcnt
) - 3);
3228 /* third of the rest */
3229 from_width
= MAX((space_left
-1)/3 - bigthread_adjust
, 1);
3232 subj_width
= space_left
- from_width
- 1;
3234 if(strlen(tcnt
) > subj_width
)
3235 tcnt
[subj_width
] = '\0';
3238 save_sfstr_func
= pith_opt_truncate_sfstr
;
3239 pith_opt_truncate_sfstr
= NULL
;
3240 from_str(iFromTo
, idata
, from
, sizeof(from
), tice
);
3241 pith_opt_truncate_sfstr
= save_sfstr_func
;
3243 ifield
= new_ifield(&tice
->ifield
);
3244 ifield
->leftadj
= 1;
3245 ielem
= new_ielem(&ifield
->ielem
);
3246 ielem
->freedata
= 1;
3247 ielem
->type
= eTypeCol
;
3248 ielem
->data
= cpystr(from
);
3249 ielem
->datalen
= strlen(from
);
3250 ifield
->width
= from_width
;
3251 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3252 ifield
->ctype
= iFrom
;
3253 if(from_width
> 0 && pico_usingcolor()
3254 && VAR_IND_FROM_FORE_COLOR
&& VAR_IND_FROM_BACK_COLOR
){
3255 ielem
->freecolor
= 1;
3256 ielem
->color
= new_color_pair(VAR_IND_FROM_FORE_COLOR
,
3257 VAR_IND_FROM_BACK_COLOR
);
3260 ifield
= new_ifield(&tice
->ifield
);
3261 ifield
->leftadj
= 0;
3262 ielem
= new_ielem(&ifield
->ielem
);
3263 ielem
->freedata
= 1;
3264 ielem
->data
= cpystr(tcnt
);
3265 ielem
->datalen
= strlen(tcnt
);
3266 ifield
->width
= ielem
->datalen
;
3267 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3268 ifield
->ctype
= iAtt
; /* not used, except that it isn't special */
3270 subj_width
-= strlen(tcnt
);
3277 if(idata
->bogus
< 2)
3278 snprintf(buffer
, sizeof(buffer
), "%-.*s", BIGWIDTH
,
3279 _("[ No Message Text Available ]"));
3283 save_sfstr_func
= pith_opt_truncate_sfstr
;
3284 pith_opt_truncate_sfstr
= NULL
;
3285 subj_str(idata
, buffer
, sizeof(buffer
), NoKW
, 0, 0, NULL
);
3286 pith_opt_truncate_sfstr
= save_sfstr_func
;
3289 ifield
= new_ifield(&tice
->ifield
);
3290 ifield
->leftadj
= 1;
3291 ielem
= new_ielem(&ifield
->ielem
);
3292 ielem
->freedata
= 1;
3293 ielem
->type
= eTypeCol
;
3294 ielem
->data
= cpystr(buffer
);
3295 ielem
->datalen
= strlen(buffer
);
3296 ifield
->width
= subj_width
;
3297 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3298 ifield
->ctype
= iSubject
;
3299 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR
&& VAR_IND_SUBJ_BACK_COLOR
){
3300 ielem
->freecolor
= 1;
3301 ielem
->color
= new_color_pair(VAR_IND_SUBJ_FORE_COLOR
,
3302 VAR_IND_SUBJ_BACK_COLOR
);
3306 else if(space_left
> 1){
3307 snprintf(p
, sizeof(buffer
)-(p
-buffer
), "%-.*s", space_left
-1, " ");
3308 ifield
= new_ifield(&tice
->ifield
);
3309 ifield
->leftadj
= 1;
3310 ielem
= new_ielem(&ifield
->ielem
);
3311 ielem
->freedata
= 1;
3312 ielem
->data
= cpystr(p
);
3313 ielem
->datalen
= strlen(p
);
3314 ifield
->width
= space_left
-1;
3315 set_print_format(ielem
, ifield
->width
, ifield
->leftadj
);
3316 ifield
->ctype
= iSubject
;
3319 tice
->widths_done
= 1;
3320 tice
->id
= ice_hash(tice
);
3323 free_ice(ticep
); /* free what is already there */
3332 * Print the fields of ice in buf with a single space between fields.
3334 * Args buf -- place to put the line
3335 * ice -- the data for the line
3336 * msgno -- this is the msgno to be used, blanks if <= 0
3338 * Returns a pointer to buf.
3341 simple_index_line(char *buf
, size_t buflen
, ICE_S
*ice
, long int msgno
)
3344 IFIELD_S
*ifield
, *previfield
= NULL
;
3348 alpine_panic("NULL buf in simple_index_line()");
3357 for(ifield
= ice
->ifield
; ifield
&& p
-buf
< buflen
; ifield
= ifield
->next
){
3359 /* space between fields */
3360 if(ifield
!= ice
->ifield
&& !(previfield
&& previfield
->ctype
== iText
))
3363 /* message number string is generated on the fly */
3364 if(ifield
->ctype
== iMessNo
){
3365 ielem
= ifield
->ielem
;
3366 if(ielem
&& ielem
->datalen
>= ifield
->width
){
3368 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.ld", ifield
->width
, msgno
);
3370 snprintf(ielem
->data
, ielem
->datalen
+1, "%*.*s", ifield
->width
, ifield
->width
, "");
3374 for(ielem
= ifield
->ielem
;
3375 ielem
&& ielem
->print_format
&& p
-buf
< buflen
;
3376 ielem
= ielem
->next
){
3381 bytes_added
= utf8_pad_to_width(p
, src
,
3382 buflen
-(p
-buf
) * sizeof(char),
3383 ielem
->wid
, ifield
->leftadj
);
3387 previfield
= ifield
;
3394 buf
[buflen
-1] = '\0';
3401 * Look in current mail_stream for matches for messages in the searchset
3402 * which match a color rule pattern. Return the color.
3403 * The searched bit will be set for all of the messages which match the
3404 * first pattern which has a match.
3406 * Args stream -- the mail stream
3407 * searchset -- restrict attention to this set of messages
3408 * pstate -- The pattern state. On the first call it will be Null.
3409 * Null means start over with a new first_pattern.
3410 * After that it will be pointing to our local PAT_STATE
3411 * so that next_pattern goes to the next one after the
3412 * ones we've already checked.
3414 * Returns 0 if no match, 1 if a match.
3415 * The color that goes with the matched rule in returned_color.
3416 * It may be NULL, which indicates default.
3419 get_index_line_color(MAILSTREAM
*stream
, SEARCHSET
*searchset
,
3420 PAT_STATE
**pstate
, COLOR_PAIR
**returned_color
)
3423 long rflags
= ROLE_INCOL
;
3424 COLOR_PAIR
*color
= NULL
;
3426 static PAT_STATE localpstate
;
3428 dprint((7, "get_index_line_color\n"));
3431 *returned_color
= NULL
;
3434 pat
= next_pattern(*pstate
);
3436 *pstate
= &localpstate
;
3437 if(!nonempty_patterns(rflags
, *pstate
))
3441 pat
= first_pattern(*pstate
);
3446 /* Go through the possible roles one at a time until we get a match. */
3447 while(!match
&& pat
){
3448 if(match_pattern(pat
->patgrp
, stream
, searchset
, NULL
,
3449 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
3450 if(!pat
->action
|| pat
->action
->bogus
)
3454 if(pat
->action
&& pat
->action
->incol
)
3455 color
= new_color_pair(pat
->action
->incol
->fg
,
3456 pat
->action
->incol
->bg
);
3459 pat
= next_pattern(*pstate
);
3463 if(match
&& returned_color
)
3464 *returned_color
= color
;
3474 index_in_overview(MAILSTREAM
*stream
)
3476 INDEX_COL_S
*cdesc
= NULL
;
3478 if(!(stream
->mailbox
&& IS_REMOTE(stream
->mailbox
)))
3479 return(FALSE
); /* no point! */
3481 if(stream
->dtb
&& stream
->dtb
->name
&& !strcmp(stream
->dtb
->name
, "nntp")){
3486 for(cdesc
= ps_global
->index_disp_format
;
3487 cdesc
->ctype
!= iNothing
;
3489 switch(cdesc
->ctype
){
3490 case iTo
: /* can't be satisfied by XOVER */
3491 case iSender
: /* ... or specifically handled */
3492 case iDescripSize
: /* ... in news case */
3507 * fetch_from - called to get a the index entry's "From:" field
3510 resent_to_us(INDEXDATA_S
*idata
)
3512 if(!idata
->valid_resent_to
){
3513 static char *fields
[] = {"Resent-To", NULL
};
3516 if(idata
->no_fetch
){
3517 idata
->bogus
= 1; /* don't do this */
3521 if((h
= pine_fetchheader_lines(idata
->stream
,idata
->rawno
,NULL
,fields
)) != NULL
){
3522 idata
->resent_to_us
= parsed_resent_to_us(h
);
3523 fs_give((void **) &h
);
3526 idata
->valid_resent_to
= 1;
3529 return(idata
->resent_to_us
);
3534 parsed_resent_to_us(char *h
)
3537 ADDRESS
*addr
= NULL
;
3540 if((p
= strindex(h
, ':')) != NULL
){
3541 for(q
= ++p
; (q
= strpbrk(q
, "\015\012")) != NULL
; q
++)
3542 *q
= ' '; /* quash junk */
3544 rfc822_parse_adrlist(&addr
, p
, ps_global
->maildomain
);
3546 rv
= address_is_us(addr
, ps_global
);
3547 mail_free_address(&addr
);
3557 * fetch_from - called to get a the index entry's "From:" field
3560 fetch_from(INDEXDATA_S
*idata
)
3562 if(idata
->no_fetch
) /* implies from is valid */
3563 return(idata
->from
);
3564 else if(idata
->bogus
)
3569 /* c-client call's just cache access at this point */
3570 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3581 * fetch_to - called to get a the index entry's "To:" field
3584 fetch_to(INDEXDATA_S
*idata
)
3586 if(idata
->no_fetch
){ /* check for specific validity */
3590 idata
->bogus
= 1; /* can't give 'em what they want */
3592 else if(idata
->bogus
){
3593 idata
->bogus
= 2; /* elevate bogosity */
3598 /* c-client call's just cache access at this point */
3599 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3610 * fetch_cc - called to get a the index entry's "Cc:" field
3613 fetch_cc(INDEXDATA_S
*idata
)
3615 if(idata
->no_fetch
){ /* check for specific validity */
3619 idata
->bogus
= 1; /* can't give 'em what they want */
3621 else if(idata
->bogus
){
3622 idata
->bogus
= 2; /* elevate bogosity */
3627 /* c-client call's just cache access at this point */
3628 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3640 * fetch_sender - called to get a the index entry's "Sender:" field
3643 fetch_sender(INDEXDATA_S
*idata
)
3645 if(idata
->no_fetch
){ /* check for specific validity */
3646 if(idata
->valid_sender
)
3647 return(idata
->sender
);
3649 idata
->bogus
= 1; /* can't give 'em what they want */
3651 else if(idata
->bogus
){
3652 idata
->bogus
= 2; /* elevate bogosity */
3657 /* c-client call's just cache access at this point */
3658 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3659 return(env
->sender
);
3669 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3672 fetch_newsgroups(INDEXDATA_S
*idata
)
3674 if(idata
->no_fetch
){ /* check for specific validity */
3675 if(idata
->valid_news
)
3676 return(idata
->newsgroups
);
3678 idata
->bogus
= 1; /* can't give 'em what they want */
3680 else if(idata
->bogus
){
3681 idata
->bogus
= 2; /* elevate bogosity */
3686 /* c-client call's just cache access at this point */
3687 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3688 return(env
->newsgroups
);
3698 * fetch_subject - called to get at the index entry's "Subject:" field
3701 fetch_subject(INDEXDATA_S
*idata
)
3703 if(idata
->no_fetch
) /* implies subject is valid */
3704 return(idata
->subject
);
3705 else if(idata
->bogus
)
3710 /* c-client call's just cache access at this point */
3711 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3712 return(env
->subject
);
3722 * Return an allocated copy of the first few characters from the body
3723 * of the message for possible use in the index screen.
3725 * Maybe we could figure out some way to do aggregate calls to get
3726 * this info for all the lines in view instead of all the one at a
3727 * time calls we're doing now.
3730 fetch_firsttext(INDEXDATA_S
*idata
, int delete_quotes
)
3733 BODY
*body
= NULL
, *new_body
= NULL
;
3734 char *firsttext
= NULL
;
3737 long partial_fetch_len
= 0L;
3738 SEARCHSET
*ss
, **sset
;
3740 PINELT_S
*pelt
= NULL
;
3742 /* we cache the result we get from this function, so that we do not have to
3743 * refetch the text in case there is a change. We could cache in the envelope
3744 * but c-client does not have a special field for that, nor we want to use the
3745 * sparep pointer, since there could be other uses for sparep later, and even
3746 * if we add a pointer to the ENVELOPE structure, we would be caching the same
3747 * text twice (one in a private pointer, and the new pointer) and that would
3748 * not make sense. Instead we will use an elt for this
3751 if((mc
= mail_elt(idata
->stream
, idata
->rawno
))
3752 && ((pelt
= (PINELT_S
*) mc
->sparep
) == NULL
)){
3753 pelt
= (PINELT_S
*) fs_get(sizeof(PINELT_S
));
3754 memset(pelt
, 0, sizeof(PINELT_S
));
3757 if(pelt
&& pelt
->firsttext
!= NULL
)
3758 return(pelt
->firsttext
);
3763 * Prevent wild prefetch, just get the one we're after.
3764 * Can we get this somehow in the overview call in build_header_work?
3766 ss
= mail_newsearchset();
3767 ss
->first
= ss
->last
= idata
->rawno
;
3768 sset
= (SEARCHSET
**) mail_parameters(idata
->stream
,
3770 (void *) idata
->stream
);
3774 if((env
= pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
)) != NULL
){
3776 char *subtype
= NULL
;
3779 if((body
->type
== TYPETEXT
3780 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3782 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3783 && body
->nested
.part
->body
.type
== TYPETEXT
3784 && (subtype
=body
->nested
.part
->body
.subtype
)
3785 && ALLOWED_SUBTYPE(subtype
))
3787 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3788 && body
->nested
.part
->body
.type
== TYPEMULTIPART
3789 && body
->nested
.part
->body
.nested
.part
3790 && body
->nested
.part
->body
.nested
.part
->body
.type
== TYPETEXT
3791 && (subtype
=body
->nested
.part
->body
.nested
.part
->body
.subtype
)
3792 && ALLOWED_SUBTYPE(subtype
))){
3794 if((so
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
3798 int one_space_done
= 0;
3800 if(partial_fetch_len
== 0L){
3801 if(subtype
&& !strucmp(subtype
, "html"))
3802 partial_fetch_len
= 1024L;
3803 else if(subtype
&& !strucmp(subtype
, "plain"))
3804 partial_fetch_len
= delete_quotes
? 128L : 64L;
3806 partial_fetch_len
= 256L;
3809 if((body
->type
== TYPETEXT
3810 && (subtype
=body
->subtype
) && ALLOWED_SUBTYPE(subtype
))
3812 (body
->type
== TYPEMULTIPART
&& body
->nested
.part
3813 && (new_body
= &body
->nested
.part
->body
) != NULL
3814 && body
->nested
.part
->body
.type
== TYPETEXT
3815 && (subtype
=body
->nested
.part
->body
.subtype
)
3816 && ALLOWED_SUBTYPE(subtype
)))
3821 gf_set_so_writec(&pc
, so
);
3822 success
= get_body_part_text(idata
->stream
, new_body
? new_body
: body
,
3824 partno
, partial_fetch_len
, pc
,
3826 GBPT_NOINTR
| GBPT_PEEK
|
3827 (delete_quotes
? GBPT_DELQUOTES
: 0));
3828 gf_clear_so_writec(so
);
3833 while(p
-buf
< sizeof(buf
)-1 && so_readc(&c
, so
)){
3834 /* delete leading whitespace */
3835 if(p
== buf
&& isspace(c
))
3837 /* and include just one space per run of whitespace */
3838 else if(isspace(c
)){
3839 if(!one_space_done
){
3857 firsttext
= fs_get((l
+1) * sizeof(char));
3858 firsttext
[0] = '\0';
3859 iutf8ncpy(firsttext
, buf
, l
);
3860 firsttext
[l
] = '\0';
3861 removing_trailing_white_space(firsttext
);
3867 /* first if means we didn't fetch all of the data */
3868 if(!(success
> 1 && success
< partial_fetch_len
)){
3869 if(partial_fetch_len
< 4096L
3870 && (!firsttext
|| utf8_width(firsttext
) < 50)){
3872 fs_give((void **) &firsttext
);
3875 mail_free_searchset(&ss
);
3877 partial_fetch_len
= 4096L;
3882 pelt
->firsttext
= firsttext
;
3889 mail_free_searchset(&ss
);
3896 * fetch_date - called to get at the index entry's "Date:" field
3899 fetch_date(INDEXDATA_S
*idata
)
3901 if(idata
->no_fetch
) /* implies date is valid */
3902 return(idata
->date
);
3903 else if(idata
->bogus
)
3908 /* c-client call's just cache access at this point */
3909 if((env
= pine_mail_fetchenvelope(idata
->stream
, idata
->rawno
)) != NULL
)
3910 return((char *) env
->date
);
3920 * fetch_header - called to get at the index entry's "Hdrname:" field
3923 fetch_header(INDEXDATA_S
*idata
, char *hdrname
)
3927 else if(idata
->bogus
)
3930 char *h
, *p
, *q
, *decoded
, *fields
[2];
3931 size_t retsize
, decsize
;
3933 unsigned char *decode_buf
= NULL
;
3935 fields
[0] = hdrname
;
3937 if(hdrname
&& hdrname
[0]
3938 && (h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
3941 if(strlen(h
) < strlen(hdrname
) + 1){
3942 fs_give((void **) &h
);
3946 /* skip "hdrname:" */
3947 for(p
= h
+ strlen(hdrname
) + 1;
3948 *p
&& isspace((unsigned char)*p
); p
++)
3951 decsize
= (4 * strlen(p
)) + 1;
3952 decode_buf
= (unsigned char *) fs_get(decsize
* sizeof(unsigned char));
3953 decoded
= (char *) rfc1522_decode_to_utf8(decode_buf
, decsize
, p
);
3956 retsize
= strlen(decoded
);
3957 q
= ret
= (char *) fs_get((retsize
+1) * sizeof(char));
3960 while(q
-ret
< retsize
&& *p
){
3961 if(*p
== '\015' || *p
== '\012')
3963 else if(*p
== '\t'){
3973 fs_give((void **) &h
);
3975 fs_give((void **) &decode_buf
);
3988 * fetch_size - called to get at the index entry's "size" field
3991 fetch_size(INDEXDATA_S
*idata
)
3993 if(idata
->no_fetch
) /* implies size is valid */
3994 return(idata
->size
);
3995 else if(idata
->bogus
)
4000 if(idata
->stream
&& idata
->rawno
> 0L
4001 && idata
->rawno
<= idata
->stream
->nmsgs
4002 && (mc
= mail_elt(idata
->stream
, idata
->rawno
)))
4003 return(mc
->rfc822_size
);
4013 * fetch_body - called to get a the index entry's body structure
4016 fetch_body(INDEXDATA_S
*idata
)
4020 if(idata
->bogus
|| idata
->no_fetch
){
4025 if(pine_mail_fetchstructure(idata
->stream
, idata
->rawno
, &body
))
4034 * s is at least size width+1
4037 set_index_addr(INDEXDATA_S
*idata
,
4039 struct mail_address
*addr
,
4045 char *p
, *stmp
= NULL
, *sptr
;
4046 char *save_personal
= NULL
;
4051 for(atmp
= addr
; idata
->stream
&& atmp
; atmp
= atmp
->next
)
4052 if(atmp
->host
&& atmp
->host
[0] == '.'){
4053 char *pref
, *h
, *fields
[2];
4055 if(idata
->no_fetch
){
4062 if((h
= pine_fetchheader_lines(idata
->stream
, idata
->rawno
,
4063 NULL
, fields
)) != NULL
){
4064 if(strlen(h
) < strlen(field
) + 1){
4069 for(p
= h
+ strlen(field
) + 1;
4070 *p
&& isspace((unsigned char)*p
); p
++)
4075 sptr
= stmp
= (char *) fs_get((orig_width
+1) * sizeof(char));
4078 for(pref
= prefix
; pref
&& *pref
; pref
++)
4087 if(*p
== '\015' || *p
== '\012')
4088 p
++; /* skip CR LF */
4091 else if(*p
== '\t'){
4098 *sptr
= '\0'; /* tie off return string */
4101 iutf8ncpy(s
, stmp
, orig_width
+1);
4102 s
[orig_width
] = '\0';
4103 fs_give((void **) &stmp
);
4106 fs_give((void **) &h
);
4109 /* else fall thru and display what c-client gave us */
4112 if(addr
&& !addr
->next
/* only one address */
4113 && addr
->host
/* not group syntax */
4114 && addr
->personal
&& addr
->personal
[0]){ /* there is a personal name */
4115 char buftmp
[MAILTMPLEN
];
4118 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4119 strncpy(s
, prefix
, width
+1);
4121 snprintf(buftmp
, sizeof(buftmp
), "%s", addr
->personal
);
4122 p
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
4123 SIZEOF_20KBUF
, buftmp
);
4124 removing_leading_and_trailing_white_space(p
);
4126 iutf8ncpy(s
+ l
, p
, width
- l
);
4133 save_personal
= addr
->personal
;
4134 addr
->personal
= NULL
;
4142 a_string
= addr_list_string(addr
, NULL
, 0);
4144 addr
->personal
= save_personal
;
4146 if((l
= prefix
? strlen(prefix
) : 0) != 0)
4147 strncpy(s
, prefix
, width
+1);
4149 iutf8ncpy(s
+ l
, a_string
, width
- l
);
4152 fs_give((void **)&a_string
);
4158 addr
->personal
= save_personal
;
4165 index_data_env(INDEXDATA_S
*idata
, ENVELOPE
*env
)
4172 idata
->from
= env
->from
;
4173 idata
->to
= env
->to
;
4174 idata
->cc
= env
->cc
;
4175 idata
->sender
= env
->sender
;
4176 idata
->subject
= env
->subject
;
4177 idata
->date
= (char *) env
->date
;
4178 idata
->newsgroups
= env
->newsgroups
;
4180 idata
->valid_to
= 1; /* signal that everything is here */
4181 idata
->valid_cc
= 1;
4182 idata
->valid_sender
= 1;
4183 idata
->valid_news
= 1;
4188 * Put a string representing the date into str. The source date is
4189 * in the string datesrc. The format to be used is in type.
4190 * Notice that type is an IndexColType, but really only a subset of
4191 * IndexColType types are allowed.
4193 * Args datesrc -- The source date string
4194 * type -- What type of output we want
4195 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4196 * str -- Put the answer here.
4197 * str_len -- Length of str
4198 * monabb_width -- This is a hack to get dates to line up right. For
4199 * example, in French (but without accents here)
4203 * For this monabb_width would be 5.
4206 date_str(char *datesrc
, IndexColType type
, int v
, char *str
, size_t str_len
,
4209 char year4
[5], /* 4 digit year */
4210 yearzero
[3], /* zero padded, 2-digit year */
4211 monzero
[3], /* zero padded, 2-digit month */
4212 mon
[3], /* 1 or 2-digit month, no pad */
4213 dayzero
[3], /* zero padded, 2-digit day */
4214 day
[3], /* 1 or 2-digit day, no pad */
4215 dayord
[3], /* 2-letter ordinal label */
4216 monabb
[10], /* 3-letter month abbrev */
4217 /* actually maybe not 3 if localized */
4218 hour24
[3], /* 2-digit, 24 hour clock hour */
4219 hour12
[3], /* 12 hour clock hour, no pad */
4220 minzero
[3], /* zero padded, 2-digit minutes */
4221 timezone
[6]; /* timezone, like -0800 or +... */
4223 int curtype
, lastmonthtype
, lastyeartype
, preftype
;
4224 int sdatetimetype
, sdatetime24type
;
4226 #define TODAYSTR N_("Today")
4228 curtype
= (type
== iCurDate
||
4229 type
== iCurDateIso
||
4230 type
== iCurDateIsoS
||
4231 type
== iCurPrefDate
||
4232 type
== iCurPrefDateTime
||
4233 type
== iCurPrefTime
||
4234 type
== iCurTime24
||
4235 type
== iCurTime12
||
4237 type
== iCurDay2Digit
||
4238 type
== iCurDayOfWeek
||
4239 type
== iCurDayOfWeekAbb
||
4241 type
== iCurMon2Digit
||
4242 type
== iCurMonLong
||
4243 type
== iCurMonAbb
||
4245 type
== iCurYear2Digit
);
4246 lastmonthtype
= (type
== iLstMon
||
4247 type
== iLstMon2Digit
||
4248 type
== iLstMonLong
||
4249 type
== iLstMonAbb
||
4250 type
== iLstMonYear
||
4251 type
== iLstMonYear2Digit
);
4252 lastyeartype
= (type
== iLstYear
||
4253 type
== iLstYear2Digit
);
4254 sdatetimetype
= (type
== iSDateTime
||
4255 type
== iSDateTimeIso
||
4256 type
== iSDateTimeIsoS
||
4257 type
== iSDateTimeS1
||
4258 type
== iSDateTimeS2
||
4259 type
== iSDateTimeS3
||
4260 type
== iSDateTimeS4
||
4261 type
== iSDateTime24
||
4262 type
== iSDateTimeIso24
||
4263 type
== iSDateTimeIsoS24
||
4264 type
== iSDateTimeS124
||
4265 type
== iSDateTimeS224
||
4266 type
== iSDateTimeS324
||
4267 type
== iSDateTimeS424
);
4268 sdatetime24type
= (type
== iSDateTime24
||
4269 type
== iSDateTimeIso24
||
4270 type
== iSDateTimeIsoS24
||
4271 type
== iSDateTimeS124
||
4272 type
== iSDateTimeS224
||
4273 type
== iSDateTimeS324
||
4274 type
== iSDateTimeS424
);
4275 preftype
= (type
== iPrefDate
||
4276 type
== iPrefDateTime
||
4277 type
== iPrefTime
||
4278 type
== iCurPrefDate
||
4279 type
== iCurPrefDateTime
||
4280 type
== iCurPrefTime
);
4284 if(!(datesrc
&& datesrc
[0]) && !(curtype
|| lastmonthtype
|| lastyeartype
))
4287 if(curtype
|| lastmonthtype
|| lastyeartype
){
4291 parse_date(dbuf
, &d
);
4295 else if(lastmonthtype
){
4304 parse_date(F_ON(F_DATES_TO_LOCAL
,ps_global
)
4305 ? convert_date_to_local(datesrc
) : datesrc
, &d
);
4306 if(d
.year
== -1 || d
.month
== -1 || d
.day
== -1){
4308 sdatetime24type
= 0;
4311 case iSDate
: case iSDateTime
: case iSDateTime24
:
4315 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4316 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
4320 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4324 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4328 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4332 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4336 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4346 /* some special ones to start with */
4348 struct tm tm
, *tmptr
= NULL
;
4352 * Make sure we get the right one if we're using current time.
4355 now
= time((time_t *) 0);
4356 if(now
!= (time_t) -1)
4357 tmptr
= localtime(&now
);
4361 memset(&tm
, 0, sizeof(tm
));
4362 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4363 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4364 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4365 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4366 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4367 tm
.tm_wday
= MIN(MAX(d
.wkday
, 0), 6);
4374 our_strftime(str
, str_len
, "%x", tmptr
);
4378 our_strftime(str
, str_len
, "%X", tmptr
);
4381 case iCurPrefDateTime
:
4382 our_strftime(str
, str_len
, "%c", tmptr
);
4392 strncpy(monabb
, (d
.month
> 0 && d
.month
< 13)
4393 ? month_abbrev_locale(d
.month
) : "", sizeof(monabb
));
4394 monabb
[sizeof(monabb
)-1] = '\0';
4396 strncpy(mon
, (d
.month
> 0 && d
.month
< 13)
4397 ? int2string(d
.month
) : "", sizeof(mon
));
4398 mon
[sizeof(mon
)-1] = '\0';
4400 strncpy(day
, (d
.day
> 0 && d
.day
< 32)
4401 ? int2string(d
.day
) : "", sizeof(day
));
4402 day
[sizeof(day
)-1] = '\0';
4405 (d
.day
<= 0 || d
.day
> 31) ? "" :
4406 (d
.day
== 1 || d
.day
== 21 || d
.day
== 31) ? "st" :
4407 (d
.day
== 2 || d
.day
== 22 ) ? "nd" :
4408 (d
.day
== 3 || d
.day
== 23 ) ? "rd" : "th", sizeof(dayord
));
4410 dayord
[sizeof(dayord
)-1] = '\0';
4412 strncpy(year4
, (d
.year
>= 1000 && d
.year
< 10000)
4413 ? int2string(d
.year
) : "????", sizeof(year4
));
4414 year4
[sizeof(year4
)-1] = '\0';
4417 if((d
.year
% 100) < 10){
4419 strncpy(yearzero
+1, int2string(d
.year
% 100), sizeof(yearzero
)-1);
4422 strncpy(yearzero
, int2string(d
.year
% 100), sizeof(yearzero
));
4425 strncpy(yearzero
, "??", sizeof(yearzero
));
4427 yearzero
[sizeof(yearzero
)-1] = '\0';
4429 if(d
.month
> 0 && d
.month
< 10){
4431 strncpy(monzero
+1, int2string(d
.month
), sizeof(monzero
)-1);
4433 else if(d
.month
>= 10 && d
.month
<= 12)
4434 strncpy(monzero
, int2string(d
.month
), sizeof(monzero
));
4436 strncpy(monzero
, "??", sizeof(monzero
));
4438 monzero
[sizeof(monzero
)-1] = '\0';
4440 if(d
.day
> 0 && d
.day
< 10){
4442 strncpy(dayzero
+1, int2string(d
.day
), sizeof(dayzero
)-1);
4444 else if(d
.day
>= 10 && d
.day
<= 31)
4445 strncpy(dayzero
, int2string(d
.day
), sizeof(dayzero
));
4447 strncpy(dayzero
, "??", sizeof(dayzero
));
4449 dayzero
[sizeof(dayzero
)-1] = '\0';
4451 hr12
= (d
.hour
== 0) ? 12 :
4452 (d
.hour
> 12) ? (d
.hour
- 12) : d
.hour
;
4454 if(hr12
> 0 && hr12
<= 12)
4455 strncpy(hour12
, int2string(hr12
), sizeof(hour12
));
4457 hour12
[sizeof(hour12
)-1] = '\0';
4460 if(d
.hour
>= 0 && d
.hour
< 10){
4462 strncpy(hour24
+1, int2string(d
.hour
), sizeof(hour24
)-1);
4464 else if(d
.hour
>= 10 && d
.hour
< 24)
4465 strncpy(hour24
, int2string(d
.hour
), sizeof(hour24
));
4467 hour24
[sizeof(hour24
)-1] = '\0';
4470 if(d
.minute
>= 0 && d
.minute
< 10){
4472 strncpy(minzero
+1, int2string(d
.minute
), sizeof(minzero
)-1);
4474 else if(d
.minute
>= 10 && d
.minute
<= 60)
4475 strncpy(minzero
, int2string(d
.minute
), sizeof(minzero
));
4477 minzero
[sizeof(minzero
)-1] = '\0';
4479 if(sizeof(timezone
) > 5){
4480 if(d
.hours_off_gmt
<= 0){
4482 d
.hours_off_gmt
*= -1;
4483 d
.min_off_gmt
*= -1;
4489 if(d
.hours_off_gmt
>= 0 && d
.hours_off_gmt
< 10){
4491 strncpy(timezone
+2, int2string(d
.hours_off_gmt
), sizeof(timezone
)-2);
4493 else if(d
.hours_off_gmt
>= 10 && d
.hours_off_gmt
< 24)
4494 strncpy(timezone
+1, int2string(d
.hours_off_gmt
), sizeof(timezone
)-1);
4501 if(d
.min_off_gmt
>= 0 && d
.min_off_gmt
< 10){
4503 strncpy(timezone
+4, int2string(d
.min_off_gmt
), sizeof(timezone
)-4);
4505 else if(d
.min_off_gmt
>= 10 && d
.min_off_gmt
<= 60)
4506 strncpy(timezone
+3, int2string(d
.min_off_gmt
), sizeof(timezone
)-3);
4513 timezone
[sizeof(timezone
)-1] = '\0';
4518 /* this one is not locale-specific */
4519 snprintf(str
, str_len
, "%s%s%s %s %s",
4520 (d
.wkday
!= -1) ? day_abbrev(d
.wkday
) : "",
4521 (d
.wkday
!= -1) ? ", " : "",
4523 (d
.month
> 0 && d
.month
< 13) ? month_abbrev(d
.month
) : "",
4527 case iCurDayOfWeekAbb
:
4528 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_abbrev_locale(d
.wkday
) : "", str_len
);
4529 str
[str_len
-1] = '\0';
4533 strncpy(str
, (d
.wkday
>= 0 && d
.wkday
<= 6) ? day_name_locale(d
.wkday
) : "", str_len
);
4534 str
[str_len
-1] = '\0';
4540 strncpy(str
, year4
, str_len
);
4544 strncpy(str
, dayzero
, str_len
);
4549 strncpy(str
, monzero
, str_len
);
4552 case iCurYear2Digit
:
4553 case iLstYear2Digit
:
4554 case iLstMonYear2Digit
:
4555 strncpy(str
, yearzero
, str_len
);
4558 strncpy(str
, timezone
, str_len
);
4562 strncpy(str
, day
, str_len
);
4565 snprintf(str
, str_len
, "%s%s", day
, dayord
);
4570 if(d
.month
> 0 && d
.month
<= 12)
4571 strncpy(str
, int2string(d
.month
), str_len
);
4577 strncpy(str
, monabb
, str_len
);
4582 strncpy(str
, (d
.month
> 0 && d
.month
< 13)
4583 ? month_name_locale(d
.month
) : "", str_len
);
4588 snprintf(str
, str_len
, "%s%s%s", monabb
, (monabb
[0] && day
[0]) ? " " : "", day
);
4590 if(monabb_width
> 0)
4591 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4592 monabb_width
, monabb_width
, monabb
, day
);
4594 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4600 snprintf(str
, str_len
, "%s%s%s%s%s", monabb
,
4601 (monabb
[0] && day
[0]) ? " " : "", day
,
4602 ((monabb
[0] || day
[0]) && year4
[0]) ? ", " : "",
4605 if(monabb_width
> 0)
4606 utf8_snprintf(str
, str_len
, "%-*.*w %2s%c %4s",
4607 monabb_width
, monabb_width
,
4609 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ', year4
);
4611 snprintf(str
, str_len
, "%s %2s%c %4s", monabb
, day
,
4612 (monabb
[0] && day
[0] && year4
[0]) ? ',' : ' ',
4625 if(monzero
[0] == '?' && dayzero
[0] == '?' &&
4627 snprintf(str
, str_len
, "%8s", "");
4631 snprintf(str
, str_len
, "%2s/%2s/%2s",
4632 monzero
, dayzero
, yearzero
);
4635 snprintf(str
, str_len
, "%2s/%2s/%2s",
4636 dayzero
, monzero
, yearzero
);
4639 snprintf(str
, str_len
, "%2s.%2s.%2s",
4640 dayzero
, monzero
, yearzero
);
4643 snprintf(str
, str_len
, "%2s.%2s.%2s",
4644 yearzero
, monzero
, dayzero
);
4648 snprintf(str
, str_len
, "%2s-%2s-%2s",
4649 yearzero
, monzero
, dayzero
);
4653 snprintf(str
, str_len
, "%4s-%2s-%2s",
4654 year4
, monzero
, dayzero
);
4664 snprintf(str
, str_len
, "%2s%c%2s",
4665 (hour24
[0] && minzero
[0]) ? hour24
: "",
4666 (hour24
[0] && minzero
[0]) ? ':' : ' ',
4667 (hour24
[0] && minzero
[0]) ? minzero
: "");
4671 snprintf(str
, str_len
, "%s%c%2s%s",
4672 (hour12
[0] && minzero
[0]) ? hour12
: "",
4673 (hour12
[0] && minzero
[0]) ? ':' : ' ',
4674 (hour12
[0] && minzero
[0]) ? minzero
: "",
4675 (hour12
[0] && minzero
[0] && d
.hour
< 12) ? "am" :
4676 (hour12
[0] && minzero
[0] && d
.hour
>= 12) ? "pm" :
4679 case iSDate
: case iSDateIso
: case iSDateIsoS
:
4680 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
4681 case iSDateTime
: case iSDateTimeIso
: case iSDateTimeIsoS
:
4682 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
4683 case iSDateTime24
: case iSDateTimeIso24
: case iSDateTimeIsoS24
:
4684 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
4685 { struct date now
, last_day
;
4687 int msg_day_of_year
, now_day_of_year
, today
;
4688 int diff
, ydiff
, last_day_of_year
;
4691 parse_date(dbuf
, &now
);
4692 today
= day_of_week(&now
) + 7;
4694 if(today
>= 0+7 && today
<= 6+7){
4695 now_day_of_year
= day_of_year(&now
);
4696 msg_day_of_year
= day_of_year(&d
);
4697 ydiff
= now
.year
- d
.year
;
4699 if(msg_day_of_year
== -1)
4702 diff
= now_day_of_year
- msg_day_of_year
;
4703 else if(ydiff
== 1){
4705 last_day
.month
= 12;
4707 last_day_of_year
= day_of_year(&last_day
);
4709 diff
= now_day_of_year
+
4710 (last_day_of_year
- msg_day_of_year
);
4712 else if(ydiff
== -1){
4714 last_day
.month
= 12;
4716 last_day_of_year
= day_of_year(&last_day
);
4718 diff
= -1 * (msg_day_of_year
+
4719 (last_day_of_year
- now_day_of_year
));
4727 strncpy(str
, _(TODAYSTR
), str_len
);
4729 strncpy(str
, _("Yesterday"), str_len
);
4730 else if(diff
> 1 && diff
< 7)
4731 snprintf(str
, str_len
, "%s", day_name_locale((today
- diff
) % 7));
4733 strncpy(str
, _("Tomorrow"), str_len
);
4734 else if(diff
< -1 && diff
> -7)
4735 snprintf(str
, str_len
, _("Next %.3s!"),
4736 day_name_locale((today
- diff
) % 7));
4739 || (ydiff
== 1 && 12 + now
.month
- d
.month
< 6))){
4741 snprintf(str
, str_len
, "%s%s%s", monabb
,
4742 (monabb
[0] && day
[0]) ? " " : "", day
);
4744 if(monabb_width
> 0)
4745 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4746 monabb_width
, monabb_width
, monabb
, day
);
4748 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4752 if(msg_day_of_year
== -1 && (type
== iSDate
|| type
== iSDateTime
))
4753 type
= iSDateTimeIsoS
;
4756 case iSDate
: case iSDateTime
: case iSDateTime24
:
4760 memset(&tm
, 0, sizeof(tm
));
4761 tm
.tm_year
= MIN(MAX(d
.year
-1900, 0), 2000);
4762 tm
.tm_mon
= MIN(MAX(d
.month
-1, 0), 11);
4763 tm
.tm_mday
= MIN(MAX(d
.day
, 1), 31);
4764 tm
.tm_hour
= MIN(MAX(d
.hour
, 0), 23);
4765 tm
.tm_min
= MIN(MAX(d
.minute
, 0), 59);
4766 our_strftime(str
, str_len
, "%x", &tm
);
4770 case iSDateS1
: case iSDateTimeS1
: case iSDateTimeS124
:
4772 snprintf(str
, str_len
, "%s/%s/%s%s", mon
, day
, yearzero
,
4773 diff
< 0 ? "!" : "");
4775 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4776 (mon
[0] && mon
[1]) ? "" : " ",
4777 mon
, dayzero
, yearzero
,
4778 diff
< 0 ? "!" : "");
4780 case iSDateS2
: case iSDateTimeS2
: case iSDateTimeS224
:
4782 snprintf(str
, str_len
, "%s/%s/%s%s", day
, mon
, yearzero
,
4783 diff
< 0 ? "!" : "");
4785 snprintf(str
, str_len
, "%s%s/%s/%s%s",
4786 (day
[0] && day
[1]) ? "" : " ",
4787 day
, monzero
, yearzero
,
4788 diff
< 0 ? "!" : "");
4790 case iSDateS3
: case iSDateTimeS3
: case iSDateTimeS324
:
4792 snprintf(str
, str_len
, "%s.%s.%s%s", day
, mon
, yearzero
,
4793 diff
< 0 ? "!" : "");
4795 snprintf(str
, str_len
, "%s%s.%s.%s%s",
4796 (day
[0] && day
[1]) ? "" : " ",
4797 day
, monzero
, yearzero
,
4798 diff
< 0 ? "!" : "");
4800 case iSDateS4
: case iSDateTimeS4
: case iSDateTimeS424
:
4802 snprintf(str
, str_len
, "%s.%s.%s%s",
4803 yearzero
, monzero
, dayzero
,
4804 diff
< 0 ? "!" : "");
4806 snprintf(str
, str_len
, "%s.%s.%s%s",
4807 yearzero
, monzero
, dayzero
,
4808 diff
< 0 ? "!" : "");
4810 case iSDateIsoS
: case iSDateTimeIsoS
: case iSDateTimeIsoS24
:
4811 snprintf(str
, str_len
, "%2s-%2s-%2s",
4812 yearzero
, monzero
, dayzero
);
4814 case iSDateIso
: case iSDateTimeIso
: case iSDateTimeIso24
:
4815 snprintf(str
, str_len
, "%4s-%2s-%2s",
4816 year4
, monzero
, dayzero
);
4826 snprintf(str
, str_len
, "%s%s%s", monabb
,
4827 (monabb
[0] && day
[0]) ? " " : "", day
);
4829 if(monabb_width
> 0)
4830 utf8_snprintf(str
, str_len
, "%-*.*w %2s",
4831 monabb_width
, monabb_width
, monabb
, day
);
4833 snprintf(str
, str_len
, "%s %2s", monabb
, day
);
4844 str
[str_len
-1] = '\0';
4846 if(type
== iSTime
||
4847 (sdatetimetype
&& !strcmp(str
, _(TODAYSTR
)))){
4848 struct date now
, last_day
;
4849 char dbuf
[200], *Ddd
, *ampm
;
4854 parse_date(dbuf
, &now
);
4856 /* Figure out if message date lands in the past week */
4858 /* (if message dated this month or last month...) */
4859 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 1) ||
4860 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
== 1)){
4862 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4865 * If msg in end of last year (and we're in first bit of "this"
4866 * year), diff will be backwards; fix up by adding number of days
4867 * in last year (usually 365, but occasionally 366)...
4869 if(d
.year
== now
.year
- 1){
4871 last_day
.month
= 12;
4874 daydiff
+= day_of_year(&last_day
);
4878 daydiff
= -100; /* comfortably out of range (of past week) */
4880 /* Build 2-digit hour and am/pm indicator, used below */
4882 if(d
.hour
>= 0 && d
.hour
< 24){
4883 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4884 ampm
= (d
.hour
< 12) ? "am" : "pm";
4885 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4888 strncpy(hour12
, "??", sizeof(hour12
));
4889 hour12
[sizeof(hour12
)-1] = '\0';
4891 strncpy(hour24
, "??", sizeof(hour24
));
4892 hour24
[sizeof(hour24
)-1] = '\0';
4895 /* Build date/time in str, in format similar to that used by w(1) */
4897 if(daydiff
== 0){ /* If date is today, "HH:MMap" */
4898 if(d
.minute
>= 0 && d
.minute
< 60)
4899 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
4901 strncpy(minzero
, "??", sizeof(minzero
));
4902 minzero
[sizeof(minzero
)-1] = '\0';
4905 snprintf(str
, str_len
, "%s:%s%s", sdatetime24type
? hour24
: hour12
,
4906 minzero
, sdatetime24type
? "" : ampm
);
4908 else if(daydiff
>= 1 && daydiff
< 6){ /* If <1wk ago, "DddHHap" */
4910 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
4911 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
4912 Ddd
= day_abbrev_locale(day_of_week(&d
));
4916 snprintf(str
, str_len
, "%s%s%s", Ddd
, hour12
, ampm
);
4918 else{ /* date is old or future, "ddMmmyy" */
4919 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
4920 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
4921 monabb
[sizeof(monabb
)-1] = '\0';
4923 if(d
.day
>= 1 && d
.day
<= 31)
4924 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
4926 strncpy(dayzero
, "??", sizeof(dayzero
));
4927 dayzero
[sizeof(dayzero
)-1] = '\0';
4930 if(d
.year
>= 0 && d
.year
<= 9999)
4931 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
4933 strncpy(yearzero
, "??", sizeof(yearzero
));
4934 yearzero
[sizeof(yearzero
)-1] = '\0';
4937 snprintf(str
, str_len
, "%s%s%s", dayzero
, monabb
, yearzero
);
4940 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4942 memmove(str
, str
+ 1, strlen(str
));
4948 /* This is like iSTime, but the format is different */
4949 if(type
== iSTime24
){
4950 struct date now
, last_day
;
4951 char dbuf
[200], *Ddd
, *ampm
;
4956 parse_date(dbuf
, &now
);
4958 /* (if message dated this month or last month...) */
4959 if((d
.year
== now
.year
&& d
.month
>= now
.month
- 6) ||
4960 (d
.year
== now
.year
- 1 && d
.month
== 12 && now
.month
<= 6)){
4962 daydiff
= day_of_year(&now
) - day_of_year(&d
);
4965 * If msg in end of last year (and we're in first bit of "this"
4966 * year), diff will be backwards; fix up by adding number of days
4967 * in last year (usually 365, but occasionally 366)...
4969 if(d
.year
== now
.year
- 1){
4971 last_day
.month
= 12;
4974 daydiff
+= day_of_year(&last_day
);
4978 daydiff
= 181; /* comfortably out of range (of past week) */
4980 /* Build 2-digit hour and am/pm indicator, used below */
4982 if(d
.hour
>= 0 && d
.hour
< 24){
4983 snprintf(hour12
, sizeof(hour12
), "%02d", (d
.hour
% 12 == 0) ? 12 : d
.hour
% 12);
4984 ampm
= (d
.hour
< 12) ? "am" : "pm";
4985 snprintf(hour24
, sizeof(hour24
), "%02d", d
.hour
);
4988 strncpy(hour12
, "??", sizeof(hour12
));
4989 hour12
[sizeof(hour12
)-1] = '\0';
4991 strncpy(hour24
, "??", sizeof(hour24
));
4992 hour24
[sizeof(hour24
)-1] = '\0';
4995 /* Build date/time in str, in format similar to that used by w(1) */
4997 if(daydiff
>= 0 && daydiff
<= 6){ /* If <1wk ago, "Ddd HH:mm" */
4999 if(d
.month
>= 1 && d
.day
>= 1 && d
.year
>= 0 &&
5000 d
.month
<= 12 && d
.day
<= 31 && d
.year
<= 9999)
5001 Ddd
= day_abbrev_locale(day_of_week(&d
));
5005 if(d
.minute
>= 0 && d
.minute
< 60)
5006 snprintf(minzero
, sizeof(minzero
), "%02d", d
.minute
);
5008 strncpy(minzero
, "??", sizeof(minzero
));
5009 minzero
[sizeof(minzero
)-1] = '\0';
5012 snprintf(str
, str_len
, "%s %s:%s", Ddd
, hour24
, minzero
);
5014 else if(daydiff
< 180){ /* date is "Mmm dd" */
5015 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
5016 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
5017 monabb
[sizeof(monabb
)-1] = '\0';
5019 if(d
.day
>= 1 && d
.day
<= 31)
5020 snprintf(dayzero
, sizeof(dayzero
), "%*d", 2, d
.day
);
5022 strncpy(dayzero
, "??", sizeof(dayzero
));
5023 dayzero
[sizeof(dayzero
)-1] = '\0';
5026 snprintf(str
, str_len
, "%s %s", monabb
, dayzero
);
5028 else { /* date is old or future, "dd/Mmm/yy" */
5029 strncpy(monabb
, (d
.month
>= 1 && d
.month
<= 12)
5030 ? month_abbrev_locale(d
.month
) : "???", sizeof(monabb
));
5031 monabb
[sizeof(monabb
)-1] = '\0';
5033 if(d
.day
>= 1 && d
.day
<= 31)
5034 snprintf(dayzero
, sizeof(dayzero
), "%02d", d
.day
);
5036 strncpy(dayzero
, "??", sizeof(dayzero
));
5037 dayzero
[sizeof(dayzero
)-1] = '\0';
5040 if(d
.year
>= 0 && d
.year
<= 9999)
5041 snprintf(yearzero
, sizeof(yearzero
), "%02d", d
.year
% 100);
5043 strncpy(yearzero
, "??", sizeof(yearzero
));
5044 yearzero
[sizeof(yearzero
)-1] = '\0';
5047 snprintf(str
, str_len
, "%s/%s/%s", dayzero
, monabb
, yearzero
);
5050 if(str
[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
5052 memmove(str
, str
+ 1, strlen(str
));
5061 * Format a string representing the keywords into ice.
5063 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
5065 * Args idata -- which message?
5066 * kwtype -- keywords or kw initials
5067 * ice -- index cache entry for message
5070 key_str(INDEXDATA_S
*idata
, SubjKW kwtype
, ICE_S
*ice
)
5075 COLOR_PAIR
*color
= NULL
;
5076 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5077 IELEM_S
*ielem
= NULL
;
5078 IFIELD_S
*ourifield
= NULL
;
5080 if(ice
&& ice
->ifield
){
5081 /* move to last ifield, the one we're working */
5082 for(ourifield
= ice
->ifield
;
5083 ourifield
&& ourifield
->next
;
5084 ourifield
= ourifield
->next
)
5091 if(kwtype
== KWInit
){
5092 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5093 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
5094 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
5095 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
5098 * Pick off the first initial. Since word is UTF-8 it may
5099 * take more than one byte for the first initial.
5102 if(word
&& word
[0]){
5104 unsigned long remaining_octets
;
5105 unsigned char *inputp
;
5107 remaining_octets
= strlen(word
);
5108 inputp
= (unsigned char *) word
;
5109 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
5110 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
5111 ielem
= new_ielem(&ourifield
->ielem
);
5112 ielem
->freedata
= 1;
5113 ielem
->datalen
= (unsigned) (inputp
- (unsigned char *) word
);
5114 ielem
->data
= (char *) fs_get((ielem
->datalen
+ 1) * sizeof(char));
5115 strncpy(ielem
->data
, word
, ielem
->datalen
);
5116 ielem
->data
[ielem
->datalen
] = '\0';
5118 if(pico_usingcolor()
5119 && ((kw
->nick
&& kw
->nick
[0]
5120 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5121 || (kw
->kw
&& kw
->kw
[0]
5122 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5123 ielem
->color
= color
;
5130 free_color_pair(&color
);
5134 else if(kwtype
== KW
){
5135 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
5136 if(user_flag_is_set(idata
->stream
, idata
->rawno
, kw
->kw
)){
5139 ielem
= new_ielem(&ourifield
->ielem
);
5140 ielem
->freedata
= 1;
5141 ielem
->data
= cpystr(" ");
5147 word
= (kw
->nick
&& kw
->nick
[0]) ? kw
->nick
:
5148 (kw
->kw
&& kw
->kw
[0]) ? kw
->kw
: "";
5151 ielem
= new_ielem(&ourifield
->ielem
);
5152 ielem
->freedata
= 1;
5153 ielem
->data
= cpystr(word
);
5154 ielem
->datalen
= strlen(word
);
5156 if(pico_usingcolor()
5157 && ((kw
->nick
&& kw
->nick
[0]
5158 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
5159 || (kw
->kw
&& kw
->kw
[0]
5160 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
5161 ielem
->color
= color
;
5167 free_color_pair(&color
);
5173 * If we're coloring some of the fields then add a dummy field
5174 * at the end that can soak up the rest of the space after the last
5175 * colored keyword. Otherwise, the last one's color will extend to
5176 * the end of the field.
5178 if(pico_usingcolor()){
5179 ielem
= new_ielem(&ourifield
->ielem
);
5180 ielem
->freedata
= 1;
5181 ielem
->data
= cpystr(" ");
5185 ourifield
->leftadj
= 1;
5186 set_ielem_widths_in_field(ourifield
);
5191 prio_str(INDEXDATA_S
*idata
, IndexColType ctype
, ICE_S
*ice
)
5193 IFIELD_S
*ourifield
= NULL
;
5194 IELEM_S
*ielem
= NULL
;
5199 if(ice
&& ice
->ifield
){
5200 /* move to last ifield, the one we're working */
5201 for(ourifield
= ice
->ifield
;
5202 ourifield
&& ourifield
->next
;
5203 ourifield
= ourifield
->next
)
5210 hdrval
= fetch_header(idata
, PRIORITYNAME
);
5212 if(hdrval
&& hdrval
[0] && isdigit(hdrval
[0])){
5214 if(v
>= 1 && v
<= 5 && v
!= 3){
5216 ielem
= new_ielem(&ourifield
->ielem
);
5217 ielem
->freedata
= 1;
5221 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5222 ielem
->data
[0] = hdrval
[0];
5223 ielem
->data
[1] = '\0';
5227 for(p
= priorities
; p
&& p
->desc
; p
++)
5232 ielem
->data
= cpystr(p
->desc
);
5237 ielem
->data
= (char *) fs_get(2 * sizeof(char));
5238 ielem
->data
[0] = '\0';
5241 ielem
->data
[0] = '!';
5246 * We could put a Unicode downarrow in here but
5247 * we have no way of knowing if the user's font
5248 * will have it (I think).
5250 ielem
->data
[0] = 'v';
5254 ielem
->data
[1] = '\0';
5259 alpine_panic("Unhandled case in prio_str");
5264 ielem
->data
= cpystr("");
5266 if(ielem
&& ielem
->data
)
5267 ielem
->datalen
= strlen(ielem
->data
);
5269 if((v
== 1 || v
== 2) && pico_usingcolor()
5270 && ps_global
->VAR_IND_HIPRI_FORE_COLOR
5271 && ps_global
->VAR_IND_HIPRI_BACK_COLOR
){
5272 ielem
->type
= eTypeCol
;
5273 ielem
->freecolor
= 1;
5274 ielem
->color
= new_color_pair(ps_global
->VAR_IND_HIPRI_FORE_COLOR
, ps_global
->VAR_IND_HIPRI_BACK_COLOR
);
5276 else if((v
== 4 || v
== 5) && pico_usingcolor()
5277 && ps_global
->VAR_IND_LOPRI_FORE_COLOR
5278 && ps_global
->VAR_IND_LOPRI_BACK_COLOR
){
5279 ielem
->type
= eTypeCol
;
5280 ielem
->freecolor
= 1;
5281 ielem
->color
= new_color_pair(ps_global
->VAR_IND_LOPRI_FORE_COLOR
, ps_global
->VAR_IND_LOPRI_BACK_COLOR
);
5284 ourifield
->leftadj
= 1;
5285 set_ielem_widths_in_field(ourifield
);
5288 fs_give((void **) &hdrval
);
5294 header_str(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
, ICE_S
*ice
)
5296 IFIELD_S
*ourifield
= NULL
;
5297 IELEM_S
*ielem
= NULL
;
5298 char *fieldval
= NULL
;
5300 if(ice
&& ice
->ifield
){
5301 /* move to last ifield, the one we're working */
5302 for(ourifield
= ice
->ifield
;
5303 ourifield
&& ourifield
->next
;
5304 ourifield
= ourifield
->next
)
5311 fieldval
= get_fieldval(idata
, hdrtok
);
5314 ielem
= new_ielem(&ourifield
->ielem
);
5315 ielem
->freedata
= 1;
5316 ielem
->data
= fieldval
;
5317 ielem
->datalen
= strlen(fieldval
);
5319 ourifield
->leftadj
= (hdrtok
->adjustment
== Left
) ? 1 : 0;
5322 set_ielem_widths_in_field(ourifield
);
5327 get_fieldval(INDEXDATA_S
*idata
, HEADER_TOK_S
*hdrtok
)
5330 char *hdrval
= NULL
, *testval
;
5331 char *fieldval
= NULL
, *firstval
;
5332 char *retfieldval
= NULL
;
5335 return(retfieldval
);
5337 if(hdrtok
&& hdrtok
->hdrname
&& hdrtok
->hdrname
[0])
5338 hdrval
= fetch_header(idata
, hdrtok
? hdrtok
->hdrname
: "");
5340 /* find start of fieldnum'th field */
5342 for(fieldnum
= MAX(hdrtok
->fieldnum
-1, 0);
5343 fieldnum
> 0 && fieldval
&& *fieldval
; fieldnum
--){
5346 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5347 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5348 if(testval
&& (!firstval
|| testval
< firstval
))
5352 fieldval
= firstval
;
5353 if(fieldval
&& *fieldval
)
5357 /* tie off end of field */
5358 if(fieldval
&& *fieldval
&& hdrtok
->fieldnum
> 0){
5360 for(sep
= 0; sep
< hdrtok
->fieldsepcnt
; sep
++){
5361 testval
= hdrtok
->fieldseps
? strchr(fieldval
, hdrtok
->fieldseps
[sep
]) : NULL
;
5362 if(testval
&& (!firstval
|| testval
< firstval
))
5373 retfieldval
= cpystr(fieldval
);
5376 fs_give((void **) &hdrval
);
5378 return(retfieldval
);
5383 scorevalfrommsg(MAILSTREAM
*stream
, MsgNo rawno
, HEADER_TOK_S
*hdrtok
, int no_fetch
)
5387 char *fieldval
= NULL
;
5390 memset(&idata
, 0, sizeof(INDEXDATA_S
));
5391 idata
.stream
= stream
;
5392 idata
.no_fetch
= no_fetch
;
5393 idata
.msgno
= mn_raw2m(sp_msgmap(stream
), rawno
);
5394 idata
.rawno
= rawno
;
5395 if(stream
&& idata
.rawno
> 0L && idata
.rawno
<= stream
->nmsgs
5396 && (mc
= mail_elt(stream
, idata
.rawno
))){
5397 idata
.size
= mc
->rfc822_size
;
5398 index_data_env(&idata
, pine_mail_fetchenvelope(stream
,idata
.rawno
));
5403 fieldval
= get_fieldval(&idata
, hdrtok
);
5406 retval
= atol(fieldval
);
5407 fs_give((void **) &fieldval
);
5414 * Put a string representing the subject into str. Idata tells us which
5415 * message we are referring to.
5417 * This means we should ensure that all data ends up being UTF-8 data.
5418 * That covers the data in ice ielems and str.
5420 * Args idata -- which message?
5421 * str -- destination buffer
5422 * strsize -- size of str buffer
5423 * kwtype -- prepend keywords or kw initials before the subject
5424 * opening -- add first text from body of message if there's room
5425 * shorten -- if on, shorten the subject.
5426 * ice -- index cache entry for message
5429 subj_str(INDEXDATA_S
*idata
, char *str
, size_t strsize
, SubjKW kwtype
, int opening
, int shorten
, ICE_S
*ice
)
5431 char *subject
, *origsubj
, *origstr
, *rawsubj
, *sptr
= NULL
;
5432 char *p
, *border
, *q
= NULL
, *free_subj
= NULL
;
5436 int depth
= 0, mult
= 2;
5438 int do_subj
= 0, truncated_tree
= 0;
5439 PINETHRD_S
*thd
, *thdorig
;
5440 IELEM_S
*ielem
= NULL
, *subjielem
= NULL
;
5441 IFIELD_S
*ourifield
= NULL
;
5447 * If we need the data at the start of the message and we're in
5448 * a c-client callback, defer the data lookup until later.
5450 if(opening
&& idata
->no_fetch
){
5455 if(ice
&& ice
->ifield
){
5456 /* move to last ifield, the one we're working on */
5457 for(ourifield
= ice
->ifield
;
5458 ourifield
&& ourifield
->next
;
5459 ourifield
= ourifield
->next
)
5463 str
[0] = str
[strsize
-1] = '\0';
5465 rawsubj
= fetch_subject(idata
);
5470 * Before we do anything else, decode the character set in the subject and
5471 * work with the result.
5473 sp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5474 SIZEOF_20KBUF
, rawsubj
);
5477 len
+= 100; /* for possible charset, escaped characters */
5478 origsubj
= fs_get((len
+1) * sizeof(unsigned char));
5481 iutf8ncpy(origsubj
, sp
, len
);
5483 origsubj
[len
] = '\0';
5484 replace_tabs_by_space(origsubj
);
5485 removing_trailing_white_space(origsubj
);
5488 * origsubj is the original subject but it has been decoded. We need
5489 * to free it at the end of this routine.
5493 shorten_subject(origsubj
);
5496 * prepend_keyword will put the keyword stuff before the subject
5497 * and split the subject up into its colored parts in subjielem.
5498 * Subjielem is a local ielem which will have to be fit into the
5499 * real ifield->ielem later. The print_format strings in subjielem will
5500 * not be filled in by prepend_keyword because of the fact that we
5501 * may have to adjust things for threading below.
5502 * We use subjielem in case we want to insert some threading information
5503 * at the front of the subject.
5505 if(kwtype
== KW
|| kwtype
== KWInit
){
5506 subject
= prepend_keyword_subject(idata
->stream
, idata
->rawno
,
5508 ourifield
? &subjielem
: NULL
,
5509 ps_global
->VAR_KW_BRACES
);
5510 free_subj
= subject
;
5515 subjielem
= new_ielem(&subjielem
);
5516 subjielem
->type
= eTypeCol
;
5517 subjielem
->freedata
= 1;
5518 subjielem
->data
= cpystr(subject
);
5519 subjielem
->datalen
= strlen(subject
);
5520 if(pico_usingcolor()
5521 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
5522 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
5523 subjielem
->freecolor
= 1;
5524 subjielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
5530 * This space is here so that if the subject does
5531 * not extend all the way to the end of the field then
5532 * we'll switch the color back and paint the rest of the
5533 * field in the Normal color or the index line color.
5536 ielem
= new_ielem(&subjielem
);
5537 ielem
->freedata
= 1;
5538 ielem
->data
= cpystr(" ");
5546 && (ps_global
->thread_disp_style
== THREAD_STRUCT
5547 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
5548 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
5549 || ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ2
)){
5552 * Why do we want to truncate the subject and from strs?
5553 * It's so we can put the [5] thread count things in below when
5554 * we are threading and the thread structure runs off the right
5555 * hand edge of the screen. This routine doesn't know that it
5556 * is running off the edge unless it knows the actual width
5557 * that we have to draw in.
5559 if(pith_opt_truncate_sfstr
5560 && (*pith_opt_truncate_sfstr
)()
5562 && ourifield
->width
> 0)
5563 width
= ourifield
->width
;
5568 width
= MIN(width
, strsize
-1);
5571 * We're counting on the fact that this initial part of the
5572 * string is ascii and we have one octet per character and
5573 * characters are width 1 on the screen.
5575 border
= str
+ width
;
5577 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
5579 if(pith_opt_condense_thread_cue
)
5580 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
5582 && get_lflag(idata
->stream
,
5587 * width is < available strsize and
5588 * border points to something less than or equal
5589 * to the end of the buffer.
5595 while(thd
->parent
&&
5596 (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
5600 if(ps_global
->thread_disp_style
== THREAD_INDENT_SUBJ1
)
5603 sptr
+= (mult
*depth
);
5604 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
5605 thd
&& thd
->parent
&& p
>= str
;
5606 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
5607 if(p
+ mult
>= border
&& !q
){
5608 if(width
>= 4 && depth
< 100){
5609 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
5612 else if(width
>= 5 && depth
< 1000){
5613 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
5617 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
5630 if(ps_global
->thread_disp_style
== THREAD_STRUCT
5631 || ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5634 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5637 if(thd
== thdorig
&& !thd
->branch
)
5638 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
5639 else if(thd
== thdorig
|| thd
->branch
)
5642 if(p
+ 1 < border
&& thd
== thdorig
)
5649 if(sptr
&& !truncated_tree
){
5651 * Look to see if the subject is the same as the previous
5652 * message in the thread, if any. If it is the same, don't
5653 * reprint the subject.
5655 * Note that when we're prepending keywords to the subject,
5656 * and the user changes a keyword, we do invalidate
5657 * the index cache for that message but we don't go to the
5658 * trouble of invalidating the index cache for the the child
5659 * of that node in the thread, so the MUTT subject line
5660 * display for the child may be wrong. That is, it may show
5661 * it is the same as this subject even though it no longer
5662 * is, or vice versa.
5664 if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5668 if(thdorig
->parent
&&
5669 (thd
= fetch_thread(idata
->stream
, thdorig
->parent
))
5671 char *this_orig
= NULL
,
5673 *free_prev_orig
= NULL
,
5674 *this_prep
= NULL
, /* includes prepend */
5678 SORTCACHE
*sc
= NULL
;
5680 /* get the stripped subject of previous message */
5681 mc
= (mailcache_t
) mail_parameters(NIL
, GET_CACHE
, NIL
);
5683 sc
= (*mc
)(idata
->stream
, thd
->rawno
, CH_SORTCACHE
);
5685 if(sc
&& sc
->subject
)
5686 prev_orig
= sc
->subject
;
5690 env
= pine_mail_fetchenvelope(idata
->stream
,
5692 stripthis
= (env
&& env
->subject
)
5693 ? env
->subject
: "";
5695 mail_strip_subject(stripthis
, &prev_orig
);
5697 free_prev_orig
= prev_orig
;
5700 mail_strip_subject(rawsubj
, &this_orig
);
5702 if(kwtype
== KW
|| kwtype
== KWInit
){
5703 prev_prep
= prepend_keyword_subject(idata
->stream
,
5707 ps_global
->VAR_KW_BRACES
);
5709 this_prep
= prepend_keyword_subject(idata
->stream
,
5713 ps_global
->VAR_KW_BRACES
);
5715 if((this_prep
|| prev_prep
)
5716 && ((this_prep
&& !prev_prep
)
5717 || (prev_prep
&& !this_prep
)
5718 || strucmp(this_prep
, prev_prep
)))
5722 if((this_orig
|| prev_orig
)
5723 && ((this_orig
&& !prev_orig
)
5724 || (prev_orig
&& !this_orig
)
5725 || strucmp(this_orig
, prev_orig
)))
5730 * If some of the thread is zoomed out of view, we
5731 * want to display the subject of the first one that
5732 * is in view. If any of the parents or grandparents
5733 * etc of this message are visible, then we don't
5734 * need to worry about it. If all of the parents have
5735 * been zoomed away, then this is the first one.
5737 * When you're looking at a particular case where
5738 * some of the messages of a thread are selected it
5739 * seems like we should look at not only our
5740 * direct parents, but the siblings of the parent
5741 * too. But that's not really correct, because those
5742 * siblings are basically the starts of different
5743 * branches, separate from our branch. They could
5744 * have their own subjects, for example. This will
5745 * give us cases where it looks like we are showing
5746 * the subject too much, but it will be correct!
5748 * In zoom_index() we clear_index_cache_ent for
5749 * some lines which have subjects which might become
5750 * visible when we zoom, and also in set_lflags
5751 * where we might change subjects by unselecting
5752 * something when zoomed.
5756 if(!msgline_hidden(idata
->stream
,
5757 sp_msgmap(idata
->stream
),
5758 mn_raw2m(sp_msgmap(idata
->stream
),
5761 break; /* found a visible parent */
5764 if(thd
&& thd
->parent
)
5765 thd
= fetch_thread(idata
->stream
,thd
->parent
);
5770 if(!thd
) /* none were visible */
5775 fs_give((void **) &this_orig
);
5778 fs_give((void **) &this_prep
);
5781 fs_give((void **) &free_prev_orig
);
5784 fs_give((void **) &prev_prep
);
5795 * We don't need to worry about truncating to width
5796 * here. If we go over the right hand edge it will be
5799 strsize
-= (sptr
- str
);
5801 strncpy(sptr
, subject
, strsize
-1);
5802 sptr
[strsize
-1] = '\0';
5804 else if(ps_global
->thread_disp_style
== THREAD_MUTTLIKE
){
5805 strsize
-= (sptr
- str
);
5813 * We decided we don't need the subject so we'd better
5814 * eliminate subjielem.
5816 free_ielem(&subjielem
);
5820 free_ielem(&subjielem
); /* no room for actual subject */
5822 if(ourifield
&& sptr
&& sptr
> origstr
){
5823 ielem
= new_ielem(&ourifield
->ielem
);
5824 ielem
->type
= eThreadInfo
;
5825 ielem
->freedata
= 1;
5828 ielem
->data
= cpystr(origstr
);
5829 ielem
->datalen
= strlen(origstr
);
5835 * Not much to do for the non-threading case. Just copy the
5836 * subject we have so far into str and truncate it.
5838 strncpy(str
, subject
, strsize
-1);
5839 str
[strsize
-1] = '\0';
5844 * We need to add subjielem to the end of the ourifield->ielem list.
5847 if(ourifield
->ielem
){
5848 for(ielem
= ourifield
->ielem
;
5849 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5852 ielem
->next
= subjielem
;
5855 ourifield
->ielem
= subjielem
;
5858 ourifield
->leftadj
= 1;
5861 if(opening
&& ourifield
){
5862 IELEM_S
*ftielem
= NULL
;
5866 first_text
= fetch_firsttext(idata
, 0);
5872 strncpy(sep
, ps_global
->VAR_OPENING_SEP
? ps_global
->VAR_OPENING_SEP
: " - ",
5874 sep
[sizeof(sep
)-1] = '\0';
5875 removing_double_quotes(sep
);
5876 seplen
= strlen(sep
);
5878 ftielem
= new_ielem(&ftielem
);
5879 ftielem
->type
= eTypeCol
;
5880 ftielem
->freedata
= 1;
5881 len
= strlen(first_text
) + seplen
;
5882 ftielem
->data
= (char *) fs_get((len
+ 1) * sizeof(char));
5884 strncpy(ftielem
->data
, sep
, seplen
);
5885 strncpy(ftielem
->data
+seplen
, first_text
, len
+1-seplen
);
5886 ftielem
->data
[len
] = '\0';
5888 ftielem
->datalen
= strlen(ftielem
->data
);
5891 if(pico_usingcolor()
5892 && ps_global
->VAR_IND_OP_FORE_COLOR
5893 && ps_global
->VAR_IND_OP_BACK_COLOR
){
5894 ftielem
->freecolor
= 1;
5895 ftielem
->color
= new_color_pair(ps_global
->VAR_IND_OP_FORE_COLOR
, ps_global
->VAR_IND_OP_BACK_COLOR
);
5898 * This space is here so that if the opening text does
5899 * not extend all the way to the end of the field then
5900 * we'll switch the color back and paint the rest of the
5901 * field in the Normal color or the index line color.
5903 ielem
= new_ielem(&ftielem
);
5904 ielem
->freedata
= 1;
5905 ielem
->data
= cpystr(" ");
5909 if(ourifield
->ielem
){
5910 for(ielem
= ourifield
->ielem
;
5911 ielem
&& ielem
->next
; ielem
= ielem
->next
)
5914 ielem
->next
= ftielem
;
5917 ourifield
->ielem
= ftielem
;
5920 ourifield
->leftadj
= 1;
5925 set_ielem_widths_in_field(ourifield
);
5928 fs_give((void **) &origsubj
);
5931 fs_give((void **) &free_subj
);
5936 * Returns an allocated string which is the passed in subject with a
5937 * list of keywords prepended.
5939 * If kwtype == KW you will end up with
5941 * {keyword1 keyword2} subject
5943 * (actually, keyword nicknames will be used instead of the actual keywords
5944 * in the case that the user defined nicknames)
5946 * If kwtype == KWInit you get
5950 * where A is the first letter of the first keyword and B is the first letter
5951 * of the second defined keyword. No space between them. There could be more
5954 * If an ielemp is passed in it will be filled out with the data and colors
5955 * of the pieces of the subject but the print_format strings will not
5959 prepend_keyword_subject(MAILSTREAM
*stream
, long int rawno
, char *subject
,
5960 SubjKW kwtype
, IELEM_S
**ielemp
, char *braces
)
5962 char *p
, *next_piece
, *retsubj
= NULL
, *str
;
5963 char *left_brace
= NULL
, *right_brace
= NULL
;
5965 int some_set
= 0, save
;
5968 COLOR_PAIR
*color
= NULL
;
5969 SPEC_COLOR_S
*sc
= ps_global
->kw_colors
;
5974 if(braces
&& *braces
)
5975 get_pair(braces
, &left_brace
, &right_brace
, 1, 0);
5977 len
= (left_brace
? strlen(left_brace
) : 0) +
5978 (right_brace
? strlen(right_brace
) : 0);
5980 if(stream
&& rawno
>= 0L && rawno
<= stream
->nmsgs
){
5981 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
)
5982 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
5985 len
++; /* space between keywords */
5987 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5990 else if(kwtype
== KWInit
){
5991 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
5992 /* interested in only the first UTF-8 initial */
5995 unsigned long remaining_octets
;
5996 unsigned char *inputp
;
5998 remaining_octets
= strlen(str
);
5999 inputp
= (unsigned char *) str
;
6000 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
6001 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
6002 len
+= (unsigned) (inputp
- (unsigned char *) str
);
6011 if((kwtype
== KW
|| kwtype
== KWInit
) && some_set
){
6012 len
+= strlen(subject
); /* subject is already UTF-8 if needed */
6013 retsubj
= (char *) fs_get((len
+ 1) * sizeof(*retsubj
));
6014 memset(retsubj
, 0, (len
+ 1) * sizeof(*retsubj
));
6015 next_piece
= p
= retsubj
;
6017 for(kw
= ps_global
->keywords
; kw
; kw
= kw
->next
){
6018 if(user_flag_is_set(stream
, rawno
, kw
->kw
)){
6020 if(left_brace
&& len
> 0)
6021 sstrncpy(&p
, left_brace
, len
);
6023 else if(kwtype
== KW
)
6026 if(ielemp
&& p
> next_piece
){
6029 ielem
= new_ielem(ielemp
);
6030 ielem
->freedata
= 1;
6031 ielem
->data
= cpystr(next_piece
);
6032 ielem
->datalen
= strlen(next_piece
);
6037 str
= kw
->nick
? kw
->nick
: kw
->kw
? kw
->kw
: "";
6039 if(kwtype
== KWInit
){
6042 unsigned long remaining_octets
;
6043 unsigned char *inputp
;
6045 remaining_octets
= strlen(str
);
6046 inputp
= (unsigned char *) str
;
6047 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
6048 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
6049 if(len
-(p
-retsubj
) > 0){
6050 sstrncpy(&p
, str
, MIN(inputp
- (unsigned char *) str
,len
-(p
-retsubj
)));
6051 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
6052 && ((kw
->nick
&& kw
->nick
[0]
6053 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
6054 || (kw
->kw
&& kw
->kw
[0]
6055 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
6056 ielem
= new_ielem(ielemp
);
6057 ielem
->freedata
= 1;
6060 ielem
->data
= cpystr(next_piece
);
6061 ielem
->datalen
= strlen(next_piece
);
6062 ielem
->color
= color
;
6071 free_color_pair(&color
);
6075 if(len
-(p
-retsubj
) > 0)
6076 sstrncpy(&p
, str
, len
-(p
-retsubj
));
6078 if(p
> next_piece
&& ielemp
&& pico_usingcolor()
6079 && ((kw
->nick
&& kw
->nick
[0]
6080 && (color
=hdr_color(kw
->nick
,NULL
,sc
)))
6081 || (kw
->kw
&& kw
->kw
[0]
6082 && (color
=hdr_color(kw
->kw
,NULL
,sc
))))){
6083 ielem
= new_ielem(ielemp
);
6084 ielem
->freedata
= 1;
6087 ielem
->data
= cpystr(next_piece
);
6088 ielem
->datalen
= strlen(next_piece
);
6089 ielem
->color
= color
;
6096 free_color_pair(&color
);
6101 if(len
-(p
-retsubj
) > 0 && right_brace
)
6102 sstrncpy(&p
, right_brace
, len
-(p
-retsubj
));
6104 if(ielemp
&& p
> next_piece
){
6107 ielem
= new_ielem(ielemp
);
6108 ielem
->freedata
= 1;
6109 ielem
->data
= cpystr(next_piece
);
6110 ielem
->datalen
= strlen(next_piece
);
6115 if(len
-(p
-retsubj
) > 0 && subject
)
6116 sstrncpy(&p
, subject
, len
-(p
-retsubj
));
6118 if(ielemp
&& p
> next_piece
){
6121 ielem
= new_ielem(ielemp
);
6122 ielem
->type
= eTypeCol
;
6123 ielem
->freedata
= 1;
6124 ielem
->data
= cpystr(next_piece
);
6125 ielem
->datalen
= strlen(next_piece
);
6128 if(pico_usingcolor()
6129 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
6130 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
6131 ielem
->freecolor
= 1;
6132 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
6136 retsubj
[len
] = '\0'; /* just making sure */
6140 ielem
= new_ielem(ielemp
);
6141 ielem
->type
= eTypeCol
;
6142 ielem
->freedata
= 1;
6143 ielem
->data
= cpystr(subject
);
6144 ielem
->datalen
= strlen(subject
);
6145 if(pico_usingcolor()
6146 && ps_global
->VAR_IND_SUBJ_FORE_COLOR
6147 && ps_global
->VAR_IND_SUBJ_BACK_COLOR
){
6148 ielem
->freecolor
= 1;
6149 ielem
->color
= new_color_pair(ps_global
->VAR_IND_SUBJ_FORE_COLOR
, ps_global
->VAR_IND_SUBJ_BACK_COLOR
);
6153 retsubj
= cpystr(subject
);
6158 fs_give((void **) &left_brace
);
6161 fs_give((void **) &right_brace
);
6169 * This means we should ensure that all data ends up being UTF-8 data.
6170 * That covers the data in ice ielems and str.
6173 from_str(IndexColType ctype
, INDEXDATA_S
*idata
, char *str
, size_t strsize
, ICE_S
*ice
)
6175 char *field
, *newsgroups
, *border
, *p
, *fptr
= NULL
, *q
= NULL
;
6178 int depth
= 0, mult
= 2;
6179 PINETHRD_S
*thd
, *thdorig
;
6182 && (ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
6183 || ps_global
->thread_disp_style
== THREAD_INDENT_FROM2
6184 || ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
)){
6186 if(pith_opt_truncate_sfstr
&& (*pith_opt_truncate_sfstr
)()){
6187 IFIELD_S
*ourifield
= NULL
;
6189 if(ice
&& ice
->ifield
){
6190 /* move to last ifield, the one we're working on */
6191 for(ourifield
= ice
->ifield
;
6192 ourifield
&& ourifield
->next
;
6193 ourifield
= ourifield
->next
)
6197 if(ourifield
&& ourifield
->width
> 0)
6198 width
= ourifield
->width
;
6204 width
= MIN(width
, strsize
-1);
6206 thdorig
= thd
= fetch_thread(idata
->stream
, idata
->rawno
);
6207 border
= str
+ width
;
6208 if(pith_opt_condense_thread_cue
)
6209 width
= (*pith_opt_condense_thread_cue
)(thd
, ice
, &str
, &strsize
, width
,
6211 && get_lflag(idata
->stream
,
6218 while(thd
->parent
&& (thd
= fetch_thread(idata
->stream
, thd
->parent
)))
6222 if(ps_global
->thread_disp_style
== THREAD_INDENT_FROM1
)
6225 fptr
+= (mult
*depth
);
6226 for(thd
= thdorig
, p
= str
+ mult
*depth
- mult
;
6227 thd
&& thd
->parent
&& p
>= str
;
6228 thd
= fetch_thread(idata
->stream
, thd
->parent
), p
-= mult
){
6229 if(p
+ mult
>= border
&& !q
){
6230 if(width
>= 4 && depth
< 100){
6231 snprintf(str
, width
+1, "%*s[%2d]", width
-4, "", depth
);
6234 else if(width
>= 5 && depth
< 1000){
6235 snprintf(str
, width
+1, "%*s[%3d]", width
-5, "", depth
);
6239 snprintf(str
, width
+1, "%s", repeat_char(width
, '.'));
6249 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6252 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6255 if(thd
== thdorig
&& !thd
->branch
)
6256 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6257 else if(thd
== thdorig
|| thd
->branch
)
6264 else if(p
< border
){
6266 if(ps_global
->thread_disp_style
== THREAD_STRUCT_FROM
){
6269 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6272 if(thd
== thdorig
&& !thd
->branch
)
6273 p
[0] = ps_global
->VAR_THREAD_LASTREPLY_CHAR
[0];
6274 else if(thd
== thdorig
|| thd
->branch
)
6285 strsize
-= (fptr
- str
);
6288 case iFromToNotNews
:
6289 if(!(addr
= fetch_from(idata
)) || address_is_us(addr
, ps_global
)){
6291 strncpy(fptr
, "To: ", strsize
-1);
6292 fptr
[strsize
-1] = '\0';
6296 if((field
= ((addr
= fetch_to(idata
))
6298 : (addr
= fetch_cc(idata
))
6301 && set_index_addr(idata
, field
, addr
, "To: ",
6305 if(ctype
== iFromTo
&&
6306 (newsgroups
= fetch_newsgroups(idata
)) &&
6308 snprintf(fptr
, strsize
, "To: %-*.*s", (int)(strsize
-1-4), (int)(strsize
-1-4),
6313 /* else fall thru to From: */
6316 /* else fall thru to From: */
6322 set_index_addr(idata
, "From", fetch_from(idata
), NULL
, strsize
-1, fptr
);
6327 if((addr
= fetch_from(idata
)) && addr
->mailbox
&& addr
->mailbox
[0]){
6328 char *mb
= NULL
, *hst
= NULL
, *at
= NULL
;
6332 if(ctype
== iAddress
&& addr
->host
&& addr
->host
[0]
6333 && addr
->host
[0] != '.'){
6339 if(!at
|| strsize
-1 <= len
)
6340 snprintf(fptr
, strsize
, "%-*.*s", (int)(strsize
-1), (int)(strsize
-1), mb
);
6342 snprintf(fptr
, strsize
, "%s@%-*.*s", mb
, (int)(strsize
-1-len
-1), (int)(strsize
-1-len
-1), hst
);
6351 replace_tabs_by_space(str
);
6356 * Set up the elements contained in field so that they take up the
6357 * whole field width. Data is assumed to be UTF-8.
6360 set_ielem_widths_in_field(IFIELD_S
*ifield
)
6362 IELEM_S
*ielem
= NULL
;
6363 int datawidth
, fmtwidth
;
6368 fmtwidth
= ifield
->width
;
6370 for(ielem
= ifield
->ielem
; ielem
&& fmtwidth
> 0; ielem
= ielem
->next
){
6371 if(!ifield
->leftadj
&& ielem
->next
){
6372 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield
->ctype
));
6376 datawidth
= (int) utf8_width(ielem
->data
);
6377 if(datawidth
>= fmtwidth
|| !ielem
->next
){
6378 set_print_format(ielem
, fmtwidth
, ifield
->leftadj
);
6382 set_print_format(ielem
, datawidth
, ifield
->leftadj
);
6383 fmtwidth
-= datawidth
;
6390 * Simple hash function from K&R 2nd edition, p. 144.
6392 * This one is modified to never return 0 so we can use that as a special
6393 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6394 * as a special value that can't be returned by line_hash.
6399 unsigned long hashval
;
6401 for(hashval
= 0; *s
!= '\0'; s
++)
6402 hashval
= *s
+ 31 * hashval
;
6404 hashval
= hashval
% LINE_HASH_N
;
6414 * Returns nonzero if considered hidden, 0 if not considered hidden.
6417 msgline_hidden(MAILSTREAM
*stream
, MSGNO_S
*msgmap
, long int msgno
, int flags
)
6421 if(flags
& MH_ANYTHD
){
6422 ret
= ((any_lflagged(msgmap
, MN_HIDE
) > 0)
6423 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
));
6425 else if(flags
& MH_THISTHD
&& THREADING() && sp_viewing_a_thread(stream
)){
6426 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6427 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
));
6430 if(THREADING() && sp_viewing_a_thread(stream
)){
6431 ret
= (get_lflag(stream
, msgmap
, msgno
, MN_HIDE
)
6432 || !get_lflag(stream
, msgmap
, msgno
, MN_CHID2
)
6433 || get_lflag(stream
, msgmap
, msgno
, MN_CHID
));
6435 else if(THRD_INDX()){
6437 * If this message is in the collapsed part of a thread,
6438 * it's hidden. It must be a top-level of a thread to be
6439 * considered visible. Even if it is top-level, it is only
6440 * visible if some message in the thread is not hidden.
6442 if(get_lflag(stream
, msgmap
, msgno
, MN_CHID
)) /* not top */
6445 unsigned long rawno
;
6446 PINETHRD_S
*thrd
= NULL
;
6448 rawno
= mn_m2raw(msgmap
, msgno
);
6450 thrd
= fetch_thread(stream
, rawno
);
6452 ret
= !thread_has_some_visible(stream
, thrd
);
6456 ret
= ((any_lflagged(msgmap
, MN_HIDE
| MN_CHID
) > 0)
6457 && get_lflag(stream
, msgmap
, msgno
, MN_HIDE
| MN_CHID
));
6462 "msgline_hidden(%ld): %s\n", msgno
, ret
? "HID" : "VIS"));
6469 adjust_cur_to_visible(MAILSTREAM
*stream
, MSGNO_S
*msgmap
)
6474 cur
= mn_get_cur(msgmap
);
6476 /* if current is hidden, adjust */
6477 if(cur
>= 1L && cur
<= mn_get_total(msgmap
)
6478 && msgline_hidden(stream
, msgmap
, cur
, 0)){
6480 dir
= mn_get_revsort(msgmap
) ? -1 : 1;
6483 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6484 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6485 && msgline_hidden(stream
, msgmap
, n
, 0);
6489 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6490 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6491 mn_reset_cur(msgmap
, n
);
6492 else{ /* no visible in that direction */
6494 ((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6495 && (n
>= 1L && n
<= mn_get_total(msgmap
))
6496 && msgline_hidden(stream
, msgmap
, n
, 0);
6500 if(((dir
== 1 && n
>= 1L) || (dir
== -1 && n
<= mn_get_total(msgmap
)))
6501 && (n
>= 1L && n
<= mn_get_total(msgmap
)))
6502 mn_reset_cur(msgmap
, n
);
6510 setup_for_index_index_screen(void)
6512 format_index_line
= format_index_index_line
;
6513 setup_header_widths
= setup_index_header_widths
;
6518 setup_for_thread_index_screen(void)
6520 format_index_line
= format_thread_index_line
;
6521 setup_header_widths
= setup_thread_header_widths
;
6526 ice_hash(ICE_S
*ice
)
6528 char buf
[MAX_SCREEN_COLS
+1];
6533 simple_index_line(buf
, sizeof(buf
), ice
, 0L);
6535 buf
[sizeof(buf
) - 1] = '\0';
6537 return(line_hash(buf
));
6542 left_adjust(int width
)
6544 return(format_str(width
, 1));
6549 right_adjust(int width
)
6551 return(format_str(width
, 0));
6556 * Returns allocated and filled in format string.
6559 format_str(int width
, int left
)
6564 len
= PRINT_FORMAT_LEN(width
,left
) * sizeof(char);
6565 format
= (char *) fs_get(len
+ 1);
6566 copy_format_str(width
, left
, format
, len
);
6574 * Put the left or right adjusted format string of width width into
6575 * dest. Dest is of size n+1.
6578 copy_format_str(int width
, int left
, char *dest
, int n
)
6582 p
= int2string(width
);
6584 snprintf(dest
, n
+1, "%%%s%s.%ss", left
? "-" : "", p
, p
);
6593 * Sets up the print_format string to be width wide with left or right
6594 * adjust. Takes care of memory freeing and allocation.
6597 set_print_format(IELEM_S
*ielem
, int width
, int leftadj
)
6602 if(ielem
->print_format
){
6603 /* is there enough room? */
6604 if(ielem
->freeprintf
< PRINT_FORMAT_LEN(width
,leftadj
)+1){
6605 fs_resize((void **) &ielem
->print_format
,
6606 (PRINT_FORMAT_LEN(width
,leftadj
)+1) * sizeof(char));
6607 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);
6610 copy_format_str(width
, leftadj
, ielem
->print_format
,
6611 PRINT_FORMAT_LEN(width
,leftadj
));
6614 ielem
->print_format
= leftadj
? left_adjust(width
)
6615 : right_adjust(width
);
6616 ielem
->freeprintf
= (PRINT_FORMAT_LEN(width
,leftadj
) + 1) * sizeof(char);