Add support for tab-completion when selecting by rule
[alpine.git] / pith / mailindx.c
blob27e1b29833b16c2c4ea35d53a778c4494968e5c4
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"
40 #ifdef _WINDOWS
41 #include "../pico/osdep/mswin.h"
42 #endif
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);
69 * Internal prototypes
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
110 format string.
112 Args: format -- the string containing the format tokens
113 answer -- put the answer here, free first if there was a previous
114 value here
115 ----*/
116 void
117 init_index_format(char *format, INDEX_COL_S **answer)
119 char *p;
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[] = {
139 {iStatus, Fixed, 3},
140 {iMessNo, WeCalculate},
141 {iSDateTime24, WeCalculate},
142 {iFromTo, Percent, 33}, /* percent of rest */
143 {iSizeNarrow, WeCalculate},
144 {iSubjKey, Percent, 67},
145 {iNothing}
148 if(*answer)
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){
173 case iSDate:
174 (*answer)[column].ctype = iS1Date;
175 break;
176 case iSDateTime:
177 (*answer)[column].ctype = iSDateTimeS1;
178 break;
179 case iSDateTime24:
180 (*answer)[column].ctype = iSDateTimeS124;
181 break;
182 default:
183 break;
187 if((*answer)[column].wtype == WeCalculate){
188 switch((*answer)[column].ctype){
189 case iPrio:
190 case iPrioBang:
191 case iAtt:
192 (*answer)[column].req_width = 1;
193 break;
194 case iYear2Digit:
195 case iDay:
196 case iMon:
197 case iDay2Digit:
198 case iMon2Digit:
199 case iArrow:
200 case iKeyInit:
201 (*answer)[column].req_width = 2;
202 break;
203 case iStatus:
204 case iMessNo:
205 case iInit:
206 (*answer)[column].req_width = 3;
207 break;
208 case iYear:
209 case iDayOrdinal:
210 case iSIStatus:
211 (*answer)[column].req_width = 4;
212 break;
213 case iTime24:
214 case iTimezone:
215 case iSizeNarrow:
216 case iKey:
217 (*answer)[column].req_width = 5;
218 break;
219 case iFStatus:
220 case iIStatus:
221 case iScore:
222 (*answer)[column].req_width = 6;
223 break;
224 case iTime12:
225 case iSTime:
226 case iKSize:
227 case iSize:
228 case iPrioAlpha:
229 (*answer)[column].req_width = 7;
230 break;
231 case iS1Date:
232 case iS2Date:
233 case iS3Date:
234 case iS4Date:
235 case iDateIsoS:
236 case iSizeComma:
237 (*answer)[column].req_width = 8;
238 break;
239 case iSTime24:
240 (*answer)[column].req_width = 9;
241 break;
242 case iMonAbb:
243 (*answer)[column].req_width = monabb_width;
244 (*answer)[column].monabb_width = monabb_width;
245 break;
246 case iDayOfWeekAbb:
248 w = 0;
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);
260 break;
261 case iDate:
262 (*answer)[column].req_width = monabb_width + 3;
263 (*answer)[column].monabb_width = monabb_width;
264 break;
265 case iMonLong:
267 w = 0;
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);
279 break;
280 case iDayOfWeek:
282 w = 0;
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);
291 break;
292 case iSDate:
293 case iSDateTime:
294 case iSDateTime24:
295 case iPrefDate:
296 case iPrefTime:
297 case iPrefDateTime:
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...
305 struct tm tm;
306 int len = 20;
307 char ss[100];
309 memset(&tm, 0, sizeof(tm));
310 tm.tm_year = 106;
311 tm.tm_mon = 11;
312 tm.tm_mday = 31;
313 tm.tm_hour = 3;
314 tm.tm_min = 23;
315 tm.tm_wday = 3;
316 switch((*answer)[column].ctype){
317 case iPrefTime:
318 our_strftime(ss, sizeof(ss), "%X", &tm);
319 break;
320 case iPrefDateTime:
321 our_strftime(ss, sizeof(ss), "%c", &tm);
322 len = 32;
323 break;
324 default:
325 our_strftime(ss, sizeof(ss), "%x", &tm);
326 break;
328 (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), len);
331 (*answer)[column].monabb_width = monabb_width;
332 break;
334 case iDescripSize:
335 case iSDateIsoS:
336 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
337 case iSDateTimeIsoS:
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;
345 break;
346 case iDateIso:
347 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
348 (*answer)[column].req_width = 10;
349 break;
350 case iLDate:
351 (*answer)[column].req_width = 12;
352 (*answer)[column].monabb_width = monabb_width;
353 break;
354 case iRDate:
355 (*answer)[column].req_width = 16;
356 break;
357 default:
358 break;
363 calc_extra_hdrs();
364 if(get_extra_hdrs())
365 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
366 (void *) get_extra_hdrs());
370 void
371 reset_index_format(void)
373 long rflags = ROLE_DO_OTHER;
374 PAT_STATE pstate;
375 PAT_S *pat;
376 int we_set_it = 0;
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))
382 break;
385 if(pat && pat->action && !pat->action->bogus
386 && pat->action->index_format){
387 we_set_it++;
388 init_index_format(pat->action->index_format,
389 &ps_global->index_disp_format);
393 if(!we_set_it)
394 init_index_format(ps_global->VAR_INDEX_FORMAT,
395 &ps_global->index_disp_format);
399 void
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++)
406 if(cdesc->hdrtok)
407 free_hdrtok(&cdesc->hdrtok);
409 fs_give((void **) disp_format);
414 HEADER_TOK_S *
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(" ");
427 return(hdrtok);
431 void
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])];
582 void
583 inverse_itokens(void)
585 INDEX_PARSE_T *pt;
586 for (pt = itokens; pt->name; pt++)
587 itokensinv[pt->ctype].ctype = pt - itokens;
591 INDEX_PARSE_T *
592 itoken(int i)
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.
607 INDEX_PARSE_T *
608 itoktype(char *txt, int flags)
610 INDEX_PARSE_T *pt;
611 char token[100 + 1];
612 char *v, *w;
615 * Separate a copy of the possible token out of txt.
617 v = txt;
618 w = token;
619 while(w < token+sizeof(token)-1 &&
620 *v &&
621 !(!(*v & 0x80) && isspace((unsigned char)*v)) &&
622 !(flags & DELIM_USCORE && *v == '_') &&
623 !(flags & DELIM_PAREN && *v == '(') &&
624 !(flags & DELIM_COLON && *v == ':'))
625 *w++ = *v++;
627 *w = '\0';
629 for(pt = itokens; pt->name; pt++)
630 if(pt->what_for & flags && !strucmp(pt->name, token))
631 return(pt);
633 return(NULL);
638 parse_index_format(char *format_str, INDEX_COL_S **answer)
640 int i, column = 0;
641 char *p, *q;
642 INDEX_PARSE_T *pt;
643 INDEX_COL_S cdesc[200]; /* plenty of temp storage for answer */
645 memset((void *)cdesc, 0, sizeof(cdesc));
647 p = format_str;
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 */
654 if(!pt){
655 char c;
656 for(q = p; *p && !isspace((unsigned char)*p); p++)
659 if((c = *p) != '\0')
660 *p++ = '\0';
662 dprint((1,
663 "parse_index_format: unrecognized token: %s\n",
664 q ? q : "?"));
665 q_status_message1(SM_ORDER | SM_DING, 0, 3,
666 _("Unrecognized word in index-format: %s"), q);
667 if(c != '\0')
668 *(p-1) = c;
669 continue;
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
687 * HEADER:hdrname or
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
698 * TEXT:text or
699 * TEXT:text(10) or
700 * TEXT:text(10%)
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.
706 /* skip over name */
707 p += strlen(pt->name);
709 /* look for header name */
710 if(*p == ':'){
711 char *w, hdrname[200];
713 hdrname[0] = '\0';
714 w = hdrname;
715 p++;
716 if(*p == '\"'){ /* quoted name */
717 p++;
718 while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
719 if(*p == '\\')
720 p++;
722 *w++ = *p++;
725 *w = '\0';
726 if(*p == '\"')
727 p++;
729 else{
730 while(w < hdrname + sizeof(hdrname)-1 &&
731 !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
732 *p != '(')
733 *w++ = *p++;
735 *w = '\0';
738 if(hdrname[0]){
739 cdesc[column].hdrtok = new_hdrtok(hdrname);
741 else{
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");
746 else{
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");
752 else{
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");
757 else{
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 != '(')
764 p++;
767 else{
768 /* skip over name and look for parens */
769 p += strlen(pt->name);
772 if(*p == '('){
773 p++;
774 q = p;
775 while(p && *p && isdigit((unsigned char) *p))
776 p++;
778 if(pt->ctype == iHeader){
779 /* first argument is width or width percentage, like for others */
780 if(p && *p && (*p == ')' || *p == ',')){
781 if(p > q){
782 cdesc[column].wtype = Fixed;
783 cdesc[column].req_width = atoi(q);
785 else{
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);
793 p++;
795 else{
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 == ','){
802 p++;
803 /* no space allowed between arguments */
804 if(*p && isdigit((unsigned char) *p)){
805 q = p;
806 while(*p && isdigit((unsigned char) *p))
807 p++;
809 cdesc[column].hdrtok->fieldnum = atoi(q);
812 * Optional 3rd argument is field separators.
813 * Comma is \, and backslash is \\.
815 if(*p == ','){
816 int j;
818 p++;
819 /* don't use default */
820 if(*p && *p != ')' && *p != ',' && cdesc[column].hdrtok->fieldseps)
821 cdesc[column].hdrtok->fieldseps[0] = '\0';
823 j = 0;
824 if(*p == '\"' && strchr(p+1, '\"')){
825 p++;
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))
831 p++;
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;
840 if(*p == '\"')
841 p++;
843 else{
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))
848 p++;
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 */
859 if(*p == ','){
860 p++;
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;
865 else{
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");
872 else{
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");
878 else{
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);
887 else{
888 cdesc[column].wtype = WeCalculate;
889 cdesc[column].req_width = 0;
893 else{
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);
899 else{
900 cdesc[column].wtype = WeCalculate;
901 cdesc[column].req_width = 0;
905 column++;
906 /* skip text at end of word */
907 while(p && *p && !isspace((unsigned char)*p))
908 p++;
911 /* if, after all that, we didn't find anything recognizable, bitch */
912 if(!column){
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."));
916 return(0);
919 /* Finish with Nothing column */
920 cdesc[column].ctype = iNothing;
922 /* free up old answer */
923 if(*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];
933 return(1);
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)
963 int j;
965 for(j = 0; ; j++){
966 if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
967 break;
969 if(ctype == fixed_ctypes[j])
970 return 1;
973 return 0;
977 /*----------------------------------------------------------------------
978 Setup the widths of the various columns in the index display
979 ----*/
980 void
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;
987 long max_msgno;
988 WidthType wtype;
989 INDEX_COL_S *cdesc;
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;
999 colspace = -1;
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;
1009 cdesc++){
1011 if(cdesc->wtype == Fixed){
1012 cdesc->width = cdesc->req_width;
1013 if(cdesc->width > 0)
1014 colspace++;
1016 else if(cdesc->wtype == Percent){
1017 cdesc->width = 0; /* calculated later */
1018 colspace++;
1020 else{ /* WeCalculate */
1021 cdesc->width = cdesc->req_width; /* reserve this for now */
1022 some_to_calculate++;
1023 colspace++;
1026 /* no space after iText */
1027 if(cdesc->ctype == iText)
1028 colspace--;
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
1044 * in fixed_ctypes.
1046 for(cdesc = ps_global->index_disp_format;
1047 cdesc->ctype != iNothing;
1048 cdesc++){
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);
1068 break;
1070 default:
1071 break;
1074 if(ctype_is_fixed_length(cdesc->ctype)){
1075 switch(cdesc->ctype){
1076 case iPrio:
1077 case iPrioBang:
1078 case iAtt:
1079 cdesc->actual_length = 1;
1080 cdesc->adjustment = Left;
1081 break;
1083 case iYear2Digit:
1084 case iDay2Digit:
1085 case iMon2Digit:
1086 cdesc->actual_length = 2;
1087 cdesc->adjustment = Left;
1088 break;
1090 case iArrow:
1091 cdesc->actual_length = 2;
1092 cdesc->adjustment = Right;
1093 break;
1095 case iStatus:
1096 case iInit:
1097 cdesc->actual_length = 3;
1098 cdesc->adjustment = Left;
1099 break;
1101 case iMessNo:
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;
1109 else
1110 cdesc->actual_length = 6;
1112 cdesc->adjustment = Right;
1113 break;
1115 case iYear:
1116 case iSIStatus:
1117 cdesc->actual_length = 4;
1118 cdesc->adjustment = Left;
1119 break;
1121 case iTime24:
1122 case iTimezone:
1123 cdesc->actual_length = 5;
1124 cdesc->adjustment = Left;
1125 break;
1127 case iSizeNarrow:
1128 cdesc->actual_length = 5;
1129 cdesc->adjustment = Right;
1130 break;
1132 case iFStatus:
1133 case iIStatus:
1134 cdesc->actual_length = 6;
1135 cdesc->adjustment = Left;
1136 break;
1138 case iScore:
1139 cdesc->actual_length = 6;
1140 cdesc->adjustment = Right;
1141 break;
1143 case iTime12:
1144 case iSize:
1145 case iKSize:
1146 cdesc->actual_length = 7;
1147 cdesc->adjustment = Right;
1148 break;
1150 case iSTime:
1151 cdesc->actual_length = 7;
1152 cdesc->adjustment = Left;
1153 break;
1155 case iPrioAlpha:
1156 cdesc->actual_length = 7;
1157 cdesc->adjustment = Left;
1158 break;
1161 case iS1Date:
1162 case iS2Date:
1163 case iS3Date:
1164 case iS4Date:
1165 case iDateIsoS:
1166 cdesc->actual_length = 8;
1167 cdesc->adjustment = Left;
1168 break;
1170 case iSizeComma:
1171 cdesc->actual_length = 8;
1172 cdesc->adjustment = Right;
1173 break;
1175 case iSDate:
1176 case iSDateTime:
1177 case iSDateTime24:
1178 case iMonAbb:
1179 case iDayOfWeekAbb:
1180 case iDayOfWeek:
1181 case iDate:
1182 case iMonLong:
1183 cdesc->actual_length = cdesc->req_width;
1184 cdesc->adjustment = Left;
1185 break;
1187 case iSTime24:
1188 case iSDateIsoS:
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;
1199 else
1200 cdesc->actual_length = 9;
1202 cdesc->adjustment = Left;
1203 break;
1205 case iDescripSize:
1206 cdesc->actual_length = 9;
1207 cdesc->adjustment = Right;
1208 break;
1210 case iDateIso:
1211 cdesc->actual_length = 10;
1212 cdesc->adjustment = Left;
1213 break;
1215 case iLDate:
1216 cdesc->actual_length = 12;
1217 cdesc->adjustment = Left;
1218 break;
1220 default:
1221 alpine_panic("Unhandled fixed case in setup_index_header");
1222 break;
1225 else if(cdesc->ctype == iHeader)
1226 cdesc->adjustment = cdesc->hdrtok ? cdesc->hdrtok->adjustment : Left;
1227 else
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;
1238 cdesc++)
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 */
1244 cdesc->width = 0;
1245 space_left += fix;
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))
1261 break;
1263 for(cdesc = ps_global->index_disp_format;
1264 cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
1265 cdesc++)
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;
1270 space_left -= 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.
1279 if(space_left > 0){
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;
1289 cdesc++){
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;
1303 cdesc++){
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;
1311 space_left -= fix;
1315 else{
1316 for(cdesc = ps_global->index_disp_format;
1317 cdesc->ctype != iNothing && space_left > 0;
1318 cdesc++){
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;
1325 space_left -= fix;
1330 else{
1331 tot_pct = 0;
1332 was_sl = space_left;
1333 /* add up total percentages requested */
1334 for(cdesc = ps_global->index_disp_format;
1335 cdesc->ctype != iNothing;
1336 cdesc++)
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;
1343 cdesc++){
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;
1348 space_left -= fix;
1354 /* split up rest, give twice as much to Subject */
1355 keep_going = 1;
1356 while(space_left > 0 && keep_going){
1357 keep_going = 0;
1358 for(cdesc = ps_global->index_disp_format;
1359 cdesc->ctype != iNothing && space_left > 0;
1360 cdesc++){
1361 if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
1362 keep_going++;
1363 cdesc->width++;
1364 space_left--;
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)){
1374 cdesc->width++;
1375 space_left--;
1381 /* if still more, pad out percent's */
1382 keep_going = 1;
1383 while(space_left > 0 && keep_going){
1384 keep_going = 0;
1385 for(cdesc = ps_global->index_disp_format;
1386 cdesc->ctype != iNothing && space_left > 0;
1387 cdesc++){
1388 if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
1389 keep_going++;
1390 cdesc->width++;
1391 space_left--;
1396 /* if user made Fixed fields too big, give back space */
1397 keep_going = 1;
1398 while(space_left < 0 && keep_going){
1399 keep_going = 0;
1400 for(cdesc = ps_global->index_disp_format;
1401 cdesc->ctype != iNothing && space_left < 0;
1402 cdesc++){
1403 if(cdesc->wtype == Fixed && cdesc->width > 0){
1404 keep_going++;
1405 cdesc->width--;
1406 space_left++;
1411 if(pith_opt_save_index_state)
1412 (*pith_opt_save_index_state)(FALSE);
1416 void
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
1432 void
1433 load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
1435 if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
1436 INDEXDATA_S idata;
1437 ICE_S *ice;
1439 memset(&idata, 0, sizeof(INDEXDATA_S));
1440 idata.no_fetch = 1;
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
1446 * in a single RTT.
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){
1458 if(THRD_INDX()){
1459 if(ice->tice)
1460 clear_ice(&ice->tice);
1462 else
1463 clear_ice(&ice);
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);
1475 ICE_S *
1476 build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
1477 long int msgno, long int top_msgno, int msgcount, int *fetched)
1479 ICE_S *ice, *ic;
1480 MESSAGECACHE *mc;
1481 long n, i, cnt, rawno, visible, limit = -1L;
1483 rawno = mn_m2raw(msgmap, msgno);
1485 /* cache hit? */
1486 if(THRD_INDX()){
1487 ice = fetch_ice(stream, rawno);
1488 if(!ice)
1489 return(NULL);
1491 if(ice->tice && ice->tice->ifield
1492 && ice->tice->color_lookup_done && ice->tice->widths_done){
1493 #ifdef DEBUG
1494 char buf[MAX_SCREEN_COLS+1];
1495 simple_index_line(buf, sizeof(buf), ice->tice, msgno);
1496 #endif
1497 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1498 ice->tice,
1499 buf[0] ? buf : "?",
1500 buf[0] ? strlen(buf) : 0));
1501 return(ice);
1504 else{
1505 ice = fetch_ice(stream, rawno);
1506 if(!ice)
1507 return(NULL);
1509 if(ice->ifield && ice->color_lookup_done && ice->widths_done){
1510 #ifdef DEBUG
1511 char buf[MAX_SCREEN_COLS+1];
1512 simple_index_line(buf, sizeof(buf), ice, msgno);
1513 #endif
1514 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1515 ice,
1516 buf[0] ? buf : "?",
1517 buf[0] ? strlen(buf) : 0));
1518 return(ice);
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
1540 * of prefetch...
1542 if(!(fetched && *fetched) && index_in_overview(stream)
1543 && ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1544 || (!THRD_INDX() && !ice->ifield))){
1545 char *seq;
1546 int count;
1547 MESSAGECACHE *mc;
1548 PINETHRD_S *thrd;
1550 if(fetched)
1551 (*fetched)++;
1553 /* clear sequence bits */
1554 for(n = 1L; n <= stream->nmsgs; n++)
1555 if((mc = mail_elt(stream, n)) != NULL)
1556 mc->sequence = 0;
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
1573 * with MN_CHID2.
1575 for(visible = 0L, n = top_msgno;
1576 visible < msgcount && n <= mn_get_total(msgmap);
1577 n++){
1579 if(!get_lflag(stream, msgmap, n, MN_CHID2))
1580 break;
1582 if(!msgline_hidden(stream, msgmap, n, 0))
1583 visible++;
1587 else
1588 visible = mn_get_total(msgmap)
1589 - any_lflagged(msgmap, MN_HIDE|MN_CHID);
1591 limit = MIN(visible, msgcount);
1593 if(THRD_INDX()){
1594 count = i = 0;
1597 * First add the msgno we're asking for in case it
1598 * isn't visible.
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
1611 * threads.
1613 while(thrd){
1614 n = mn_raw2m(msgmap, thrd->rawno);
1615 if(n >= msgno
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);
1621 if(++i >= limit)
1622 break;
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);
1630 else
1631 thrd = NULL;
1632 } while(thrd
1633 && msgline_hidden(stream, msgmap,
1634 mn_raw2m(msgmap, thrd->rawno), 0));
1637 else{
1638 count = i = 0;
1641 * First add the msgno we're asking for in case it
1642 * isn't visible.
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
1651 * in that case.
1653 if(THREADING()
1654 && ps_global->thread_disp_style == THREAD_MUTTLIKE
1655 && thrd->top)
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){
1663 mc->sequence = 1;
1664 count++;
1668 n = top_msgno;
1669 while(1){
1670 if(n >= msgno
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
1678 * in that case.
1680 if(THREADING()
1681 && ps_global->thread_disp_style == THREAD_MUTTLIKE
1682 && thrd->top)
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){
1690 mc->sequence = 1;
1691 count++;
1695 if(++i >= limit)
1696 break;
1698 /* find next n which is visible */
1699 while(++n <= mn_get_total(msgmap)
1700 && msgline_hidden(stream, msgmap, n, 0))
1705 if(count){
1706 seq = build_sequence(stream, NULL, NULL);
1707 if(seq){
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);
1725 if(!ice)
1726 return(NULL);
1729 if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1730 || (!THRD_INDX() && !ice->ifield)){
1731 INDEXDATA_S idata;
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));
1746 else
1747 idata.bogus = 2;
1749 ice = (*format_index_line)(&idata);
1750 if(!ice)
1751 return(NULL);
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)){
1763 ICE_S *working_ice;
1764 IFIELD_S *ifield;
1765 INDEX_COL_S *cdesc;
1767 if(need_format_setup(stream))
1768 setup_header_widths(stream);
1770 if(THRD_INDX())
1771 working_ice = ice ? ice->tice : NULL;
1772 else
1773 working_ice = ice;
1775 if(working_ice){
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++){
1783 if(cdesc->width){
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));
1786 assert(0);
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
1810 * search.
1812 if(!THRD_INDX() && !ice->color_lookup_done){
1813 COLOR_PAIR *linecolor;
1814 SEARCHSET *ss, *s;
1815 ICE_S *ic;
1816 PAT_STATE *pstate = NULL;
1818 if(pico_usingcolor()){
1819 if(limit < 0L){
1820 if(THREADING() && sp_viewing_a_thread(stream)){
1821 for(visible = 0L, n = top_msgno;
1822 visible < msgcount && n <= mn_get_total(msgmap);
1823 n++){
1825 if(!get_lflag(stream, msgmap, n, MN_CHID2))
1826 break;
1828 if(!msgline_hidden(stream, msgmap, n, 0))
1829 visible++;
1833 else
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)
1842 mc->sequence = 0;
1844 cnt = i = 0;
1845 n = top_msgno;
1846 while(1){
1847 if(n >= msgno
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))){
1853 mc->sequence = 1;
1854 cnt++;
1858 if(++i >= limit)
1859 break;
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.
1878 while(cnt > 0L){
1879 ss = build_searchset(stream);
1880 if(ss){
1881 int colormatch;
1883 linecolor = NULL;
1884 colormatch = get_index_line_color(stream, ss, &pstate,
1885 &linecolor);
1888 * Assign this color to all matched msgno's and
1889 * turn off the sequence bit so we won't check
1890 * for them again.
1892 if(colormatch){
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))
1897 && mc->searched){
1898 cnt--;
1899 mc->sequence = 0;
1900 ic = fetch_ice(stream, n);
1901 if(ic){
1902 ic->color_lookup_done = 1;
1903 if(linecolor)
1904 ic->linecolor = new_color_pair(linecolor->fg,
1905 linecolor->bg);
1911 if(linecolor)
1912 free_color_pair(&linecolor);
1914 else{
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))
1920 && mc->sequence){
1921 cnt--;
1922 ic = fetch_ice(stream, n);
1923 if(ic)
1924 ic->color_lookup_done = 1;
1929 /* just making sure */
1930 cnt = 0L;
1933 mail_free_searchset(&ss);
1935 else
1936 cnt = 0L;
1939 ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
1941 else
1942 ice->color_lookup_done = 1;
1945 return(ice); /* Return formatted index data */
1950 day_of_week(struct date *d)
1952 int m, y;
1954 m = d->month;
1955 y = d->year;
1956 if(m <= 2){
1957 m += 9;
1958 y--;
1960 else
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)
1975 int i, leap, doy;
1977 if(d->year <= 0 || d->month < 1 || d->month > 12)
1978 return(-1);
1980 doy = d->day;
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];
1985 return(doy);
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)
1999 ----*/
2000 ICE_S *
2001 format_index_index_line(INDEXDATA_S *idata)
2003 char str[BIGWIDTH+1], to_us, status, *field,
2004 *p, *newsgroups;
2005 int i, collapsed = 0, start, fromfield;
2006 long l, score;
2007 BODY *body = NULL;
2008 MESSAGECACHE *mc = NULL;
2009 ADDRESS *addr, *toaddr, *ccaddr, *last_to;
2010 PINETHRD_S *thrd = NULL;
2011 INDEX_COL_S *cdesc = NULL;
2012 ICE_S *ice, **icep;
2013 IFIELD_S *ifield;
2014 IELEM_S *ielem;
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);
2023 if(!ice)
2024 return(NULL);
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++)
2051 if(cdesc->width){
2052 memset(str, 0, sizeof(str));
2053 ifield = new_ifield(&ice->ifield);
2054 ifield->ctype = cdesc->ctype;
2055 ifield->width = cdesc->width;
2056 fromfield = 0;
2058 if(idata->bogus){
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 ]"));
2072 else
2073 switch(cdesc->ctype){
2074 case iStatus:
2075 to_us = status = ' ';
2076 if(collapsed){
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,
2080 cdesc->ctype);
2082 else{
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)){
2089 ice->to_us = 1;
2090 if(to_us == ' ')
2091 to_us = '+';
2093 break;
2096 if(to_us != '+' && resent_to_us(idata)){
2097 ice->to_us = 1;
2098 if(to_us == ' ')
2099 to_us = '+';
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)){
2105 ice->cc_us = 1;
2106 to_us = '-';
2107 break;
2111 status = (!idata->stream || !IS_NEWS(idata->stream)
2112 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
2113 ? 'N' : ' ';
2115 if(mc->seen)
2116 status = ' ';
2118 if(user_flag_is_set(idata->stream, idata->rawno, FORWARDED_FLAG))
2119 status = 'F';
2121 if(mc->answered)
2122 status = 'A';
2124 if(mc->deleted)
2125 status = 'D';
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';
2137 ielem->datalen = 1;
2138 set_print_format(ielem, 1, ifield->leftadj);
2141 if(pico_usingcolor()){
2143 if(str[0] == '*'){
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);
2158 if(str[2] == 'D'){
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);
2188 break;
2190 case iFStatus:
2191 case iIStatus:
2192 case iSIStatus:
2194 char new, answered, deleted, flagged;
2196 if(collapsed){
2197 thrd = fetch_thread(idata->stream, idata->rawno);
2198 to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
2200 else{
2201 to_us = ' ';
2202 if(!IS_NEWS(idata->stream)){
2203 for(addr = fetch_to(idata); addr; addr = addr->next)
2204 if(address_is_us(addr, ps_global)){
2205 to_us = '+';
2206 break;
2209 if(to_us == ' ' && resent_to_us(idata))
2210 to_us = '+';
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)){
2215 to_us = '-';
2216 break;
2221 new = answered = deleted = flagged = ' ';
2223 if(collapsed){
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;
2231 thrd->branch = 0L;
2233 tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
2234 F_NONE);
2236 cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
2237 if(cnt)
2238 deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
2240 cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
2241 if(cnt)
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))
2246 flagged = '*';
2248 new = status_symbol_for_thread(idata->stream, thrd,
2249 cdesc->ctype);
2251 thrd->branch = save_branch;
2253 else{
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){
2259 if(mc->recent)
2260 new = mc->seen ? 'R' : 'N';
2261 else if (!mc->seen)
2262 new = 'U';
2264 else if(!mc->seen
2265 && (!IS_NEWS(idata->stream)
2266 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
2267 new = 'N';
2269 if(mc->answered)
2270 answered = 'A';
2272 if(mc->deleted)
2273 deleted = 'D';
2275 if(mc->flagged)
2276 flagged = '*';
2280 snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
2281 answered, deleted);
2283 if(cdesc->ctype == iSIStatus)
2284 start = 2;
2285 else
2286 start = 0;
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';
2295 ielem->datalen = 1;
2296 set_print_format(ielem, 1, ifield->leftadj);
2299 if(pico_usingcolor()){
2301 if(str[0] == '+' || str[0] == '-'){
2302 if(start == 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);
2311 if(str[2] == '*'){
2312 if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
2313 if(start == 2)
2314 ielem = ifield->ielem;
2315 else
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){
2325 if(start == 2)
2326 ielem = ifield->ielem->next;
2327 else
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){
2336 if(start == 2)
2337 ielem = ifield->ielem->next;
2338 else
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){
2347 if(start == 2)
2348 ielem = ifield->ielem->next;
2349 else
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){
2359 if(start == 2)
2360 ielem = ifield->ielem->next->next;
2361 else
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){
2371 if(start == 2)
2372 ielem = ifield->ielem->next->next->next;
2373 else
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);
2383 break;
2385 case iMessNo:
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, " ");
2393 break;
2395 case iArrow:
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);
2409 break;
2411 case iScore:
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;
2418 if(ss){
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
2431 * parameter.)
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.
2442 idata->bogus =
2443 (calculate_some_scores(idata->stream,
2444 ss, idata->no_fetch) == 0)
2445 ? 1 : 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);
2452 break;
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:
2460 case iSDateTime:
2461 case iSDateTimeIsoS: case iSDateTimeIso:
2462 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
2463 case iSDateTime24:
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);
2472 break;
2474 case iFromTo:
2475 case iFromToNotNews:
2476 case iFrom:
2477 case iAddress:
2478 case iMailbox:
2479 fromfield++;
2480 from_str(cdesc->ctype, idata, str, sizeof(str), ice);
2481 break;
2483 case iTo:
2484 if(((field = ((addr = fetch_to(idata))
2485 ? "To"
2486 : (addr = fetch_cc(idata))
2487 ? "Cc"
2488 : NULL))
2489 && !set_index_addr(idata, field, addr, NULL, BIGWIDTH, str))
2490 || !field)
2491 if((newsgroups = fetch_newsgroups(idata)) != NULL)
2492 snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
2494 break;
2496 case iCc:
2497 set_index_addr(idata, "Cc", fetch_cc(idata), NULL, BIGWIDTH, str);
2498 break;
2500 case iRecips:
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 */
2509 if(last_to)
2510 last_to->next = ccaddr;
2512 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2514 if(last_to)
2515 last_to->next = NULL;
2517 break;
2519 case iSender:
2520 fromfield++;
2521 if((addr = fetch_sender(idata)) != NULL)
2522 set_index_addr(idata, "Sender", addr, NULL, BIGWIDTH, str);
2524 break;
2526 case iInit:
2527 {ADDRESS *addr;
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;
2540 if(name && *name){
2541 initials = reply_quote_initials(name);
2542 snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
2547 break;
2549 case iSize:
2550 /* 0 ... 9999 */
2551 if((l = fetch_size(idata)) < 10*1000L)
2552 snprintf(str, sizeof(str), "(%lu)", l);
2553 /* 10K ... 999K */
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)
2561 ? 1L : 0L);
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)
2567 ? 1L : 0L);
2568 snprintf(str, sizeof(str), "(%luM)", l);
2570 else
2571 snprintf(str, sizeof(str), "(HUGE!)");
2573 break;
2575 case iSizeComma:
2576 /* 0 ... 99,999 */
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)
2587 ? 1L : 0L);
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)
2593 ? 1L : 0L);
2594 snprintf(str, sizeof(str), "(%sM)", comatose(l));
2596 else
2597 snprintf(str, sizeof(str), "(HUGE!)");
2599 break;
2601 case iSizeNarrow:
2602 /* 0 ... 999 */
2603 if((l = fetch_size(idata)) < 1000L)
2604 snprintf(str, sizeof(str), "(%lu)", l);
2605 /* 1K ... 99K */
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);
2610 /* .1M ... .9M */
2611 else if(l < 1000L*1000L - 100L*1000L/2){
2612 l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
2613 ? 1L : 0L);
2614 snprintf(str, sizeof(str), "(.%luM)", l);
2616 /* 1M ... 99M */
2617 else if(l < 1000L*100L*1000L - 1000L*1000L/2){
2618 l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2619 ? 1L : 0L);
2620 snprintf(str, sizeof(str), "(%luM)", l);
2622 /* .1G ... .9G */
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);
2628 /* 1G ... 2G */
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);
2634 else
2635 snprintf(str, sizeof(str), "(HUGE!)");
2637 break;
2639 /* From Carl Jacobsen <carl@ucsd.edu> */
2640 case iKSize:
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);
2654 } else {
2655 snprintf(str, sizeof(str), "(HUGE!)");
2658 break;
2660 case iDescripSize:
2661 if((body = fetch_body(idata)) != NULL)
2662 switch(body->type){
2663 case TYPETEXT:
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 )");
2674 else
2675 snprintf(str, sizeof(str), "(huge )");
2678 break;
2680 case TYPEMULTIPART:
2681 if(strucmp(body->subtype, "MIXED") == 0){
2682 int x;
2684 x = body->nested.part
2685 ? body->nested.part->body.type
2686 : TYPETEXT + 1000;
2687 switch(x){
2688 case TYPETEXT:
2689 if(body->nested.part->body.size.bytes < 6000)
2690 snprintf(str, sizeof(str), "(short+ )");
2691 else if(body->nested.part->body.size.bytes
2692 < 25000)
2693 snprintf(str, sizeof(str), "(medium+)");
2694 else if(body->nested.part->body.size.bytes
2695 < 100000)
2696 snprintf(str, sizeof(str), "(long+ )");
2697 else
2698 snprintf(str, sizeof(str), "(huge+ )");
2699 break;
2701 default:
2702 snprintf(str, sizeof(str), "(multi )");
2703 break;
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)");
2712 else
2713 snprintf(str, sizeof(str), "(multi )");
2715 break;
2717 case TYPEMESSAGE:
2718 snprintf(str, sizeof(str), "(message)");
2719 break;
2721 case TYPEAPPLICATION:
2722 snprintf(str, sizeof(str), "(applica)");
2723 break;
2725 case TYPEAUDIO:
2726 snprintf(str, sizeof(str), "(audio )");
2727 break;
2729 case TYPEIMAGE:
2730 snprintf(str, sizeof(str), "(image )");
2731 break;
2733 case TYPEVIDEO:
2734 snprintf(str, sizeof(str), "(video )");
2735 break;
2737 default:
2738 snprintf(str, sizeof(str), "(other )");
2739 break;
2742 break;
2744 case iAtt:
2745 str[0] = SPACE;
2746 str[1] = '\0';
2747 if((body = fetch_body(idata)) &&
2748 body->type == TYPEMULTIPART &&
2749 strucmp(body->subtype, "ALTERNATIVE") != 0){
2750 PART *part;
2751 int atts = 0;
2753 part = body->nested.part; /* 1st part, don't count */
2754 while(part && part->next && atts < 10){
2755 atts++;
2756 part = part->next;
2759 if(atts > 9)
2760 str[0] = '*';
2761 else if(atts > 0)
2762 str[0] = '0' + atts;
2765 break;
2767 case iSubject:
2768 subj_str(idata, str, sizeof(str), NoKW, 0, 0, ice);
2769 break;
2771 case iShortSubject:
2772 subj_str(idata, str, sizeof(str), NoKW, 0, 1, ice);
2773 break;
2775 case iSubjectText:
2776 subj_str(idata, str, sizeof(str), NoKW, 1, 0, ice);
2777 break;
2779 case iSubjKey:
2780 subj_str(idata, str, sizeof(str), KW, 0, 0, ice);
2781 break;
2783 case iShortSubjKey:
2784 subj_str(idata, str, sizeof(str), KW, 0, 1, ice);
2785 break;
2787 case iSubjKeyText:
2788 subj_str(idata, str, sizeof(str), KW, 1, 0, ice);
2789 break;
2791 case iSubjKeyInit:
2792 subj_str(idata, str, sizeof(str), KWInit, 0, 0, ice);
2793 break;
2795 case iShortSubjKeyInit:
2796 subj_str(idata, str, sizeof(str), KWInit, 0, 1, ice);
2797 break;
2799 case iSubjKeyInitText:
2800 subj_str(idata, str, sizeof(str), KWInit, 1, 0, ice);
2801 break;
2803 case iOpeningText:
2804 case iOpeningTextNQ:
2805 if(idata->no_fetch)
2806 idata->bogus = 1;
2807 else{
2808 char *first_text;
2810 first_text = fetch_firsttext(idata, cdesc->ctype == iOpeningTextNQ);
2812 if(first_text){
2813 strncpy(str, first_text, BIGWIDTH);
2814 str[BIGWIDTH] = '\0';
2818 break;
2820 case iKey:
2821 key_str(idata, KW, ice);
2822 break;
2824 case iKeyInit:
2825 key_str(idata, KWInit, ice);
2826 break;
2828 case iNews:
2829 if((newsgroups = fetch_newsgroups(idata)) != NULL){
2830 strncpy(str, newsgroups, BIGWIDTH);
2831 str[BIGWIDTH] = '\0';
2834 break;
2836 case iNewsAndTo:
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);
2843 else{
2844 if(l > 0){
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);
2848 if(!str[l+5])
2849 str[l] = '\0';
2851 else
2852 set_index_addr(idata, "To", fetch_to(idata),
2853 NULL, BIGWIDTH, str);
2857 break;
2859 case iToAndNews:
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);
2866 else{
2867 if(l > 0)
2868 strncpy(str+l, " and ", sizeof(str)-l);
2870 if(l > 0)
2871 strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2872 else
2873 strncpy(str, newsgroups, BIGWIDTH);
2877 break;
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);
2886 else{
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 */
2895 if(last_to)
2896 last_to->next = ccaddr;
2898 if(l > 0){
2899 strncpy(str+l, " and ", sizeof(str)-l);
2900 set_index_addr(idata, "To", toaddr,
2901 NULL, BIGWIDTH-l-5, str+l+5);
2902 if(!str[l+5])
2903 str[l] = '\0';
2905 else
2906 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2908 if(last_to)
2909 last_to->next = NULL;
2913 break;
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 */
2924 if(last_to)
2925 last_to->next = ccaddr;
2927 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2929 if(last_to)
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);
2936 else{
2937 if(l > 0)
2938 strncpy(str+l, " and ", sizeof(str)-l);
2940 if(l > 0)
2941 strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2942 else
2943 strncpy(str, newsgroups, BIGWIDTH);
2947 break;
2949 case iPrio:
2950 case iPrioAlpha:
2951 case iPrioBang:
2952 prio_str(idata, cdesc->ctype, ice);
2953 break;
2955 case iHeader:
2956 header_str(idata, cdesc->hdrtok, ice);
2957 break;
2959 case iText:
2960 strncpy(str, (cdesc->hdrtok && cdesc->hdrtok->hdrname) ? cdesc->hdrtok->hdrname : "", sizeof(str));
2961 str[sizeof(str)-1] = '\0';
2962 break;
2964 default:
2965 break;
2969 * If the element wasn't already filled in above, do it here.
2971 if(!ifield->ielem){
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(" ");
3002 ielem->datalen = 1;
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(" ");
3021 ielem->datalen = 1;
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
3034 * real thing.
3036 icep = fetch_ice_ptr(idata->stream, idata->rawno);
3037 if(icep){
3038 free_ice(icep); /* free what is already there */
3039 *icep = ice;
3042 return(ice);
3046 ICE_S *
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;
3053 IFIELD_S *ifield;
3054 IELEM_S *ielem;
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)
3064 thdlen = 3;
3065 else if(ps_global->msgmap->max_thrdno < 10000)
3066 thdlen = 4;
3067 else if(ps_global->msgmap->max_thrdno < 100000)
3068 thdlen = 5;
3069 else
3070 thdlen = 6;
3072 ice = fetch_ice(idata->stream, idata->rawno);
3074 thrd = fetch_thread(idata->stream, idata->rawno);
3076 if(!thrd || !ice) /* can't happen? */
3077 return(ice);
3079 if(!ice->tice){
3080 tice = (ICE_S *) fs_get(sizeof(*tice));
3081 memset(tice, 0, sizeof(*tice));
3082 ice->tice = tice;
3085 tice = ice->tice;
3087 if(!tice)
3088 return(ice);
3090 free_ifield(&tice->ifield);
3092 ticep = &ice->tice;
3093 tice = copy_ice(tice);
3095 if(space_left >= 3){
3096 char to_us, status;
3098 p = buffer;
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)){
3103 p[0] = to_us;
3104 p[1] = ' ';
3105 p[2] = status;
3106 p[3] = '\0';;
3109 space_left -= 3;
3111 ifield = new_ifield(&tice->ifield);
3112 ifield->ctype = iStatus;
3113 ifield->width = 3;
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';
3121 ielem->datalen = 1;
3122 set_print_format(ielem, 1, ifield->leftadj);
3125 if(pico_usingcolor()){
3126 if(to_us == '*'
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);
3144 if(status == 'D'
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){
3162 p = buffer;
3163 space_left--;
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){
3181 p = buffer;
3182 space_left--;
3184 date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer), 0);
3185 if(sizeof(buffer) > 6)
3186 p[6] = '\0';
3188 if(strlen(p) < 6 && (sizeof(buffer)) > 6){
3189 char *q;
3191 for(q = p + strlen(p); q < p + 6; q++)
3192 *q = ' ';
3195 space_left -= 6;
3197 ifield = new_ifield(&tice->ifield);
3198 ifield->ctype = iDate;
3199 ifield->width = 6;
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);
3209 if(space_left > 3){
3210 int from_width, subj_width, bigthread_adjust;
3211 long in_thread;
3212 char from[BIGWIDTH+1];
3213 char tcnt[50];
3215 space_left--;
3217 in_thread = count_lflags_in_thread(idata->stream, thrd,
3218 ps_global->msgmap, MN_NONE);
3220 p = buffer;
3221 if(in_thread == 1 && THRD_AUTO_VIEW())
3222 snprintf(tcnt, sizeof(tcnt), " ");
3223 else
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);
3231 /* the rest */
3232 subj_width = space_left - from_width - 1;
3234 if(strlen(tcnt) > subj_width)
3235 tcnt[subj_width] = '\0';
3237 from[0] = '\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);
3272 if(subj_width > 0)
3273 subj_width--;
3275 if(subj_width > 0){
3276 if(idata->bogus){
3277 if(idata->bogus < 2)
3278 snprintf(buffer, sizeof(buffer), "%-.*s", BIGWIDTH,
3279 _("[ No Message Text Available ]"));
3281 else{
3282 buffer[0] = '\0';
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);
3322 if(ticep){
3323 free_ice(ticep); /* free what is already there */
3324 *ticep = tice;
3327 return(ice);
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.
3340 char *
3341 simple_index_line(char *buf, size_t buflen, ICE_S *ice, long int msgno)
3343 char *p;
3344 IFIELD_S *ifield, *previfield = NULL;
3345 IELEM_S *ielem;
3347 if(!buf)
3348 alpine_panic("NULL buf in simple_index_line()");
3350 if(buflen > 0)
3351 buf[0] = '\0';
3353 p = buf;
3355 if(ice){
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))
3361 *p++ = ' ';
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){
3367 if(msgno > 0L)
3368 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
3369 else
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){
3377 char *src;
3378 size_t bytes_added;
3380 src = ielem->data;
3381 bytes_added = utf8_pad_to_width(p, src,
3382 buflen-(p-buf) * sizeof(char),
3383 ielem->wid, ifield->leftadj);
3384 p += bytes_added;
3387 previfield = ifield;
3390 if(p-buf < buflen)
3391 *p = '\0';
3394 buf[buflen-1] = '\0';
3396 return(buf);
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)
3422 PAT_S *pat = NULL;
3423 long rflags = ROLE_INCOL;
3424 COLOR_PAIR *color = NULL;
3425 int match = 0;
3426 static PAT_STATE localpstate;
3428 dprint((7, "get_index_line_color\n"));
3430 if(returned_color)
3431 *returned_color = NULL;
3433 if(*pstate)
3434 pat = next_pattern(*pstate);
3435 else{
3436 *pstate = &localpstate;
3437 if(!nonempty_patterns(rflags, *pstate))
3438 *pstate = NULL;
3440 if(*pstate)
3441 pat = first_pattern(*pstate);
3444 if(*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)
3451 break;
3453 match++;
3454 if(pat->action && pat->action->incol)
3455 color = new_color_pair(pat->action->incol->fg,
3456 pat->action->incol->bg);
3458 else
3459 pat = next_pattern(*pstate);
3463 if(match && returned_color)
3464 *returned_color = color;
3466 return(match);
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")){
3483 if(THRD_INDX())
3484 return(TRUE);
3486 for(cdesc = ps_global->index_disp_format;
3487 cdesc->ctype != iNothing;
3488 cdesc++)
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 */
3493 case iAtt:
3494 return(FALSE);
3496 default :
3497 break;
3501 return(TRUE);
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};
3514 char *h;
3516 if(idata->no_fetch){
3517 idata->bogus = 1; /* don't do this */
3518 return(FALSE);
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)
3536 char *p, *q;
3537 ADDRESS *addr = NULL;
3538 int rv = FALSE;
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);
3545 if(addr){
3546 rv = address_is_us(addr, ps_global);
3547 mail_free_address(&addr);
3551 return(rv);
3557 * fetch_from - called to get a the index entry's "From:" field
3559 ADDRESS *
3560 fetch_from(INDEXDATA_S *idata)
3562 if(idata->no_fetch) /* implies from is valid */
3563 return(idata->from);
3564 else if(idata->bogus)
3565 idata->bogus = 2;
3566 else{
3567 ENVELOPE *env;
3569 /* c-client call's just cache access at this point */
3570 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3571 return(env->from);
3573 idata->bogus = 1;
3576 return(NULL);
3581 * fetch_to - called to get a the index entry's "To:" field
3583 ADDRESS *
3584 fetch_to(INDEXDATA_S *idata)
3586 if(idata->no_fetch){ /* check for specific validity */
3587 if(idata->valid_to)
3588 return(idata->to);
3589 else
3590 idata->bogus = 1; /* can't give 'em what they want */
3592 else if(idata->bogus){
3593 idata->bogus = 2; /* elevate bogosity */
3595 else{
3596 ENVELOPE *env;
3598 /* c-client call's just cache access at this point */
3599 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3600 return(env->to);
3602 idata->bogus = 1;
3605 return(NULL);
3610 * fetch_cc - called to get a the index entry's "Cc:" field
3612 ADDRESS *
3613 fetch_cc(INDEXDATA_S *idata)
3615 if(idata->no_fetch){ /* check for specific validity */
3616 if(idata->valid_cc)
3617 return(idata->cc);
3618 else
3619 idata->bogus = 1; /* can't give 'em what they want */
3621 else if(idata->bogus){
3622 idata->bogus = 2; /* elevate bogosity */
3624 else{
3625 ENVELOPE *env;
3627 /* c-client call's just cache access at this point */
3628 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3629 return(env->cc);
3631 idata->bogus = 1;
3634 return(NULL);
3640 * fetch_sender - called to get a the index entry's "Sender:" field
3642 ADDRESS *
3643 fetch_sender(INDEXDATA_S *idata)
3645 if(idata->no_fetch){ /* check for specific validity */
3646 if(idata->valid_sender)
3647 return(idata->sender);
3648 else
3649 idata->bogus = 1; /* can't give 'em what they want */
3651 else if(idata->bogus){
3652 idata->bogus = 2; /* elevate bogosity */
3654 else{
3655 ENVELOPE *env;
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);
3661 idata->bogus = 1;
3664 return(NULL);
3669 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3671 char *
3672 fetch_newsgroups(INDEXDATA_S *idata)
3674 if(idata->no_fetch){ /* check for specific validity */
3675 if(idata->valid_news)
3676 return(idata->newsgroups);
3677 else
3678 idata->bogus = 1; /* can't give 'em what they want */
3680 else if(idata->bogus){
3681 idata->bogus = 2; /* elevate bogosity */
3683 else{
3684 ENVELOPE *env;
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);
3690 idata->bogus = 1;
3693 return(NULL);
3698 * fetch_subject - called to get at the index entry's "Subject:" field
3700 char *
3701 fetch_subject(INDEXDATA_S *idata)
3703 if(idata->no_fetch) /* implies subject is valid */
3704 return(idata->subject);
3705 else if(idata->bogus)
3706 idata->bogus = 2;
3707 else{
3708 ENVELOPE *env;
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);
3714 idata->bogus = 1;
3717 return(NULL);
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.
3729 char *
3730 fetch_firsttext(INDEXDATA_S *idata, int delete_quotes)
3732 ENVELOPE *env;
3733 BODY *body = NULL, *new_body = NULL;
3734 char *firsttext = NULL;
3735 STORE_S *so;
3736 gf_io_t pc;
3737 long partial_fetch_len = 0L;
3738 SEARCHSET *ss, **sset;
3739 MESSAGECACHE *mc;
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);
3760 try_again:
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,
3769 GET_FETCHLOOKAHEAD,
3770 (void *) idata->stream);
3771 if(sset)
3772 *sset = ss;
3774 if((env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)) != NULL){
3775 if(body){
3776 char *subtype = NULL;
3777 char *partno;
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){
3795 char buf[1025], *p;
3796 unsigned char c;
3797 int success;
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;
3805 else
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)))
3817 partno = "1";
3818 else
3819 partno = "1.1";
3821 gf_set_so_writec(&pc, so);
3822 success = get_body_part_text(idata->stream, new_body ? new_body : body,
3823 idata->rawno,
3824 partno, partial_fetch_len, pc,
3825 NULL, NULL,
3826 GBPT_NOINTR | GBPT_PEEK |
3827 (delete_quotes ? GBPT_DELQUOTES : 0));
3828 gf_clear_so_writec(so);
3830 if(success){
3831 so_seek(so, 0L, 0);
3832 p = buf;
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){
3840 *p++ = SPACE;
3841 one_space_done++;
3844 else{
3845 one_space_done = 0;
3846 *p++ = c;
3850 *p = '\0';
3852 if(p > buf){
3853 size_t l;
3855 l = strlen(buf);
3856 l += 100;
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);
3865 so_give(&so);
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)){
3871 if(firsttext)
3872 fs_give((void **) &firsttext);
3874 if(ss)
3875 mail_free_searchset(&ss);
3877 partial_fetch_len = 4096L;
3878 goto try_again;
3881 if(mc && pelt)
3882 pelt->firsttext = firsttext;
3888 if(ss)
3889 mail_free_searchset(&ss);
3891 return(firsttext);
3896 * fetch_date - called to get at the index entry's "Date:" field
3898 char *
3899 fetch_date(INDEXDATA_S *idata)
3901 if(idata->no_fetch) /* implies date is valid */
3902 return(idata->date);
3903 else if(idata->bogus)
3904 idata->bogus = 2;
3905 else{
3906 ENVELOPE *env;
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);
3912 idata->bogus = 1;
3915 return(NULL);
3920 * fetch_header - called to get at the index entry's "Hdrname:" field
3922 char *
3923 fetch_header(INDEXDATA_S *idata, char *hdrname)
3925 if(idata->no_fetch)
3926 idata->bogus = 1;
3927 else if(idata->bogus)
3928 idata->bogus = 2;
3929 else{
3930 char *h, *p, *q, *decoded, *fields[2];
3931 size_t retsize, decsize;
3932 char *ret = NULL;
3933 unsigned char *decode_buf = NULL;
3935 fields[0] = hdrname;
3936 fields[1] = NULL;
3937 if(hdrname && hdrname[0]
3938 && (h = pine_fetchheader_lines(idata->stream, idata->rawno,
3939 NULL, fields))){
3941 if(strlen(h) < strlen(hdrname) + 1){
3942 fs_give((void **) &h);
3943 return(cpystr(""));
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);
3954 p = decoded;
3956 retsize = strlen(decoded);
3957 q = ret = (char *) fs_get((retsize+1) * sizeof(char));
3959 *q = '\0';
3960 while(q-ret < retsize && *p){
3961 if(*p == '\015' || *p == '\012')
3962 p++;
3963 else if(*p == '\t'){
3964 *q++ = SPACE;
3965 p++;
3967 else
3968 *q++ = *p++;
3971 *q = '\0';
3973 fs_give((void **) &h);
3974 if(decode_buf)
3975 fs_give((void **) &decode_buf);
3977 return(ret);
3980 idata->bogus = 1;
3983 return(NULL);
3988 * fetch_size - called to get at the index entry's "size" field
3990 long
3991 fetch_size(INDEXDATA_S *idata)
3993 if(idata->no_fetch) /* implies size is valid */
3994 return(idata->size);
3995 else if(idata->bogus)
3996 idata->bogus = 2;
3997 else{
3998 MESSAGECACHE *mc;
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);
4005 idata->bogus = 1;
4008 return(0L);
4013 * fetch_body - called to get a the index entry's body structure
4015 BODY *
4016 fetch_body(INDEXDATA_S *idata)
4018 BODY *body;
4020 if(idata->bogus || idata->no_fetch){
4021 idata->bogus = 2;
4022 return(NULL);
4025 if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
4026 return(body);
4028 idata->bogus = 1;
4029 return(NULL);
4034 * s is at least size width+1
4037 set_index_addr(INDEXDATA_S *idata,
4038 char *field,
4039 struct mail_address *addr,
4040 char *prefix,
4041 int width,
4042 char *s)
4044 ADDRESS *atmp;
4045 char *p, *stmp = NULL, *sptr;
4046 char *save_personal = NULL;
4047 int orig_width;
4049 s[0] = '\0';
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){
4056 idata->bogus = 1;
4057 return(TRUE);
4060 fields[0] = field;
4061 fields[1] = NULL;
4062 if((h = pine_fetchheader_lines(idata->stream, idata->rawno,
4063 NULL, fields)) != NULL){
4064 if(strlen(h) < strlen(field) + 1){
4065 p = h + strlen(h);
4067 else{
4068 /* skip "field:" */
4069 for(p = h + strlen(field) + 1;
4070 *p && isspace((unsigned char)*p); p++)
4074 orig_width = width;
4075 sptr = stmp = (char *) fs_get((orig_width+1) * sizeof(char));
4077 /* add prefix */
4078 for(pref = prefix; pref && *pref; pref++)
4079 if(width){
4080 *sptr++ = *pref;
4081 width--;
4083 else
4084 break;
4086 while(width--)
4087 if(*p == '\015' || *p == '\012')
4088 p++; /* skip CR LF */
4089 else if(!*p)
4090 *sptr++ = ' ';
4091 else if(*p == '\t'){
4092 *sptr++ = ' ';
4093 p++;
4095 else
4096 *sptr++ = *p++;
4098 *sptr = '\0'; /* tie off return string */
4100 if(stmp){
4101 iutf8ncpy(s, stmp, orig_width+1);
4102 s[orig_width] = '\0';
4103 fs_give((void **) &stmp);
4106 fs_give((void **) &h);
4107 return(TRUE);
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];
4116 int l;
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);
4128 s[width] = '\0';
4130 if(*(s+l))
4131 return(TRUE);
4132 else{
4133 save_personal = addr->personal;
4134 addr->personal = NULL;
4138 if(addr){
4139 char *a_string;
4140 int l;
4142 a_string = addr_list_string(addr, NULL, 0);
4143 if(save_personal)
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);
4150 s[width] = '\0';
4152 fs_give((void **)&a_string);
4154 return(TRUE);
4157 if(save_personal)
4158 addr->personal = save_personal;
4160 return(FALSE);
4164 void
4165 index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
4167 if(!env){
4168 idata->bogus = 2;
4169 return;
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)
4200 * dec. 21
4201 * fevr. 23
4202 * mars 7
4203 * For this monabb_width would be 5.
4205 void
4206 date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len,
4207 int monabb_width)
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 +... */
4222 int hr12;
4223 int curtype, lastmonthtype, lastyeartype, preftype;
4224 int sdatetimetype, sdatetime24type;
4225 struct date d;
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 ||
4236 type == iCurDay ||
4237 type == iCurDay2Digit ||
4238 type == iCurDayOfWeek ||
4239 type == iCurDayOfWeekAbb ||
4240 type == iCurMon ||
4241 type == iCurMon2Digit ||
4242 type == iCurMonLong ||
4243 type == iCurMonAbb ||
4244 type == iCurYear ||
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);
4281 if(str_len > 0)
4282 str[0] = '\0';
4284 if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
4285 return;
4287 if(curtype || lastmonthtype || lastyeartype){
4288 char dbuf[200];
4290 rfc822_date(dbuf);
4291 parse_date(dbuf, &d);
4293 if(lastyeartype)
4294 d.year--;
4295 else if(lastmonthtype){
4296 d.month--;
4297 if(d.month <= 0){
4298 d.month = 12;
4299 d.year--;
4303 else{
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){
4307 sdatetimetype = 0;
4308 sdatetime24type = 0;
4309 preftype = 0;
4310 switch(type){
4311 case iSDate: case iSDateTime: case iSDateTime24:
4312 type = iS1Date;
4313 break;
4315 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
4316 case iPrefDate: case iPrefTime: case iPrefDateTime:
4317 type = iDateIso;
4318 break;
4320 case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
4321 type = iDateIsoS;
4322 break;
4324 case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
4325 type = iS1Date;
4326 break;
4328 case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
4329 type = iS1Date;
4330 break;
4332 case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
4333 type = iS1Date;
4334 break;
4336 case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
4337 type = iS1Date;
4338 break;
4340 default:
4341 break;
4346 /* some special ones to start with */
4347 if(preftype){
4348 struct tm tm, *tmptr = NULL;
4349 time_t now;
4352 * Make sure we get the right one if we're using current time.
4354 if(curtype){
4355 now = time((time_t *) 0);
4356 if(now != (time_t) -1)
4357 tmptr = localtime(&now);
4360 if(!tmptr){
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);
4368 tmptr = &tm;
4371 switch(type){
4372 case iPrefDate:
4373 case iCurPrefDate:
4374 our_strftime(str, str_len, "%x", tmptr);
4375 break;
4376 case iPrefTime:
4377 case iCurPrefTime:
4378 our_strftime(str, str_len, "%X", tmptr);
4379 break;
4380 case iPrefDateTime:
4381 case iCurPrefDateTime:
4382 our_strftime(str, str_len, "%c", tmptr);
4383 break;
4384 default:
4385 assert(0);
4386 break;
4389 return;
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';
4404 strncpy(dayord,
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';
4416 if(d.year >= 0){
4417 if((d.year % 100) < 10){
4418 yearzero[0] = '0';
4419 strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
4421 else
4422 strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
4424 else
4425 strncpy(yearzero, "??", sizeof(yearzero));
4427 yearzero[sizeof(yearzero)-1] = '\0';
4429 if(d.month > 0 && d.month < 10){
4430 monzero[0] = '0';
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));
4435 else
4436 strncpy(monzero, "??", sizeof(monzero));
4438 monzero[sizeof(monzero)-1] = '\0';
4440 if(d.day > 0 && d.day < 10){
4441 dayzero[0] = '0';
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));
4446 else
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;
4453 hour12[0] = '\0';
4454 if(hr12 > 0 && hr12 <= 12)
4455 strncpy(hour12, int2string(hr12), sizeof(hour12));
4457 hour12[sizeof(hour12)-1] = '\0';
4459 hour24[0] = '\0';
4460 if(d.hour >= 0 && d.hour < 10){
4461 hour24[0] = '0';
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';
4469 minzero[0] = '\0';
4470 if(d.minute >= 0 && d.minute < 10){
4471 minzero[0] = '0';
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){
4481 timezone[0] = '-';
4482 d.hours_off_gmt *= -1;
4483 d.min_off_gmt *= -1;
4485 else
4486 timezone[0] = '+';
4488 timezone[1] = '\0';
4489 if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
4490 timezone[1] = '0';
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);
4495 else{
4496 timezone[1] = '0';
4497 timezone[2] = '0';
4500 timezone[3] = '\0';
4501 if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
4502 timezone[3] = '0';
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);
4507 else{
4508 timezone[3] = '0';
4509 timezone[4] = '0';
4512 timezone[5] = '\0';
4513 timezone[sizeof(timezone)-1] = '\0';
4516 switch(type){
4517 case iRDate:
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) ? ", " : "",
4522 day,
4523 (d.month > 0 && d.month < 13) ? month_abbrev(d.month) : "",
4524 year4);
4525 break;
4526 case iDayOfWeekAbb:
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';
4530 break;
4531 case iDayOfWeek:
4532 case iCurDayOfWeek:
4533 strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_name_locale(d.wkday) : "", str_len);
4534 str[str_len-1] = '\0';
4535 break;
4536 case iYear:
4537 case iCurYear:
4538 case iLstYear:
4539 case iLstMonYear:
4540 strncpy(str, year4, str_len);
4541 break;
4542 case iDay2Digit:
4543 case iCurDay2Digit:
4544 strncpy(str, dayzero, str_len);
4545 break;
4546 case iMon2Digit:
4547 case iCurMon2Digit:
4548 case iLstMon2Digit:
4549 strncpy(str, monzero, str_len);
4550 break;
4551 case iYear2Digit:
4552 case iCurYear2Digit:
4553 case iLstYear2Digit:
4554 case iLstMonYear2Digit:
4555 strncpy(str, yearzero, str_len);
4556 break;
4557 case iTimezone:
4558 strncpy(str, timezone, str_len);
4559 break;
4560 case iDay:
4561 case iCurDay:
4562 strncpy(str, day, str_len);
4563 break;
4564 case iDayOrdinal:
4565 snprintf(str, str_len, "%s%s", day, dayord);
4566 break;
4567 case iMon:
4568 case iCurMon:
4569 case iLstMon:
4570 if(d.month > 0 && d.month <= 12)
4571 strncpy(str, int2string(d.month), str_len);
4573 break;
4574 case iMonAbb:
4575 case iCurMonAbb:
4576 case iLstMonAbb:
4577 strncpy(str, monabb, str_len);
4578 break;
4579 case iMonLong:
4580 case iCurMonLong:
4581 case iLstMonLong:
4582 strncpy(str, (d.month > 0 && d.month < 13)
4583 ? month_name_locale(d.month) : "", str_len);
4584 break;
4585 case iDate:
4586 case iCurDate:
4587 if(v)
4588 snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
4589 else{
4590 if(monabb_width > 0)
4591 utf8_snprintf(str, str_len, "%-*.*w %2s",
4592 monabb_width, monabb_width, monabb, day);
4593 else
4594 snprintf(str, str_len, "%s %2s", monabb, day);
4597 break;
4598 case iLDate:
4599 if(v)
4600 snprintf(str, str_len, "%s%s%s%s%s", monabb,
4601 (monabb[0] && day[0]) ? " " : "", day,
4602 ((monabb[0] || day[0]) && year4[0]) ? ", " : "",
4603 year4);
4604 else{
4605 if(monabb_width > 0)
4606 utf8_snprintf(str, str_len, "%-*.*w %2s%c %4s",
4607 monabb_width, monabb_width,
4608 monabb, day,
4609 (monabb[0] && day[0] && year4[0]) ? ',' : ' ', year4);
4610 else
4611 snprintf(str, str_len, "%s %2s%c %4s", monabb, day,
4612 (monabb[0] && day[0] && year4[0]) ? ',' : ' ',
4613 year4);
4616 break;
4617 case iS1Date:
4618 case iS2Date:
4619 case iS3Date:
4620 case iS4Date:
4621 case iDateIso:
4622 case iDateIsoS:
4623 case iCurDateIso:
4624 case iCurDateIsoS:
4625 if(monzero[0] == '?' && dayzero[0] == '?' &&
4626 yearzero[0] == '?')
4627 snprintf(str, str_len, "%8s", "");
4628 else{
4629 switch(type){
4630 case iS1Date:
4631 snprintf(str, str_len, "%2s/%2s/%2s",
4632 monzero, dayzero, yearzero);
4633 break;
4634 case iS2Date:
4635 snprintf(str, str_len, "%2s/%2s/%2s",
4636 dayzero, monzero, yearzero);
4637 break;
4638 case iS3Date:
4639 snprintf(str, str_len, "%2s.%2s.%2s",
4640 dayzero, monzero, yearzero);
4641 break;
4642 case iS4Date:
4643 snprintf(str, str_len, "%2s.%2s.%2s",
4644 yearzero, monzero, dayzero);
4645 break;
4646 case iDateIsoS:
4647 case iCurDateIsoS:
4648 snprintf(str, str_len, "%2s-%2s-%2s",
4649 yearzero, monzero, dayzero);
4650 break;
4651 case iDateIso:
4652 case iCurDateIso:
4653 snprintf(str, str_len, "%4s-%2s-%2s",
4654 year4, monzero, dayzero);
4655 break;
4656 default:
4657 break;
4661 break;
4662 case iTime24:
4663 case iCurTime24:
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 : "");
4668 break;
4669 case iTime12:
4670 case iCurTime12:
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" :
4677 " ");
4678 break;
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;
4686 char dbuf[200];
4687 int msg_day_of_year, now_day_of_year, today;
4688 int diff, ydiff, last_day_of_year;
4690 rfc822_date(dbuf);
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)
4700 diff = -100;
4701 else if(ydiff == 0)
4702 diff = now_day_of_year - msg_day_of_year;
4703 else if(ydiff == 1){
4704 last_day = d;
4705 last_day.month = 12;
4706 last_day.day = 31;
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){
4713 last_day = now;
4714 last_day.month = 12;
4715 last_day.day = 31;
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));
4721 else if(ydiff > 1)
4722 diff = 100;
4723 else
4724 diff = -100;
4726 if(diff == 0)
4727 strncpy(str, _(TODAYSTR), str_len);
4728 else if(diff == 1)
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));
4732 else if(diff == -1)
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));
4737 else if(diff > 0
4738 && (ydiff == 0
4739 || (ydiff == 1 && 12 + now.month - d.month < 6))){
4740 if(v)
4741 snprintf(str, str_len, "%s%s%s", monabb,
4742 (monabb[0] && day[0]) ? " " : "", day);
4743 else{
4744 if(monabb_width > 0)
4745 utf8_snprintf(str, str_len, "%-*.*w %2s",
4746 monabb_width, monabb_width, monabb, day);
4747 else
4748 snprintf(str, str_len, "%s %2s", monabb, day);
4751 else{
4752 if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
4753 type = iSDateTimeIsoS;
4755 switch(type){
4756 case iSDate: case iSDateTime: case iSDateTime24:
4758 struct tm tm;
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);
4769 break;
4770 case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
4771 if(v)
4772 snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
4773 diff < 0 ? "!" : "");
4774 else
4775 snprintf(str, str_len, "%s%s/%s/%s%s",
4776 (mon[0] && mon[1]) ? "" : " ",
4777 mon, dayzero, yearzero,
4778 diff < 0 ? "!" : "");
4779 break;
4780 case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
4781 if(v)
4782 snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
4783 diff < 0 ? "!" : "");
4784 else
4785 snprintf(str, str_len, "%s%s/%s/%s%s",
4786 (day[0] && day[1]) ? "" : " ",
4787 day, monzero, yearzero,
4788 diff < 0 ? "!" : "");
4789 break;
4790 case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
4791 if(v)
4792 snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
4793 diff < 0 ? "!" : "");
4794 else
4795 snprintf(str, str_len, "%s%s.%s.%s%s",
4796 (day[0] && day[1]) ? "" : " ",
4797 day, monzero, yearzero,
4798 diff < 0 ? "!" : "");
4799 break;
4800 case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
4801 if(v)
4802 snprintf(str, str_len, "%s.%s.%s%s",
4803 yearzero, monzero, dayzero,
4804 diff < 0 ? "!" : "");
4805 else
4806 snprintf(str, str_len, "%s.%s.%s%s",
4807 yearzero, monzero, dayzero,
4808 diff < 0 ? "!" : "");
4809 break;
4810 case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
4811 snprintf(str, str_len, "%2s-%2s-%2s",
4812 yearzero, monzero, dayzero);
4813 break;
4814 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
4815 snprintf(str, str_len, "%4s-%2s-%2s",
4816 year4, monzero, dayzero);
4817 break;
4818 default:
4819 break;
4824 else{
4825 if(v)
4826 snprintf(str, str_len, "%s%s%s", monabb,
4827 (monabb[0] && day[0]) ? " " : "", day);
4828 else{
4829 if(monabb_width > 0)
4830 utf8_snprintf(str, str_len, "%-*.*w %2s",
4831 monabb_width, monabb_width, monabb, day);
4832 else
4833 snprintf(str, str_len, "%s %2s", monabb, day);
4838 break;
4840 default:
4841 break;
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;
4850 int daydiff;
4852 str[0] = '\0';
4853 rfc822_date(dbuf);
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){
4870 last_day = d;
4871 last_day.month = 12;
4872 last_day.day = 31;
4874 daydiff += day_of_year(&last_day);
4877 else
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);
4887 else{
4888 strncpy(hour12, "??", sizeof(hour12));
4889 hour12[sizeof(hour12)-1] = '\0';
4890 ampm = "__";
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);
4900 else{
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));
4913 else
4914 Ddd = "???";
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);
4925 else{
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);
4932 else{
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 */
4941 if(v)
4942 memmove(str, str + 1, strlen(str));
4943 else
4944 str[0] = ' ';
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;
4952 int daydiff;
4954 str[0] = '\0';
4955 rfc822_date(dbuf);
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){
4970 last_day = d;
4971 last_day.month = 12;
4972 last_day.day = 31;
4974 daydiff += day_of_year(&last_day);
4977 else
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);
4987 else{
4988 strncpy(hour12, "??", sizeof(hour12));
4989 hour12[sizeof(hour12)-1] = '\0';
4990 ampm = "__";
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));
5002 else
5003 Ddd = "???";
5005 if(d.minute >= 0 && d.minute < 60)
5006 snprintf(minzero, sizeof(minzero), "%02d", d.minute);
5007 else{
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);
5021 else{
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);
5035 else{
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);
5042 else{
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 */
5051 if(v)
5052 memmove(str, str + 1, strlen(str));
5053 else
5054 str[0] = ' ';
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
5069 void
5070 key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
5072 int firstone = 1;
5073 KEYWORD_S *kw;
5074 char *word;
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)
5088 if(!ourifield)
5089 return;
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]){
5103 UCS ucs;
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;
5124 color = NULL;
5129 if(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)){
5138 if(!firstone){
5139 ielem = new_ielem(&ourifield->ielem);
5140 ielem->freedata = 1;
5141 ielem->data = cpystr(" ");
5142 ielem->datalen = 1;
5145 firstone = 0;
5147 word = (kw->nick && kw->nick[0]) ? kw->nick :
5148 (kw->kw && kw->kw[0]) ? kw->kw : "";
5150 if(word[0]){
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;
5162 color = NULL;
5166 if(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(" ");
5182 ielem->datalen = 1;
5185 ourifield->leftadj = 1;
5186 set_ielem_widths_in_field(ourifield);
5190 void
5191 prio_str(INDEXDATA_S *idata, IndexColType ctype, ICE_S *ice)
5193 IFIELD_S *ourifield = NULL;
5194 IELEM_S *ielem = NULL;
5195 char *hdrval;
5196 PRIORITY_S *p;
5197 int v;
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)
5207 if(!ourifield)
5208 return;
5210 hdrval = fetch_header(idata, PRIORITYNAME);
5212 if(hdrval && hdrval[0] && isdigit(hdrval[0])){
5213 v = atoi(hdrval);
5214 if(v >= 1 && v <= 5 && v != 3){
5216 ielem = new_ielem(&ourifield->ielem);
5217 ielem->freedata = 1;
5219 switch(ctype){
5220 case iPrio:
5221 ielem->data = (char *) fs_get(2 * sizeof(char));
5222 ielem->data[0] = hdrval[0];
5223 ielem->data[1] = '\0';
5224 break;
5226 case iPrioAlpha:
5227 for(p = priorities; p && p->desc; p++)
5228 if(p->val == v)
5229 break;
5231 if(p && p->desc)
5232 ielem->data = cpystr(p->desc);
5234 break;
5236 case iPrioBang:
5237 ielem->data = (char *) fs_get(2 * sizeof(char));
5238 ielem->data[0] = '\0';
5239 switch(v){
5240 case 1: case 2:
5241 ielem->data[0] = '!';
5242 break;
5244 case 4: case 5:
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';
5251 break;
5254 ielem->data[1] = '\0';
5256 break;
5258 default:
5259 alpine_panic("Unhandled case in prio_str");
5260 break;
5263 if(!ielem->data)
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);
5293 void
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)
5308 if(!ourifield)
5309 return;
5311 fieldval = get_fieldval(idata, hdrtok);
5313 if(fieldval){
5314 ielem = new_ielem(&ourifield->ielem);
5315 ielem->freedata = 1;
5316 ielem->data = fieldval;
5317 ielem->datalen = strlen(fieldval);
5318 fieldval = NULL;
5319 ourifield->leftadj = (hdrtok->adjustment == Left) ? 1 : 0;
5322 set_ielem_widths_in_field(ourifield);
5326 char *
5327 get_fieldval(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok)
5329 int sep, fieldnum;
5330 char *hdrval = NULL, *testval;
5331 char *fieldval = NULL, *firstval;
5332 char *retfieldval = NULL;
5334 if(!hdrtok)
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 */
5341 fieldval = hdrval;
5342 for(fieldnum = MAX(hdrtok->fieldnum-1, 0);
5343 fieldnum > 0 && fieldval && *fieldval; fieldnum--){
5345 firstval = NULL;
5346 for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
5347 testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
5348 if(testval && (!firstval || testval < firstval))
5349 firstval = testval;
5352 fieldval = firstval;
5353 if(fieldval && *fieldval)
5354 fieldval++;
5357 /* tie off end of field */
5358 if(fieldval && *fieldval && hdrtok->fieldnum > 0){
5359 firstval = NULL;
5360 for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
5361 testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
5362 if(testval && (!firstval || testval < firstval))
5363 firstval = testval;
5366 if(firstval)
5367 *firstval = '\0';
5370 if(!fieldval)
5371 fieldval = "";
5373 retfieldval = cpystr(fieldval);
5375 if(hdrval)
5376 fs_give((void **) &hdrval);
5378 return(retfieldval);
5382 long
5383 scorevalfrommsg(MAILSTREAM *stream, MsgNo rawno, HEADER_TOK_S *hdrtok, int no_fetch)
5385 INDEXDATA_S idata;
5386 MESSAGECACHE *mc;
5387 char *fieldval = NULL;
5388 long retval = 0L;
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));
5400 else
5401 idata.bogus = 2;
5403 fieldval = get_fieldval(&idata, hdrtok);
5405 if(fieldval){
5406 retval = atol(fieldval);
5407 fs_give((void **) &fieldval);
5410 return(retval);
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
5428 void
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;
5433 char *sp;
5434 size_t len;
5435 int width = -1;
5436 int depth = 0, mult = 2;
5437 int save;
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;
5443 if(strsize <= 0)
5444 return;
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){
5451 idata->bogus = 1;
5452 return;
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';
5464 origstr = str;
5465 rawsubj = fetch_subject(idata);
5466 if(!rawsubj)
5467 rawsubj = "";
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);
5476 len = strlen(sp);
5477 len += 100; /* for possible charset, escaped characters */
5478 origsubj = fs_get((len+1) * sizeof(unsigned char));
5479 origsubj[0] = '\0';
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.
5492 if(shorten)
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,
5507 origsubj, kwtype,
5508 ourifield ? &subjielem : NULL,
5509 ps_global->VAR_KW_BRACES);
5510 free_subj = subject;
5512 else{
5513 subject = origsubj;
5514 if(ourifield){
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.
5535 if(!opening){
5536 ielem = new_ielem(&subjielem);
5537 ielem->freedata = 1;
5538 ielem->data = cpystr(" ");
5539 ielem->datalen = 1;
5542 if(!subject)
5543 subject = "";
5545 if(THREADING()
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)()
5561 && ourifield
5562 && ourifield->width > 0)
5563 width = ourifield->width;
5565 if(width < 0)
5566 width = strsize-1;
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,
5581 thd && thd->next
5582 && get_lflag(idata->stream,
5583 NULL,idata->rawno,
5584 MN_COLL));
5587 * width is < available strsize and
5588 * border points to something less than or equal
5589 * to the end of the buffer.
5592 sptr = str;
5594 if(thd)
5595 while(thd->parent &&
5596 (thd = fetch_thread(idata->stream, thd->parent)))
5597 depth++;
5599 if(depth > 0){
5600 if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
5601 mult = 1;
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);
5610 q = str + width-4;
5612 else if(width >= 5 && depth < 1000){
5613 snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
5614 q = str + width-5;
5616 else{
5617 snprintf(str, width+1, "%s", repeat_char(width, '.'));
5618 q = str;
5621 border = q;
5622 truncated_tree++;
5625 if(p < border){
5626 p[0] = ' ';
5627 if(p + 1 < border)
5628 p[1] = ' ';
5630 if(ps_global->thread_disp_style == THREAD_STRUCT
5631 || ps_global->thread_disp_style == THREAD_MUTTLIKE){
5633 * WARNING!
5634 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5635 * is ascii.
5637 if(thd == thdorig && !thd->branch)
5638 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
5639 else if(thd == thdorig || thd->branch)
5640 p[0] = '|';
5642 if(p + 1 < border && thd == thdorig)
5643 p[1] = '-';
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){
5665 if(depth == 0)
5666 do_subj++;
5667 else{
5668 if(thdorig->parent &&
5669 (thd = fetch_thread(idata->stream, thdorig->parent))
5670 && thd->rawno){
5671 char *this_orig = NULL,
5672 *prev_orig = NULL,
5673 *free_prev_orig = NULL,
5674 *this_prep = NULL, /* includes prepend */
5675 *prev_prep = NULL;
5676 ENVELOPE *env;
5677 mailcache_t mc;
5678 SORTCACHE *sc = NULL;
5680 /* get the stripped subject of previous message */
5681 mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
5682 if(mc)
5683 sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
5685 if(sc && sc->subject)
5686 prev_orig = sc->subject;
5687 else{
5688 char *stripthis;
5690 env = pine_mail_fetchenvelope(idata->stream,
5691 thd->rawno);
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,
5704 thd->rawno,
5705 prev_orig,
5706 kwtype, NULL,
5707 ps_global->VAR_KW_BRACES);
5709 this_prep = prepend_keyword_subject(idata->stream,
5710 idata->rawno,
5711 this_orig,
5712 kwtype, NULL,
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)))
5719 do_subj++;
5721 else{
5722 if((this_orig || prev_orig)
5723 && ((this_orig && !prev_orig)
5724 || (prev_orig && !this_orig)
5725 || strucmp(this_orig, prev_orig)))
5726 do_subj++;
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.
5754 if(!do_subj){
5755 while(thd){
5756 if(!msgline_hidden(idata->stream,
5757 sp_msgmap(idata->stream),
5758 mn_raw2m(sp_msgmap(idata->stream),
5759 (long) thd->rawno),
5760 0)){
5761 break; /* found a visible parent */
5764 if(thd && thd->parent)
5765 thd = fetch_thread(idata->stream,thd->parent);
5766 else
5767 thd = NULL;
5770 if(!thd) /* none were visible */
5771 do_subj++;
5774 if(this_orig)
5775 fs_give((void **) &this_orig);
5777 if(this_prep)
5778 fs_give((void **) &this_prep);
5780 if(free_prev_orig)
5781 fs_give((void **) &free_prev_orig);
5783 if(prev_prep)
5784 fs_give((void **) &prev_prep);
5786 else
5787 do_subj++;
5790 else
5791 do_subj++;
5793 if(do_subj){
5795 * We don't need to worry about truncating to width
5796 * here. If we go over the right hand edge it will be
5797 * truncated.
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);
5807 if(strsize > 0){
5808 sptr[0] = '>';
5809 sptr++;
5813 * We decided we don't need the subject so we'd better
5814 * eliminate subjielem.
5816 free_ielem(&subjielem);
5819 else
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;
5826 save = *sptr;
5827 *sptr = '\0';
5828 ielem->data = cpystr(origstr);
5829 ielem->datalen = strlen(origstr);
5830 *sptr = save;
5833 else{
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';
5842 if(ourifield){
5844 * We need to add subjielem to the end of the ourifield->ielem list.
5846 if(subjielem){
5847 if(ourifield->ielem){
5848 for(ielem = ourifield->ielem;
5849 ielem && ielem->next; ielem = ielem->next)
5852 ielem->next = subjielem;
5854 else
5855 ourifield->ielem = subjielem;
5858 ourifield->leftadj = 1;
5861 if(opening && ourifield){
5862 IELEM_S *ftielem = NULL;
5863 size_t len;
5864 char *first_text;
5866 first_text = fetch_firsttext(idata, 0);
5868 if(first_text){
5869 char sep[200];
5870 int seplen;
5872 strncpy(sep, ps_global->VAR_OPENING_SEP ? ps_global->VAR_OPENING_SEP : " - ",
5873 sizeof(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);
5890 if(ftielem){
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(" ");
5906 ielem->datalen = 1;
5909 if(ourifield->ielem){
5910 for(ielem = ourifield->ielem;
5911 ielem && ielem->next; ielem = ielem->next)
5914 ielem->next = ftielem;
5916 else
5917 ourifield->ielem = ftielem;
5920 ourifield->leftadj = 1;
5924 if(ourifield)
5925 set_ielem_widths_in_field(ourifield);
5927 if(origsubj)
5928 fs_give((void **) &origsubj);
5930 if(free_subj)
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
5948 * {AB} subject
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
5952 * than two.
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
5956 * be set.
5958 char *
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;
5964 size_t len;
5965 int some_set = 0, save;
5966 IELEM_S *ielem;
5967 KEYWORD_S *kw;
5968 COLOR_PAIR *color = NULL;
5969 SPEC_COLOR_S *sc = ps_global->kw_colors;
5971 if(!subject)
5972 subject = "";
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)){
5983 if(kwtype == KW){
5984 if(some_set)
5985 len++; /* space between keywords */
5987 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
5988 len += strlen(str);
5990 else if(kwtype == KWInit){
5991 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
5992 /* interested in only the first UTF-8 initial */
5993 if(str && str[0]){
5994 UCS ucs;
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);
6007 some_set++;
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)){
6019 if(p == retsubj){
6020 if(left_brace && len > 0)
6021 sstrncpy(&p, left_brace, len);
6023 else if(kwtype == KW)
6024 *p++ = ' ';
6026 if(ielemp && p > next_piece){
6027 save = *p;
6028 *p = '\0';
6029 ielem = new_ielem(ielemp);
6030 ielem->freedata = 1;
6031 ielem->data = cpystr(next_piece);
6032 ielem->datalen = strlen(next_piece);
6033 *p = save;
6034 next_piece = p;
6037 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
6039 if(kwtype == KWInit){
6040 if(str && str[0]){
6041 UCS ucs;
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;
6058 save = *p;
6059 *p = '\0';
6060 ielem->data = cpystr(next_piece);
6061 ielem->datalen = strlen(next_piece);
6062 ielem->color = color;
6063 color = NULL;
6064 *p = save;
6065 next_piece = p;
6070 if(color)
6071 free_color_pair(&color);
6074 else{
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;
6085 save = *p;
6086 *p = '\0';
6087 ielem->data = cpystr(next_piece);
6088 ielem->datalen = strlen(next_piece);
6089 ielem->color = color;
6090 color = NULL;
6091 *p = save;
6092 next_piece = p;
6095 if(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){
6105 save = *p;
6106 *p = '\0';
6107 ielem = new_ielem(ielemp);
6108 ielem->freedata = 1;
6109 ielem->data = cpystr(next_piece);
6110 ielem->datalen = strlen(next_piece);
6111 *p = save;
6112 next_piece = p;
6115 if(len-(p-retsubj) > 0 && subject)
6116 sstrncpy(&p, subject, len-(p-retsubj));
6118 if(ielemp && p > next_piece){
6119 save = *p;
6120 *p = '\0';
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);
6126 *p = save;
6127 next_piece = p;
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 */
6138 else{
6139 if(ielemp){
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);
6156 if(braces){
6157 if(left_brace)
6158 fs_give((void **) &left_brace);
6160 if(right_brace)
6161 fs_give((void **) &right_brace);
6164 return(retsubj);
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.
6172 void
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;
6176 ADDRESS *addr;
6177 int width = -1;
6178 int depth = 0, mult = 2;
6179 PINETHRD_S *thd, *thdorig;
6181 if(THREADING()
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;
6201 if(width < 0)
6202 width = strsize-1;
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,
6210 thd && thd->next
6211 && get_lflag(idata->stream,
6212 NULL,idata->rawno,
6213 MN_COLL));
6215 fptr = str;
6217 if(thd)
6218 while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
6219 depth++;
6221 if(depth > 0){
6222 if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
6223 mult = 1;
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);
6232 q = str + width-4;
6234 else if(width >= 5 && depth < 1000){
6235 snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
6236 q = str + width-5;
6238 else{
6239 snprintf(str, width+1, "%s", repeat_char(width, '.'));
6240 q = str;
6243 border = q;
6244 fptr = NULL;
6247 if(p + 1 < border){
6248 p[0] = p[1] = ' ';
6249 if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
6251 * WARNING!
6252 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6253 * is ascii.
6255 if(thd == thdorig && !thd->branch)
6256 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
6257 else if(thd == thdorig || thd->branch)
6258 p[0] = '|';
6260 if(thd == thdorig)
6261 p[1] = '-';
6264 else if(p < border){
6265 p[0] = ' ';
6266 if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
6268 * WARNING!
6269 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6270 * is ascii.
6272 if(thd == thdorig && !thd->branch)
6273 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
6274 else if(thd == thdorig || thd->branch)
6275 p[0] = '|';
6281 else
6282 fptr = str;
6284 if(fptr){
6285 strsize -= (fptr - str);
6286 switch(ctype){
6287 case iFromTo:
6288 case iFromToNotNews:
6289 if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
6290 if(strsize-1 <= 4){
6291 strncpy(fptr, "To: ", strsize-1);
6292 fptr[strsize-1] = '\0';
6293 break;
6295 else{
6296 if((field = ((addr = fetch_to(idata))
6297 ? "To"
6298 : (addr = fetch_cc(idata))
6299 ? "Cc"
6300 : NULL))
6301 && set_index_addr(idata, field, addr, "To: ",
6302 strsize-1, fptr))
6303 break;
6305 if(ctype == iFromTo &&
6306 (newsgroups = fetch_newsgroups(idata)) &&
6307 *newsgroups){
6308 snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4),
6309 newsgroups);
6310 break;
6313 /* else fall thru to From: */
6316 /* else fall thru to From: */
6318 if(idata->bogus)
6319 break;
6321 case iFrom:
6322 set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
6323 break;
6325 case iAddress:
6326 case iMailbox:
6327 if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
6328 char *mb = NULL, *hst = NULL, *at = NULL;
6329 size_t len;
6331 mb = addr->mailbox;
6332 if(ctype == iAddress && addr->host && addr->host[0]
6333 && addr->host[0] != '.'){
6334 at = "@";
6335 hst = addr->host;
6338 len = strlen(mb);
6339 if(!at || strsize-1 <= len)
6340 snprintf(fptr, strsize, "%-*.*s", (int)(strsize-1), (int)(strsize-1), mb);
6341 else
6342 snprintf(fptr, strsize, "%s@%-*.*s", mb, (int)(strsize-1-len-1), (int)(strsize-1-len-1), hst);
6345 break;
6347 default:
6348 break;
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.
6359 void
6360 set_ielem_widths_in_field(IFIELD_S *ifield)
6362 IELEM_S *ielem = NULL;
6363 int datawidth, fmtwidth;
6365 if(!ifield)
6366 return;
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));
6373 assert(0);
6376 datawidth = (int) utf8_width(ielem->data);
6377 if(datawidth >= fmtwidth || !ielem->next){
6378 set_print_format(ielem, fmtwidth, ifield->leftadj);
6379 fmtwidth = 0;
6381 else{
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.
6396 unsigned long
6397 line_hash(char *s)
6399 unsigned long hashval;
6401 for(hashval = 0; *s != '\0'; s++)
6402 hashval = *s + 31 * hashval;
6404 hashval = hashval % LINE_HASH_N;
6406 if(!hashval)
6407 hashval++;
6409 return(hashval);
6414 * Returns nonzero if considered hidden, 0 if not considered hidden.
6417 msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
6419 int ret;
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));
6429 else{
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 */
6443 ret = 1;
6444 else{
6445 unsigned long rawno;
6446 PINETHRD_S *thrd = NULL;
6448 rawno = mn_m2raw(msgmap, msgno);
6449 if(rawno)
6450 thrd = fetch_thread(stream, rawno);
6452 ret = !thread_has_some_visible(stream, thrd);
6455 else{
6456 ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
6457 && get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
6461 dprint((10,
6462 "msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
6464 return(ret);
6468 void
6469 adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
6471 long n, cur;
6472 int dir;
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;
6482 for(n = cur;
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);
6486 n -= dir)
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 */
6493 for(n = cur;
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);
6497 n += dir)
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);
6503 /* else trouble! */
6509 void
6510 setup_for_index_index_screen(void)
6512 format_index_line = format_index_index_line;
6513 setup_header_widths = setup_index_header_widths;
6517 void
6518 setup_for_thread_index_screen(void)
6520 format_index_line = format_thread_index_line;
6521 setup_header_widths = setup_thread_header_widths;
6525 unsigned long
6526 ice_hash(ICE_S *ice)
6528 char buf[MAX_SCREEN_COLS+1];
6530 buf[0] = '\0';
6532 if(ice)
6533 simple_index_line(buf, sizeof(buf), ice, 0L);
6535 buf[sizeof(buf) - 1] = '\0';
6537 return(line_hash(buf));
6541 char *
6542 left_adjust(int width)
6544 return(format_str(width, 1));
6548 char *
6549 right_adjust(int width)
6551 return(format_str(width, 0));
6556 * Returns allocated and filled in format string.
6558 char *
6559 format_str(int width, int left)
6561 char *format;
6562 size_t len;
6564 len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
6565 format = (char *) fs_get(len + 1);
6566 copy_format_str(width, left, format, len);
6567 format[len] = '\0';
6569 return(format);
6574 * Put the left or right adjusted format string of width width into
6575 * dest. Dest is of size n+1.
6577 char *
6578 copy_format_str(int width, int left, char *dest, int n)
6580 char *p;
6582 p = int2string(width);
6584 snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
6586 dest[n] = '\0';
6588 return(dest);
6593 * Sets up the print_format string to be width wide with left or right
6594 * adjust. Takes care of memory freeing and allocation.
6596 void
6597 set_print_format(IELEM_S *ielem, int width, int leftadj)
6599 if(ielem){
6600 ielem->wid = width;
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));
6613 else{
6614 ielem->print_format = leftadj ? left_adjust(width)
6615 : right_adjust(width);
6616 ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);