* New token SHORTSUBJECT, SHORTSUBJKEY and SHORTSUBJKEYINIT to shorten
[alpine.git] / pith / mailindx.c
blobb79677a5b6586969e4409518d5be83c889166eb6
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /* ========================================================================
6 * Copyright 2013-2016 Eduardo Chappa
7 * Copyright 2006-2008 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
18 #include "../pith/headers.h"
19 #include "../pith/mailindx.h"
20 #include "../pith/mailview.h"
21 #include "../pith/flag.h"
22 #include "../pith/icache.h"
23 #include "../pith/msgno.h"
24 #include "../pith/thread.h"
25 #include "../pith/strlst.h"
26 #include "../pith/status.h"
27 #include "../pith/mailcmd.h"
28 #include "../pith/search.h"
29 #include "../pith/charset.h"
30 #include "../pith/reply.h"
31 #include "../pith/bldaddr.h"
32 #include "../pith/addrstring.h"
33 #include "../pith/news.h"
34 #include "../pith/util.h"
35 #include "../pith/pattern.h"
36 #include "../pith/sequence.h"
37 #include "../pith/color.h"
38 #include "../pith/stream.h"
39 #include "../pith/string.h"
40 #include "../pith/send.h"
41 #include "../pith/options.h"
42 #include "../pith/ablookup.h"
43 #ifdef _WINDOWS
44 #include "../pico/osdep/mswin.h"
45 #endif
48 * pointers to formatting functions
50 ICE_S *(*format_index_line)(INDEXDATA_S *);
51 void (*setup_header_widths)(MAILSTREAM *);
54 * pointer to optional load_overview functionality
56 void (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
59 * pointer to hook for saving index format state
61 void (*pith_opt_save_index_state)(int);
64 * hook to allow caller to insert cue that indicates a condensed
65 * thread relationship cue
67 int (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, size_t *, int, int);
68 int (*pith_opt_truncate_sfstr)(void);
72 * Internal prototypes
74 void setup_for_thread_index_screen(void);
75 ICE_S *format_index_index_line(INDEXDATA_S *);
76 ICE_S *format_thread_index_line(INDEXDATA_S *);
77 int set_index_addr(INDEXDATA_S *, char *, ADDRESS *, char *, int, char *);
78 int ctype_is_fixed_length(IndexColType);
79 void setup_index_header_widths(MAILSTREAM *);
80 void setup_thread_header_widths(MAILSTREAM *);
81 int parse_index_format(char *, INDEX_COL_S **);
82 int index_in_overview(MAILSTREAM *);
83 ADDRESS *fetch_from(INDEXDATA_S *);
84 ADDRESS *fetch_sender(INDEXDATA_S *);
85 char *fetch_newsgroups(INDEXDATA_S *);
86 char *fetch_subject(INDEXDATA_S *);
87 char *fetch_date(INDEXDATA_S *);
88 long fetch_size(INDEXDATA_S *);
89 BODY *fetch_body(INDEXDATA_S *);
90 char *fetch_firsttext(INDEXDATA_S *idata, int);
91 char *fetch_header(INDEXDATA_S *idata, char *hdrname);
92 void subj_str(INDEXDATA_S *, char *, size_t, SubjKW, int, int, ICE_S *);
93 void key_str(INDEXDATA_S *, SubjKW, ICE_S *);
94 void header_str(INDEXDATA_S *, HEADER_TOK_S *, ICE_S *);
95 void prio_str(INDEXDATA_S *, IndexColType, ICE_S *);
96 void from_str(IndexColType, INDEXDATA_S *, char *, size_t, ICE_S *);
97 int day_of_week(struct date *);
98 int day_of_year(struct date *);
99 unsigned long ice_hash(ICE_S *);
100 char *left_adjust(int);
101 char *right_adjust(int);
102 char *format_str(int, int);
103 char *copy_format_str(int, int, char *, int);
104 void set_print_format(IELEM_S *, int, int);
105 void set_ielem_widths_in_field(IFIELD_S *);
108 #define BIGWIDTH 2047
111 /*----------------------------------------------------------------------
112 Initialize the index_disp_format array in ps_global from this
113 format string.
115 Args: format -- the string containing the format tokens
116 answer -- put the answer here, free first if there was a previous
117 value here
118 ----*/
119 void
120 init_index_format(char *format, INDEX_COL_S **answer)
122 char *p;
123 int i, w, monabb_width = 0, column = 0;
126 * Record the fact that SCORE appears in some index format. This
127 * is a heavy-handed approach. It will stick at 1 if any format ever
128 * contains score during this session. This is ok since it will just
129 * cause recalculation if wrong and these things rarely change much.
131 if(!ps_global->a_format_contains_score && format
132 && strstr(format, "SCORE")){
133 ps_global->a_format_contains_score = 1;
134 /* recalculate need for scores */
135 scores_are_used(SCOREUSE_INVALID);
138 set_need_format_setup(ps_global->mail_stream);
139 /* if custom format is specified, try it, else go with default */
140 if(!(format && *format && parse_index_format(format, answer))){
141 static INDEX_COL_S answer_default[] = {
142 {iStatus, Fixed, 3},
143 {iMessNo, WeCalculate},
144 {iSDateTime24, WeCalculate},
145 {iFromTo, Percent, 33}, /* percent of rest */
146 {iSizeNarrow, WeCalculate},
147 {iSubjKey, Percent, 67},
148 {iNothing}
151 if(*answer)
152 free_index_format(answer);
154 *answer = (INDEX_COL_S *)fs_get(sizeof(answer_default));
155 memcpy(*answer, answer_default, sizeof(answer_default));
159 * Test to see how long the month abbreviations are.
161 for(i = 1; i <= 12; i++){
162 p = month_abbrev_locale(i);
163 monabb_width = MAX(utf8_width(p), monabb_width);
166 monabb_width = MIN(MAX(2, monabb_width), 5);
169 * Fill in req_width's for WeCalculate items.
171 for(column = 0; (*answer)[column].ctype != iNothing; column++){
173 /* don't use strftime if we're not trying to use the LC_TIME stuff */
174 if(F_ON(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
175 switch((*answer)[column].ctype){
176 case iSDate:
177 (*answer)[column].ctype = iS1Date;
178 break;
179 case iSDateTime:
180 (*answer)[column].ctype = iSDateTimeS1;
181 break;
182 case iSDateTime24:
183 (*answer)[column].ctype = iSDateTimeS124;
184 break;
185 default:
186 break;
190 if((*answer)[column].wtype == WeCalculate){
191 switch((*answer)[column].ctype){
192 case iPrio:
193 case iPrioBang:
194 case iAtt:
195 (*answer)[column].req_width = 1;
196 break;
197 case iYear2Digit:
198 case iDay:
199 case iMon:
200 case iDay2Digit:
201 case iMon2Digit:
202 case iArrow:
203 case iKeyInit:
204 (*answer)[column].req_width = 2;
205 break;
206 case iStatus:
207 case iMessNo:
208 case iInit:
209 (*answer)[column].req_width = 3;
210 break;
211 case iYear:
212 case iDayOrdinal:
213 case iSIStatus:
214 (*answer)[column].req_width = 4;
215 break;
216 case iTime24:
217 case iTimezone:
218 case iSizeNarrow:
219 case iKey:
220 (*answer)[column].req_width = 5;
221 break;
222 case iFStatus:
223 case iIStatus:
224 case iScore:
225 (*answer)[column].req_width = 6;
226 break;
227 case iTime12:
228 case iSTime:
229 case iKSize:
230 case iSize:
231 case iPrioAlpha:
232 (*answer)[column].req_width = 7;
233 break;
234 case iS1Date:
235 case iS2Date:
236 case iS3Date:
237 case iS4Date:
238 case iDateIsoS:
239 case iSizeComma:
240 (*answer)[column].req_width = 8;
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 {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
489 {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
490 {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
491 {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
492 {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
493 {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
494 {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
495 {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
496 {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
497 {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
498 {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
499 {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
500 {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
501 {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
502 {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
503 {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
504 {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
505 {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
506 {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
507 {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
508 {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
509 {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
510 {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
511 {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
512 {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
513 {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
514 {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
515 {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
516 {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
517 {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
518 {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
519 {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
520 {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
521 {"NEWSANDTO", iNewsAndTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
522 {"RECIPSANDNEWS", iRecipsAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
523 {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
524 {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE},
525 {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE},
526 {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
527 {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
528 {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
529 {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
530 {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
531 {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
532 {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
533 {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
534 {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
535 {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
536 {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
537 {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
538 {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
539 {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
540 {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE},
541 {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
542 {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
543 {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
544 {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
545 {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
546 {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
547 {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
548 {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
549 {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
550 {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
551 FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
552 {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
553 {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
554 {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
555 {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
556 {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
557 {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
558 {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
559 {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
560 {"CURPREFDATETIME", iCurPrefDateTime,
561 FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
562 {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
563 {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
564 {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
565 {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
566 {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
567 {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
568 FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
569 {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
570 {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
571 {"HEADER", iHeader, FOR_INDEX},
572 {"TEXT", iText, FOR_INDEX},
573 {"ARROW", iArrow, FOR_INDEX},
574 {"NEWLINE", iNewLine, FOR_REPLY_INTRO},
575 {"CURSORPOS", iCursorPos, FOR_TEMPLATE},
576 {NULL, iNothing, FOR_NOTHING}
579 INDEX_PARSE_T itokensinv[sizeof(itokens)/sizeof(itokens[0])];
581 void
582 inverse_itokens(void)
584 INDEX_PARSE_T *pt;
585 for (pt = itokens; pt->name; pt++)
586 itokensinv[pt->ctype].ctype = pt - itokens;
590 INDEX_PARSE_T *
591 itoken(int i)
593 return((i < sizeof(itokens) && itokens[i].name) ? &itokens[i] : NULL);
598 * Args txt -- The token being checked begins at the beginning
599 * of txt. The end of the token is delimited by a null, or
600 * white space, or an underscore if DELIM_USCORE is set,
601 * or a left paren if DELIM_PAREN is set.
602 * flags -- Flags contains the what_for value, and DELIM_ values.
604 * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
606 INDEX_PARSE_T *
607 itoktype(char *txt, int flags)
609 INDEX_PARSE_T *pt;
610 char token[100 + 1];
611 char *v, *w;
614 * Separate a copy of the possible token out of txt.
616 v = txt;
617 w = token;
618 while(w < token+sizeof(token)-1 &&
619 *v &&
620 !(!(*v & 0x80) && isspace((unsigned char)*v)) &&
621 !(flags & DELIM_USCORE && *v == '_') &&
622 !(flags & DELIM_PAREN && *v == '(') &&
623 !(flags & DELIM_COLON && *v == ':'))
624 *w++ = *v++;
626 *w = '\0';
628 for(pt = itokens; pt->name; pt++)
629 if(pt->what_for & flags && !strucmp(pt->name, token))
630 return(pt);
632 return(NULL);
637 parse_index_format(char *format_str, INDEX_COL_S **answer)
639 int i, column = 0;
640 char *p, *q;
641 INDEX_PARSE_T *pt;
642 INDEX_COL_S cdesc[200]; /* plenty of temp storage for answer */
644 memset((void *)cdesc, 0, sizeof(cdesc));
646 p = format_str;
647 while(p && *p && column < 200-1){
648 /* skip leading white space for next word */
649 p = skip_white_space(p);
650 pt = itoktype(p, FOR_INDEX | DELIM_PAREN | DELIM_COLON);
652 /* ignore unrecognized word */
653 if(!pt){
654 for(q = p; *p && !isspace((unsigned char)*p); p++)
657 if(*p)
658 *p++ = '\0';
660 dprint((1,
661 "parse_index_format: unrecognized token: %s\n",
662 q ? q : "?"));
663 q_status_message1(SM_ORDER | SM_DING, 0, 3,
664 _("Unrecognized word in index-format: %s"), q);
665 continue;
668 cdesc[column].ctype = pt->ctype;
670 if(pt->ctype == iHeader || pt->ctype == iText){
672 * iHeader field has special syntax.
674 * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
676 * where width is the regular width or percentage width or
677 * left out for default width, fieldnum defaults to 0 for
678 * whole thing, 1 for first field, ...
679 * and field_separators is a list of characters which separate
680 * the fields. The whole parenthesized part is optional. If used
681 * the arguments can be dropped from the right, so
683 * HEADER:hdrname or
684 * HEADER:hdrname(10) or
685 * HEADER:hdrname(10%) or
686 * HEADER:hdrname(10,2) or
687 * HEADER:hdrname(,2) or
688 * HEADER:hdrname(10,2, ) or
689 * HEADER:hdrname(10,2,\,:) or
690 * HEADER:hdrname(10,2,\,:,R)
692 * iText field uses the hdrtok field for convenience. It has syntax
694 * TEXT:text or
695 * TEXT:text(10) or
696 * TEXT:text(10%)
698 * and the literal text goes into the index line. It is also special
699 * because there is no 1 column space after this field.
702 /* skip over name */
703 p += strlen(pt->name);
705 /* look for header name */
706 if(*p == ':'){
707 char *w, hdrname[200];
709 hdrname[0] = '\0';
710 w = hdrname;
711 p++;
712 if(*p == '\"'){ /* quoted name */
713 p++;
714 while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
715 if(*p == '\\')
716 p++;
718 *w++ = *p++;
721 *w = '\0';
722 if(*p == '\"')
723 p++;
725 else{
726 while(w < hdrname + sizeof(hdrname)-1 &&
727 !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
728 *p != '(')
729 *w++ = *p++;
731 *w = '\0';
734 if(hdrname[0]){
735 cdesc[column].hdrtok = new_hdrtok(hdrname);
737 else{
738 if(pt->ctype == iHeader){
739 dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
740 q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
742 else{
743 dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
744 q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
748 else{
749 if(pt->ctype == iHeader){
750 dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p));
751 q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
753 else{
754 dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p));
755 q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
758 /* skip over rest of bogus config */
759 while(!(!(*p & 0x80) && isspace((unsigned char)*p)) && *p != '(')
760 p++;
763 else{
764 /* skip over name and look for parens */
765 p += strlen(pt->name);
768 if(*p == '('){
769 p++;
770 q = p;
771 while(p && *p && isdigit((unsigned char) *p))
772 p++;
774 if(pt->ctype == iHeader){
775 /* first argument is width or width percentage, like for others */
776 if(p && *p && (*p == ')' || *p == ',')){
777 if(p > q){
778 cdesc[column].wtype = Fixed;
779 cdesc[column].req_width = atoi(q);
781 else{
782 cdesc[column].wtype = WeCalculate;
783 cdesc[column].req_width = 0;
786 else if(p && *p && *p == '%' && p > q){
787 cdesc[column].wtype = Percent;
788 cdesc[column].req_width = atoi(q);
789 p++;
791 else{
792 cdesc[column].wtype = WeCalculate;
793 cdesc[column].req_width = 0;
796 /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
797 if(p && *p && *p == ','){
798 p++;
799 /* no space allowed between arguments */
800 if(*p && isdigit((unsigned char) *p)){
801 q = p;
802 while(*p && isdigit((unsigned char) *p))
803 p++;
805 cdesc[column].hdrtok->fieldnum = atoi(q);
808 * Optional 3rd argument is field separators.
809 * Comma is \, and backslash is \\.
811 if(*p == ','){
812 int j;
814 p++;
815 /* don't use default */
816 if(*p && *p != ')' && *p != ',' && cdesc[column].hdrtok->fieldseps)
817 cdesc[column].hdrtok->fieldseps[0] = '\0';
819 j = 0;
820 if(*p == '\"' && strchr(p+1, '\"')){
821 p++;
822 while(*p && *p != ')' && *p != '\"' && *p != ','){
823 if(cdesc[column].hdrtok->fieldseps)
824 fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
826 if(*p == '\\' && *(p+1))
827 p++;
829 if(cdesc[column].hdrtok->fieldseps){
830 cdesc[column].hdrtok->fieldseps[j++] = *p++;
831 cdesc[column].hdrtok->fieldseps[j] = '\0';
832 cdesc[column].hdrtok->fieldsepcnt = j;
836 if(*p == '\"')
837 p++;
839 else{
840 while(*p && *p != ')' && *p != ','){
841 if(cdesc[column].hdrtok->fieldseps)
842 fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
843 if(*p == '\\' && *(p+1))
844 p++;
846 if(cdesc[column].hdrtok->fieldseps){
847 cdesc[column].hdrtok->fieldseps[j++] = *p++;
848 cdesc[column].hdrtok->fieldseps[j] = '\0';
849 cdesc[column].hdrtok->fieldsepcnt = j;
854 /* optional 4th argument, left or right adjust */
855 if(*p == ','){
856 p++;
857 if(*p == 'L' || *p == 'l')
858 cdesc[column].hdrtok->adjustment = Left;
859 else if(*p == 'R' || *p == 'r')
860 cdesc[column].hdrtok->adjustment = Right;
861 else{
862 dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p ? p : "<null>"));
863 q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 4th argument should be L or R");
868 else{
869 dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p ? p : "<null>"));
870 q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
874 else{
875 if(p && *p && *p == ')' && p > q){
876 cdesc[column].wtype = Fixed;
877 cdesc[column].req_width = atoi(q);
879 else if(p && *p && *p == '%' && p > q){
880 cdesc[column].wtype = Percent;
881 cdesc[column].req_width = atoi(q);
883 else{
884 cdesc[column].wtype = WeCalculate;
885 cdesc[column].req_width = 0;
889 else{
890 /* if they left out width for iText we can figure it out */
891 if(pt->ctype == iText && cdesc[column].hdrtok && cdesc[column].hdrtok->hdrname){
892 cdesc[column].wtype = Fixed;
893 cdesc[column].req_width = utf8_width(cdesc[column].hdrtok->hdrname);
895 else{
896 cdesc[column].wtype = WeCalculate;
897 cdesc[column].req_width = 0;
901 column++;
902 /* skip text at end of word */
903 while(p && *p && !isspace((unsigned char)*p))
904 p++;
907 /* if, after all that, we didn't find anything recognizable, bitch */
908 if(!column){
909 dprint((1, "Completely unrecognizable index-format\n"));
910 q_status_message(SM_ORDER | SM_DING, 0, 3,
911 _("Configured \"index-format\" unrecognizable. Using default."));
912 return(0);
915 /* Finish with Nothing column */
916 cdesc[column].ctype = iNothing;
918 /* free up old answer */
919 if(*answer)
920 free_index_format(answer);
922 /* allocate space for new answer */
923 *answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
924 memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
925 /* copy answer to real place */
926 for(i = 0; i <= column; i++)
927 (*answer)[i] = cdesc[i];
929 return(1);
934 * These types are basically fixed in width.
935 * The order is slightly significant. The ones towards the front of the
936 * list get space allocated sooner than the ones at the end of the list.
938 static IndexColType fixed_ctypes[] = {
939 iMessNo, iStatus, iFStatus, iIStatus, iSIStatus,
940 iDate, iSDate, iSDateTime, iSDateTime24,
941 iSTime, iLDate,
942 iS1Date, iS2Date, iS3Date, iS4Date, iDateIso, iDateIsoS,
943 iSDateIso, iSDateIsoS,
944 iSDateS1, iSDateS2, iSDateS3, iSDateS4,
945 iSDateTimeIso, iSDateTimeIsoS,
946 iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
947 iSDateTimeIso24, iSDateTimeIsoS24,
948 iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
949 iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize,
950 iPrio, iPrioBang, iPrioAlpha, iInit,
951 iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit,
952 iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek
957 ctype_is_fixed_length(IndexColType ctype)
959 int j;
961 for(j = 0; ; j++){
962 if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
963 break;
965 if(ctype == fixed_ctypes[j])
966 return 1;
969 return 0;
973 /*----------------------------------------------------------------------
974 Setup the widths of the various columns in the index display
975 ----*/
976 void
977 setup_index_header_widths(MAILSTREAM *stream)
979 int colspace; /* for reserving space between columns */
980 int j, some_to_calculate;
981 int space_left, screen_width, fix;
982 int keep_going, tot_pct, was_sl;
983 long max_msgno;
984 WidthType wtype;
985 INDEX_COL_S *cdesc;
987 max_msgno = mn_get_total(ps_global->msgmap);
989 dprint((8, "=== setup_index_header_widths() ===\n"));
991 clear_icache_flags(stream);
992 screen_width = ps_global->ttyo->screen_cols;
993 space_left = screen_width;
994 some_to_calculate = 0;
995 colspace = -1;
998 * Calculate how many fields there are so we know how many spaces
999 * between columns to reserve. Fill in Fixed widths now. Reserve
1000 * special case WeCalculate with non-zero req_widths before doing
1001 * Percent cases below.
1003 for(cdesc = ps_global->index_disp_format;
1004 cdesc->ctype != iNothing;
1005 cdesc++){
1007 if(cdesc->wtype == Fixed){
1008 cdesc->width = cdesc->req_width;
1009 if(cdesc->width > 0)
1010 colspace++;
1012 else if(cdesc->wtype == Percent){
1013 cdesc->width = 0; /* calculated later */
1014 colspace++;
1016 else{ /* WeCalculate */
1017 cdesc->width = cdesc->req_width; /* reserve this for now */
1018 some_to_calculate++;
1019 colspace++;
1022 /* no space after iText */
1023 if(cdesc->ctype == iText)
1024 colspace--;
1026 space_left -= cdesc->width;
1029 colspace = MAX(colspace, 0);
1031 space_left -= colspace; /* space between columns */
1033 ps_global->display_keywords_in_subject = 0;
1034 ps_global->display_keywordinits_in_subject = 0;
1037 * Set the actual lengths for the fixed width fields and set up
1038 * the left or right adjustment for everything.
1039 * There should be a case setting actual_length for all of the types
1040 * in fixed_ctypes.
1042 for(cdesc = ps_global->index_disp_format;
1043 cdesc->ctype != iNothing;
1044 cdesc++){
1046 wtype = cdesc->wtype;
1048 if(cdesc->ctype == iSubjKey || cdesc->ctype == iSubjKeyText)
1049 ps_global->display_keywords_in_subject = 1;
1050 else if(cdesc->ctype == iSubjKeyInit || cdesc->ctype == iSubjKeyInitText)
1051 ps_global->display_keywordinits_in_subject = 1;
1053 if(wtype == WeCalculate || wtype == Percent || cdesc->width != 0){
1055 switch(cdesc->ctype){
1056 case iSDate: case iSDateIso: case iSDateIsoS:
1057 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
1058 case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
1059 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
1060 case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
1061 case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
1062 case iSTime:
1063 set_format_includes_smartdate(stream);
1064 break;
1066 default:
1067 break;
1070 if(ctype_is_fixed_length(cdesc->ctype)){
1071 switch(cdesc->ctype){
1072 case iPrio:
1073 case iPrioBang:
1074 case iAtt:
1075 cdesc->actual_length = 1;
1076 cdesc->adjustment = Left;
1077 break;
1079 case iYear2Digit:
1080 case iDay2Digit:
1081 case iMon2Digit:
1082 cdesc->actual_length = 2;
1083 cdesc->adjustment = Left;
1084 break;
1086 case iArrow:
1087 cdesc->actual_length = 2;
1088 cdesc->adjustment = Right;
1089 break;
1091 case iStatus:
1092 case iInit:
1093 cdesc->actual_length = 3;
1094 cdesc->adjustment = Left;
1095 break;
1097 case iMessNo:
1098 set_format_includes_msgno(stream);
1099 if(max_msgno < 1000)
1100 cdesc->actual_length = 3;
1101 else if(max_msgno < 10000)
1102 cdesc->actual_length = 4;
1103 else if(max_msgno < 100000)
1104 cdesc->actual_length = 5;
1105 else
1106 cdesc->actual_length = 6;
1108 cdesc->adjustment = Right;
1109 break;
1111 case iYear:
1112 case iSIStatus:
1113 cdesc->actual_length = 4;
1114 cdesc->adjustment = Left;
1115 break;
1117 case iTime24:
1118 case iTimezone:
1119 cdesc->actual_length = 5;
1120 cdesc->adjustment = Left;
1121 break;
1123 case iSizeNarrow:
1124 cdesc->actual_length = 5;
1125 cdesc->adjustment = Right;
1126 break;
1128 case iFStatus:
1129 case iIStatus:
1130 cdesc->actual_length = 6;
1131 cdesc->adjustment = Left;
1132 break;
1134 case iScore:
1135 cdesc->actual_length = 6;
1136 cdesc->adjustment = Right;
1137 break;
1139 case iTime12:
1140 case iSize:
1141 case iKSize:
1142 cdesc->actual_length = 7;
1143 cdesc->adjustment = Right;
1144 break;
1146 case iSTime:
1147 cdesc->actual_length = 7;
1148 cdesc->adjustment = Left;
1149 break;
1151 case iPrioAlpha:
1152 cdesc->actual_length = 7;
1153 cdesc->adjustment = Left;
1154 break;
1157 case iS1Date:
1158 case iS2Date:
1159 case iS3Date:
1160 case iS4Date:
1161 case iDateIsoS:
1162 cdesc->actual_length = 8;
1163 cdesc->adjustment = Left;
1164 break;
1166 case iSizeComma:
1167 cdesc->actual_length = 8;
1168 cdesc->adjustment = Right;
1169 break;
1171 case iSDate:
1172 case iSDateTime:
1173 case iSDateTime24:
1174 case iMonAbb:
1175 case iDayOfWeekAbb:
1176 case iDayOfWeek:
1177 case iDate:
1178 case iMonLong:
1179 cdesc->actual_length = cdesc->req_width;
1180 cdesc->adjustment = Left;
1181 break;
1183 case iSDateIsoS:
1184 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
1185 case iSDateTimeIsoS:
1186 case iSDateTimeIsoS24:
1187 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
1188 case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
1189 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
1190 if(cdesc->ctype == iSDateIso
1191 || cdesc->ctype == iSDateTimeIso
1192 || cdesc->ctype == iSDateTimeIso24)
1193 cdesc->actual_length = 10;
1194 else
1195 cdesc->actual_length = 9;
1197 cdesc->adjustment = Left;
1198 break;
1200 case iDescripSize:
1201 cdesc->actual_length = 9;
1202 cdesc->adjustment = Right;
1203 break;
1205 case iDateIso:
1206 cdesc->actual_length = 10;
1207 cdesc->adjustment = Left;
1208 break;
1210 case iLDate:
1211 cdesc->actual_length = 12;
1212 cdesc->adjustment = Left;
1213 break;
1215 default:
1216 alpine_panic("Unhandled fixed case in setup_index_header");
1217 break;
1220 else if(cdesc->ctype == iHeader)
1221 cdesc->adjustment = cdesc->hdrtok ? cdesc->hdrtok->adjustment : Left;
1222 else
1223 cdesc->adjustment = Left;
1227 if(ps_global->display_keywords_in_subject)
1228 ps_global->display_keywordinits_in_subject = 0;
1230 /* if have reserved unneeded space for size, give it back */
1231 for(cdesc = ps_global->index_disp_format;
1232 cdesc->ctype != iNothing;
1233 cdesc++)
1234 if(cdesc->ctype == iSize || cdesc->ctype == iKSize ||
1235 cdesc->ctype == iSizeNarrow ||
1236 cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){
1237 if(cdesc->actual_length == 0){
1238 if((fix=cdesc->width) > 0){ /* had this reserved */
1239 cdesc->width = 0;
1240 space_left += fix;
1243 space_left++; /* +1 for space between columns */
1248 * Calculate the field widths that are basically fixed in width.
1249 * Do them in this order in case we don't have enough space to go around.
1250 * The set of fixed_ctypes here is the same as the set where we
1251 * set the actual_lengths above.
1253 for(j = 0; space_left > 0 && some_to_calculate; j++){
1255 if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
1256 break;
1258 for(cdesc = ps_global->index_disp_format;
1259 cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
1260 cdesc++)
1261 if(cdesc->ctype == fixed_ctypes[j] && cdesc->wtype == WeCalculate){
1262 some_to_calculate--;
1263 fix = MIN(cdesc->actual_length - cdesc->width, space_left);
1264 cdesc->width += fix;
1265 space_left -= fix;
1270 * Fill in widths for Percent cases. If there are no more to calculate,
1271 * use the percentages as relative numbers and use the rest of the space,
1272 * else treat them as absolute percentages of the original avail screen.
1274 if(space_left > 0){
1275 if(some_to_calculate){
1276 int tot_requested = 0;
1279 * Requests are treated as percent of screen width. See if they
1280 * will all fit. If not, trim them back proportionately.
1282 for(cdesc = ps_global->index_disp_format;
1283 cdesc->ctype != iNothing;
1284 cdesc++){
1285 if(cdesc->wtype == Percent){
1286 /* The 2, 200, and +100 are because we're rounding */
1287 fix = ((2*cdesc->req_width *
1288 (screen_width-colspace))+100) / 200;
1289 tot_requested += fix;
1293 if(tot_requested > space_left){
1294 int multiplier = (100 * space_left) / tot_requested;
1296 for(cdesc = ps_global->index_disp_format;
1297 cdesc->ctype != iNothing && space_left > 0;
1298 cdesc++){
1299 if(cdesc->wtype == Percent){
1300 /* The 2, 200, and +100 are because we're rounding */
1301 fix = ((2*cdesc->req_width *
1302 (screen_width-colspace))+100) / 200;
1303 fix = (2 * fix * multiplier + 100) / 200;
1304 fix = MIN(fix, space_left);
1305 cdesc->width += fix;
1306 space_left -= fix;
1310 else{
1311 for(cdesc = ps_global->index_disp_format;
1312 cdesc->ctype != iNothing && space_left > 0;
1313 cdesc++){
1314 if(cdesc->wtype == Percent){
1315 /* The 2, 200, and +100 are because we're rounding */
1316 fix = ((2*cdesc->req_width *
1317 (screen_width-colspace))+100) / 200;
1318 fix = MIN(fix, space_left);
1319 cdesc->width += fix;
1320 space_left -= fix;
1325 else{
1326 tot_pct = 0;
1327 was_sl = space_left;
1328 /* add up total percentages requested */
1329 for(cdesc = ps_global->index_disp_format;
1330 cdesc->ctype != iNothing;
1331 cdesc++)
1332 if(cdesc->wtype == Percent)
1333 tot_pct += cdesc->req_width;
1335 /* give relative weight to requests */
1336 for(cdesc = ps_global->index_disp_format;
1337 cdesc->ctype != iNothing && space_left > 0 && tot_pct > 0;
1338 cdesc++){
1339 if(cdesc->wtype == Percent){
1340 fix = ((2*cdesc->req_width*was_sl)+tot_pct) / (2*tot_pct);
1341 fix = MIN(fix, space_left);
1342 cdesc->width += fix;
1343 space_left -= fix;
1349 /* split up rest, give twice as much to Subject */
1350 keep_going = 1;
1351 while(space_left > 0 && keep_going){
1352 keep_going = 0;
1353 for(cdesc = ps_global->index_disp_format;
1354 cdesc->ctype != iNothing && space_left > 0;
1355 cdesc++){
1356 if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
1357 keep_going++;
1358 cdesc->width++;
1359 space_left--;
1360 if(space_left > 0 && (cdesc->ctype == iSubject
1361 || cdesc->ctype == iShortSubject
1362 || cdesc->ctype == iSubjectText
1363 || cdesc->ctype == iSubjKey
1364 || cdesc->ctype == iShortSubjKey
1365 || cdesc->ctype == iSubjKeyText
1366 || cdesc->ctype == iSubjKeyInit
1367 || cdesc->ctype == iShortSubjKeyInit
1368 || cdesc->ctype == iSubjKeyInitText)){
1369 cdesc->width++;
1370 space_left--;
1376 /* if still more, pad out percent's */
1377 keep_going = 1;
1378 while(space_left > 0 && keep_going){
1379 keep_going = 0;
1380 for(cdesc = ps_global->index_disp_format;
1381 cdesc->ctype != iNothing && space_left > 0;
1382 cdesc++){
1383 if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
1384 keep_going++;
1385 cdesc->width++;
1386 space_left--;
1391 /* if user made Fixed fields too big, give back space */
1392 keep_going = 1;
1393 while(space_left < 0 && keep_going){
1394 keep_going = 0;
1395 for(cdesc = ps_global->index_disp_format;
1396 cdesc->ctype != iNothing && space_left < 0;
1397 cdesc++){
1398 if(cdesc->wtype == Fixed && cdesc->width > 0){
1399 keep_going++;
1400 cdesc->width--;
1401 space_left++;
1406 if(pith_opt_save_index_state)
1407 (*pith_opt_save_index_state)(FALSE);
1411 void
1412 setup_thread_header_widths(MAILSTREAM *stream)
1414 clear_icache_flags(stream);
1415 if(pith_opt_save_index_state)
1416 (*pith_opt_save_index_state)(TRUE);
1421 * load_overview - c-client call back to gather overview data
1423 * Note: if we never get called, UID represents a hole
1424 * if we're passed a zero UID, totally bogus overview data
1425 * if we're passed a zero obuf, mostly bogus overview data
1427 void
1428 load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
1430 if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
1431 INDEXDATA_S idata;
1432 ICE_S *ice;
1434 memset(&idata, 0, sizeof(INDEXDATA_S));
1435 idata.no_fetch = 1;
1438 * Only really load the thing if we've got an NNTP stream
1439 * otherwise we're just using mail_fetch_overview to load the
1440 * IMAP envelope cache with the specific set of messages
1441 * in a single RTT.
1443 idata.stream = stream;
1444 idata.rawno = rawno;
1445 idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
1446 idata.size = obuf->optional.octets;
1447 idata.from = obuf->from;
1448 idata.date = obuf->date;
1449 idata.subject = obuf->subject;
1451 ice = (*format_index_line)(&idata);
1452 if(idata.bogus && ice){
1453 if(THRD_INDX()){
1454 if(ice->tice)
1455 clear_ice(&ice->tice);
1457 else
1458 clear_ice(&ice);
1460 else if(F_OFF(F_QUELL_NEWS_ENV_CB, ps_global)
1461 && (!THRD_INDX() || (ice && ice->tice))
1462 && !msgline_hidden(stream, sp_msgmap(stream), idata.msgno, 0)
1463 && pith_opt_paint_index_hline){
1464 (*pith_opt_paint_index_hline)(stream, idata.msgno, ice);
1470 ICE_S *
1471 build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
1472 long int msgno, long int top_msgno, int msgcount, int *fetched)
1474 ICE_S *ice, *ic;
1475 MESSAGECACHE *mc;
1476 long n, i, cnt, rawno, visible, limit = -1L;
1478 rawno = mn_m2raw(msgmap, msgno);
1480 /* cache hit? */
1481 if(THRD_INDX()){
1482 ice = fetch_ice(stream, rawno);
1483 if(!ice)
1484 return(NULL);
1486 if(ice->tice && ice->tice->ifield
1487 && ice->tice->color_lookup_done && ice->tice->widths_done){
1488 #ifdef DEBUG
1489 char buf[MAX_SCREEN_COLS+1];
1490 simple_index_line(buf, sizeof(buf), ice->tice, msgno);
1491 #endif
1492 dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1493 ice->tice,
1494 buf[0] ? buf : "?",
1495 buf[0] ? strlen(buf) : 0));
1496 return(ice);
1499 else{
1500 ice = fetch_ice(stream, rawno);
1501 if(!ice)
1502 return(NULL);
1504 if(ice->ifield && ice->color_lookup_done && ice->widths_done){
1505 #ifdef DEBUG
1506 char buf[MAX_SCREEN_COLS+1];
1507 simple_index_line(buf, sizeof(buf), ice, msgno);
1508 #endif
1509 dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1510 ice,
1511 buf[0] ? buf : "?",
1512 buf[0] ? strlen(buf) : 0));
1513 return(ice);
1518 * If we are in THRD_INDX() and the width changed we don't currently
1519 * have a method of fixing just the widths and print_format strings.
1520 * Instead, we clear the index cache entry and start over.
1522 if(THRD_INDX() && ice && ice->tice && ice->tice->ifield
1523 && !ice->tice->widths_done){
1524 clear_ice(&ice->tice);
1528 * Fetch everything we need to start filling in the index line
1529 * explicitly via mail_fetch_overview. On an nntp stream
1530 * this has the effect of building the index lines in the
1531 * load_overview callback. Under IMAP we're either getting
1532 * the envelope data via the imap_envelope callback or
1533 * preloading the cache. Either way, we're getting exactly
1534 * what we want rather than relying on linear lookahead sort
1535 * of prefetch...
1537 if(!(fetched && *fetched) && index_in_overview(stream)
1538 && ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1539 || (!THRD_INDX() && !ice->ifield))){
1540 char *seq;
1541 int count;
1542 MESSAGECACHE *mc;
1543 PINETHRD_S *thrd;
1545 if(fetched)
1546 (*fetched)++;
1548 /* clear sequence bits */
1549 for(n = 1L; n <= stream->nmsgs; n++)
1550 if((mc = mail_elt(stream, n)) != NULL)
1551 mc->sequence = 0;
1554 * Light interesting bits
1555 * NOTE: not set above because m2raw's cheaper
1556 * than raw2m for every message
1560 * Unfortunately, it is expensive to calculate visible pages
1561 * in thread index if we are zoomed, so we don't try.
1563 if(THRD_INDX() && any_lflagged(msgmap, MN_HIDE))
1564 visible = msgmap->visible_threads;
1565 else if(THREADING() && sp_viewing_a_thread(stream)){
1567 * We know that all visible messages in the thread are marked
1568 * with MN_CHID2.
1570 for(visible = 0L, n = top_msgno;
1571 visible < msgcount && n <= mn_get_total(msgmap);
1572 n++){
1574 if(!get_lflag(stream, msgmap, n, MN_CHID2))
1575 break;
1577 if(!msgline_hidden(stream, msgmap, n, 0))
1578 visible++;
1582 else
1583 visible = mn_get_total(msgmap)
1584 - any_lflagged(msgmap, MN_HIDE|MN_CHID);
1586 limit = MIN(visible, msgcount);
1588 if(THRD_INDX()){
1589 count = i = 0;
1592 * First add the msgno we're asking for in case it
1593 * isn't visible.
1595 thrd = fetch_thread(stream, mn_m2raw(msgmap, msgno));
1596 if(msgno <= mn_get_total(msgmap)
1597 && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
1598 count += mark_msgs_in_thread(stream, thrd, msgmap);
1601 thrd = fetch_thread(stream, mn_m2raw(msgmap, top_msgno));
1604 * Loop through visible threads, marking them for fetching.
1605 * Stop at end of screen or sooner if we run out of visible
1606 * threads.
1608 while(thrd){
1609 n = mn_raw2m(msgmap, thrd->rawno);
1610 if(n >= msgno
1611 && n <= mn_get_total(msgmap)
1612 && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
1613 count += mark_msgs_in_thread(stream, thrd, msgmap);
1616 if(++i >= limit)
1617 break;
1619 /* find next thread which is visible */
1621 if(mn_get_revsort(msgmap) && thrd->prevthd)
1622 thrd = fetch_thread(stream, thrd->prevthd);
1623 else if(!mn_get_revsort(msgmap) && thrd->nextthd)
1624 thrd = fetch_thread(stream, thrd->nextthd);
1625 else
1626 thrd = NULL;
1627 } while(thrd
1628 && msgline_hidden(stream, msgmap,
1629 mn_raw2m(msgmap, thrd->rawno), 0));
1632 else{
1633 count = i = 0;
1636 * First add the msgno we're asking for in case it
1637 * isn't visible.
1639 if(msgno > 0L && msgno <= mn_get_total(msgmap)
1640 && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,msgno)))) || !ic->ifield)){
1641 if((thrd = fetch_thread(stream, rawno)) != NULL){
1643 * If we're doing a MUTTLIKE display the index line
1644 * may depend on the thread parent, and grandparent,
1645 * and further back. So just fetch the whole thread
1646 * in that case.
1648 if(THREADING()
1649 && ps_global->thread_disp_style == THREAD_MUTTLIKE
1650 && thrd->top)
1651 thrd = fetch_thread(stream, thrd->top);
1653 count += mark_msgs_in_thread(stream, thrd, msgmap);
1655 else if(rawno > 0L && rawno <= stream->nmsgs
1656 && (mc = mail_elt(stream,rawno))
1657 && !mc->private.msg.env){
1658 mc->sequence = 1;
1659 count++;
1663 n = top_msgno;
1664 while(1){
1665 if(n >= msgno
1666 && n <= mn_get_total(msgmap)
1667 && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,n)))) || !ic->ifield)){
1668 if((thrd = fetch_thread(stream, rawno)) != NULL){
1670 * If we're doing a MUTTLIKE display the index line
1671 * may depend on the thread parent, and grandparent,
1672 * and further back. So just fetch the whole thread
1673 * in that case.
1675 if(THREADING()
1676 && ps_global->thread_disp_style == THREAD_MUTTLIKE
1677 && thrd->top)
1678 thrd = fetch_thread(stream, thrd->top);
1680 count += mark_msgs_in_thread(stream, thrd, msgmap);
1682 else if(rawno > 0L && rawno <= stream->nmsgs
1683 && (mc = mail_elt(stream,rawno))
1684 && !mc->private.msg.env){
1685 mc->sequence = 1;
1686 count++;
1690 if(++i >= limit)
1691 break;
1693 /* find next n which is visible */
1694 while(++n <= mn_get_total(msgmap)
1695 && msgline_hidden(stream, msgmap, n, 0))
1700 if(count){
1701 seq = build_sequence(stream, NULL, NULL);
1702 if(seq){
1703 ps_global->dont_count_flagchanges = 1;
1704 mail_fetch_overview_sequence(stream, seq,
1705 (stream->dtb && stream->dtb->name
1706 && !strcmp(stream->dtb->name, "imap"))
1707 ? NULL : load_overview);
1708 ps_global->dont_count_flagchanges = 0;
1709 fs_give((void **) &seq);
1714 * reassign ice from the cache as it may've been built
1715 * within the overview callback or it may have become stale
1716 * in the prior sequence bit setting loop ...
1718 rawno = mn_m2raw(msgmap, msgno);
1719 ice = fetch_ice(stream, rawno);
1720 if(!ice)
1721 return(NULL);
1724 if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1725 || (!THRD_INDX() && !ice->ifield)){
1726 INDEXDATA_S idata;
1729 * With pre-fetching/callback-formatting done and no success,
1730 * fall into formatting the requested line...
1732 memset(&idata, 0, sizeof(INDEXDATA_S));
1733 idata.stream = stream;
1734 idata.msgno = msgno;
1735 idata.rawno = mn_m2raw(msgmap, msgno);
1736 if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
1737 && (mc = mail_elt(stream, idata.rawno))){
1738 idata.size = mc->rfc822_size;
1739 index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
1741 else
1742 idata.bogus = 2;
1744 ice = (*format_index_line)(&idata);
1745 if(!ice)
1746 return(NULL);
1750 * If needed, reset the print_format strings so that they add up to
1751 * the right total width. The reset width functionality isn't implemented
1752 * for THRD_INDX() so we are just doing a complete rebuild in that
1753 * case. This is driven by the clear_ice() call in clear_index_cache_ent()
1754 * so it should never be the case that THRD_INDX() is true and only
1755 * widths_done needs to be fixed.
1757 if((!THRD_INDX() && ice->ifield && !ice->widths_done)){
1758 ICE_S *working_ice;
1759 IFIELD_S *ifield;
1760 INDEX_COL_S *cdesc;
1762 if(need_format_setup(stream))
1763 setup_header_widths(stream);
1765 if(THRD_INDX())
1766 working_ice = ice ? ice->tice : NULL;
1767 else
1768 working_ice = ice;
1770 if(working_ice){
1772 * First fix the ifield widths. The cdescs with nonzero widths
1773 * should correspond to the ifields that are defined.
1775 ifield = working_ice->ifield;
1776 for(cdesc = ps_global->index_disp_format;
1777 cdesc->ctype != iNothing && ifield; cdesc++){
1778 if(cdesc->width){
1779 if(cdesc->ctype != ifield->ctype){
1780 dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno, (int) cdesc->ctype, (int) ifield->ctype));
1781 assert(0);
1784 ifield->width = cdesc->width;
1785 ifield = ifield->next;
1789 /* fix the print_format strings and widths */
1790 for(ifield = working_ice->ifield; ifield; ifield = ifield->next)
1791 set_ielem_widths_in_field(ifield);
1793 working_ice->widths_done = 1;
1797 if(THRD_INDX() && ice->tice)
1798 ice->tice->color_lookup_done = 1;
1801 * Look for a color for this line (and other lines in the current
1802 * view). This does a SEARCH for each role which has a color until
1803 * it finds a match. This will be satisfied by the c-client
1804 * cache created by the mail_fetch_overview above if it is a header
1805 * search.
1807 if(!THRD_INDX() && !ice->color_lookup_done){
1808 COLOR_PAIR *linecolor;
1809 SEARCHSET *ss, *s;
1810 ICE_S *ic;
1811 PAT_STATE *pstate = NULL;
1813 if(pico_usingcolor()){
1814 if(limit < 0L){
1815 if(THREADING() && sp_viewing_a_thread(stream)){
1816 for(visible = 0L, n = top_msgno;
1817 visible < msgcount && n <= mn_get_total(msgmap);
1818 n++){
1820 if(!get_lflag(stream, msgmap, n, MN_CHID2))
1821 break;
1823 if(!msgline_hidden(stream, msgmap, n, 0))
1824 visible++;
1828 else
1829 visible = mn_get_total(msgmap)
1830 - any_lflagged(msgmap, MN_HIDE|MN_CHID);
1832 limit = MIN(visible, msgcount);
1834 /* clear sequence bits */
1835 for(n = 1L; n <= stream->nmsgs; n++)
1836 if((mc = mail_elt(stream, n)) != NULL)
1837 mc->sequence = 0;
1839 cnt = i = 0;
1840 n = top_msgno;
1841 while(1){
1842 if(n >= msgno
1843 && n <= mn_get_total(msgmap)
1844 && (!(ic=fetch_ice(stream,(rawno = mn_m2raw(msgmap, n)))) || !ic->color_lookup_done)){
1846 if(rawno >= 1L && rawno <= stream->nmsgs
1847 && (mc = mail_elt(stream, rawno))){
1848 mc->sequence = 1;
1849 cnt++;
1853 if(++i >= limit)
1854 break;
1856 /* find next n which is visible */
1857 while(++n <= mn_get_total(msgmap)
1858 && msgline_hidden(stream, msgmap, n, 0))
1863 * Why is there a loop here? The first call to get_index_line_color
1864 * will return a set of messages which match one of the roles.
1865 * Then, we eliminate those messages from the search set and try
1866 * again. This time we'd get past that role and into a different
1867 * role. Because of that, we hang onto the state and don't reset
1868 * to the first_pattern on the second and subsequent times
1869 * through the loop, avoiding fruitless match_pattern calls in
1870 * get_index_line_color.
1871 * Before the first call, pstate should be set to NULL.
1873 while(cnt > 0L){
1874 ss = build_searchset(stream);
1875 if(ss){
1876 int colormatch;
1878 linecolor = NULL;
1879 colormatch = get_index_line_color(stream, ss, &pstate,
1880 &linecolor);
1883 * Assign this color to all matched msgno's and
1884 * turn off the sequence bit so we won't check
1885 * for them again.
1887 if(colormatch){
1888 for(s = ss; s; s = s->next){
1889 for(n = s->first; n <= s->last; n++){
1890 if(n >= 1L && n <= stream->nmsgs
1891 && (mc = mail_elt(stream, n))
1892 && mc->searched){
1893 cnt--;
1894 mc->sequence = 0;
1895 ic = fetch_ice(stream, n);
1896 if(ic){
1897 ic->color_lookup_done = 1;
1898 if(linecolor)
1899 ic->linecolor = new_color_pair(linecolor->fg,
1900 linecolor->bg);
1906 if(linecolor)
1907 free_color_pair(&linecolor);
1909 else{
1910 /* have to mark the rest of the lookups done */
1911 for(s = ss; s && cnt > 0; s = s->next){
1912 for(n = s->first; n <= s->last && cnt > 0; n++){
1913 if(n >= 1L && n <= stream->nmsgs
1914 && (mc = mail_elt(stream, n))
1915 && mc->sequence){
1916 cnt--;
1917 ic = fetch_ice(stream, n);
1918 if(ic)
1919 ic->color_lookup_done = 1;
1924 /* just making sure */
1925 cnt = 0L;
1928 mail_free_searchset(&ss);
1930 else
1931 cnt = 0L;
1934 ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
1936 else
1937 ice->color_lookup_done = 1;
1940 return(ice); /* Return formatted index data */
1945 day_of_week(struct date *d)
1947 int m, y;
1949 m = d->month;
1950 y = d->year;
1951 if(m <= 2){
1952 m += 9;
1953 y--;
1955 else
1956 m -= 3; /* March is month 0 */
1958 return((d->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100))%7);
1962 static int daytab[2][13] = {
1963 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1964 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1968 day_of_year(struct date *d)
1970 int i, leap, doy;
1972 if(d->year <= 0 || d->month < 1 || d->month > 12)
1973 return(-1);
1975 doy = d->day;
1976 leap = (d->year%4 == 0 && d->year%100 != 0) || d->year%400 == 0;
1977 for(i = 1; i < d->month; i++)
1978 doy += daytab[leap][i];
1980 return(doy);
1985 /*----------------------------------------------------------------------
1986 Format a string summarizing the message header for index on screen
1988 Args: buffer -- buffer to place formatted line
1989 idata -- snot it takes to format the line
1991 Result: returns pointer given buffer IF entry formatted
1992 else NULL if there was a problem (but the buffer is
1993 still suitable for display)
1994 ----*/
1995 ICE_S *
1996 format_index_index_line(INDEXDATA_S *idata)
1998 char str[BIGWIDTH+1], to_us, status, *field,
1999 *p, *newsgroups;
2000 int i, collapsed = 0, start, fromfield;
2001 long l, score;
2002 BODY *body = NULL;
2003 MESSAGECACHE *mc;
2004 ADDRESS *addr, *toaddr, *ccaddr, *last_to;
2005 PINETHRD_S *thrd = NULL;
2006 INDEX_COL_S *cdesc = NULL;
2007 ICE_S *ice, **icep;
2008 IFIELD_S *ifield;
2009 IELEM_S *ielem;
2010 COLOR_PAIR *color = NULL;
2011 struct variable *vars = ps_global->vars;
2013 dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
2014 idata ? idata->msgno : -1, idata ? idata->rawno : -1));
2017 ice = fetch_ice(idata->stream, idata->rawno);
2018 if(!ice)
2019 return(NULL);
2021 free_ifield(&ice->ifield);
2024 * Operate on a temporary copy of ice. The reason for this
2025 * is that we may end up causing a pine_mail_fetchenvelope() call
2026 * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
2027 * and mm_flags may do a clear_ice(), freeing the ice we are working
2028 * on out from under us. We try to fetch everything we need in
2029 * build_header_work() but c-client will short-circuit our request
2030 * if we already got the raw header for some reason. One possible
2031 * reason is a categorizer command in a filter. In that case
2032 * we still need a fetch fast to get the rest of the envelope data.
2034 ice = copy_ice(ice);
2036 /* is this a collapsed thread index line? */
2037 if(!idata->bogus && THREADING()){
2038 thrd = fetch_thread(idata->stream, idata->rawno);
2039 collapsed = thrd && thrd->next
2040 && get_lflag(idata->stream, NULL,
2041 idata->rawno, MN_COLL);
2044 /* calculate contents of the required fields */
2045 for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
2046 if(cdesc->width){
2047 memset(str, 0, sizeof(str));
2048 ifield = new_ifield(&ice->ifield);
2049 ifield->ctype = cdesc->ctype;
2050 ifield->width = cdesc->width;
2051 fromfield = 0;
2053 if(idata->bogus){
2054 if(cdesc->ctype == iMessNo)
2055 snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
2056 else if(idata->bogus < 2 && (cdesc->ctype == iSubject
2057 || cdesc->ctype == iShortSubject
2058 || cdesc->ctype == iSubjectText
2059 || cdesc->ctype == iSubjKey
2060 || cdesc->ctype == iShortSubjKey
2061 || cdesc->ctype == iSubjKeyText
2062 || cdesc->ctype == iSubjKeyInit
2063 || cdesc->ctype == iShortSubjKeyInit
2064 || cdesc->ctype == iSubjKeyInitText))
2065 snprintf(str, sizeof(str), "%s", _("[ No Message Text Available ]"));
2067 else
2068 switch(cdesc->ctype){
2069 case iStatus:
2070 to_us = status = ' ';
2071 if(collapsed){
2072 thrd = fetch_thread(idata->stream, idata->rawno);
2073 to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
2074 status = status_symbol_for_thread(idata->stream, thrd,
2075 cdesc->ctype);
2077 else{
2078 if(idata->rawno > 0L && idata->rawno <= idata->stream->nmsgs
2079 && (mc=mail_elt(idata->stream,idata->rawno)) && mc->flagged)
2080 to_us = '*'; /* simple */
2081 else if(!IS_NEWS(idata->stream)){
2082 for(addr = fetch_to(idata); addr; addr = addr->next)
2083 if(address_is_us(addr, ps_global)){
2084 ice->to_us = 1;
2085 if(to_us == ' ')
2086 to_us = '+';
2088 break;
2091 if(to_us != '+' && resent_to_us(idata)){
2092 ice->to_us = 1;
2093 if(to_us == ' ')
2094 to_us = '+';
2097 if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
2098 for(addr = fetch_cc(idata); addr; addr = addr->next)
2099 if(address_is_us(addr, ps_global)){
2100 ice->cc_us = 1;
2101 to_us = '-';
2102 break;
2106 status = (!idata->stream || !IS_NEWS(idata->stream)
2107 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
2108 ? 'N' : ' ';
2110 if(mc->seen)
2111 status = ' ';
2113 if(user_flag_is_set(idata->stream, idata->rawno, FORWARDED_FLAG))
2114 status = 'F';
2116 if(mc->answered)
2117 status = 'A';
2119 if(mc->deleted)
2120 status = 'D';
2123 snprintf(str, sizeof(str), "%c %c", to_us, status);
2125 ifield->leftadj = 1;
2126 for(i = 0; i < 3; i++){
2127 ielem = new_ielem(&ifield->ielem);
2128 ielem->freedata = 1;
2129 ielem->data = (char *) fs_get(2 * sizeof(char));
2130 ielem->data[0] = str[i];
2131 ielem->data[1] = '\0';
2132 ielem->datalen = 1;
2133 set_print_format(ielem, 1, ifield->leftadj);
2136 if(pico_usingcolor()){
2138 if(str[0] == '*'){
2139 if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
2140 ielem = ifield->ielem;
2141 ielem->freecolor = 1;
2142 ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
2145 else if(str[0] == '+' || str[0] == '-'){
2146 if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
2147 ielem = ifield->ielem;
2148 ielem->freecolor = 1;
2149 ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
2153 if(str[2] == 'D'){
2154 if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
2155 ielem = ifield->ielem->next->next;
2156 ielem->freecolor = 1;
2157 ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
2160 else if(str[2] == 'A'){
2161 if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
2162 ielem = ifield->ielem->next->next;
2163 ielem->freecolor = 1;
2164 ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
2167 else if(str[2] == 'F'){
2168 if(VAR_IND_FWD_FORE_COLOR && VAR_IND_FWD_BACK_COLOR){
2169 ielem = ifield->ielem->next->next;
2170 ielem->freecolor = 1;
2171 ielem->color = new_color_pair(VAR_IND_FWD_FORE_COLOR, VAR_IND_FWD_BACK_COLOR);
2174 else if(str[2] == 'N'){
2175 if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
2176 ielem = ifield->ielem->next->next;
2177 ielem->freecolor = 1;
2178 ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
2183 break;
2185 case iFStatus:
2186 case iIStatus:
2187 case iSIStatus:
2189 char new, answered, deleted, flagged;
2191 if(collapsed){
2192 thrd = fetch_thread(idata->stream, idata->rawno);
2193 to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
2195 else{
2196 to_us = ' ';
2197 if(!IS_NEWS(idata->stream)){
2198 for(addr = fetch_to(idata); addr; addr = addr->next)
2199 if(address_is_us(addr, ps_global)){
2200 to_us = '+';
2201 break;
2204 if(to_us == ' ' && resent_to_us(idata))
2205 to_us = '+';
2207 if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
2208 for(addr = fetch_cc(idata); addr; addr = addr->next)
2209 if(address_is_us(addr, ps_global)){
2210 to_us = '-';
2211 break;
2216 new = answered = deleted = flagged = ' ';
2218 if(collapsed){
2219 unsigned long save_branch, cnt, tot_in_thrd;
2222 * Branch is a sibling, not part of the thread, so
2223 * don't consider it when displaying this line.
2225 save_branch = thrd->branch;
2226 thrd->branch = 0L;
2228 tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
2229 F_NONE);
2231 cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
2232 if(cnt)
2233 deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
2235 cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
2236 if(cnt)
2237 answered = (cnt == tot_in_thrd) ? 'A' : 'a';
2239 /* no lower case *, same thing for some or all */
2240 if(count_flags_in_thread(idata->stream, thrd, F_FLAG))
2241 flagged = '*';
2243 new = status_symbol_for_thread(idata->stream, thrd,
2244 cdesc->ctype);
2246 thrd->branch = save_branch;
2248 else{
2249 mc = (idata->rawno > 0L && idata->stream
2250 && idata->rawno <= idata->stream->nmsgs)
2251 ? mail_elt(idata->stream, idata->rawno) : NULL;
2252 if(mc && mc->valid){
2253 if(cdesc->ctype == iIStatus || cdesc->ctype == iSIStatus){
2254 if(mc->recent)
2255 new = mc->seen ? 'R' : 'N';
2256 else if (!mc->seen)
2257 new = 'U';
2259 else if(!mc->seen
2260 && (!IS_NEWS(idata->stream)
2261 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
2262 new = 'N';
2264 if(mc->answered)
2265 answered = 'A';
2267 if(mc->deleted)
2268 deleted = 'D';
2270 if(mc->flagged)
2271 flagged = '*';
2275 snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
2276 answered, deleted);
2278 if(cdesc->ctype == iSIStatus)
2279 start = 2;
2280 else
2281 start = 0;
2283 ifield->leftadj = 1;
2284 for(i = start; i < 6; i++){
2285 ielem = new_ielem(&ifield->ielem);
2286 ielem->freedata = 1;
2287 ielem->data = (char *) fs_get(2 * sizeof(char));
2288 ielem->data[0] = str[i];
2289 ielem->data[1] = '\0';
2290 ielem->datalen = 1;
2291 set_print_format(ielem, 1, ifield->leftadj);
2294 if(pico_usingcolor()){
2296 if(str[0] == '+' || str[0] == '-'){
2297 if(start == 0
2298 && VAR_IND_PLUS_FORE_COLOR
2299 && VAR_IND_PLUS_BACK_COLOR){
2300 ielem = ifield->ielem;
2301 ielem->freecolor = 1;
2302 ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
2306 if(str[2] == '*'){
2307 if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
2308 if(start == 2)
2309 ielem = ifield->ielem;
2310 else
2311 ielem = ifield->ielem->next->next;
2313 ielem->freecolor = 1;
2314 ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
2318 if(str[3] == 'N' || str[3] == 'n'){
2319 if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
2320 if(start == 2)
2321 ielem = ifield->ielem->next;
2322 else
2323 ielem = ifield->ielem->next->next->next;
2325 ielem->freecolor = 1;
2326 ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
2329 else if(str[3] == 'R' || str[3] == 'r'){
2330 if(VAR_IND_REC_FORE_COLOR && VAR_IND_REC_BACK_COLOR){
2331 if(start == 2)
2332 ielem = ifield->ielem->next;
2333 else
2334 ielem = ifield->ielem->next->next->next;
2336 ielem->freecolor = 1;
2337 ielem->color = new_color_pair(VAR_IND_REC_FORE_COLOR, VAR_IND_REC_BACK_COLOR);
2340 else if(str[3] == 'U' || str[3] == 'u'){
2341 if(VAR_IND_UNS_FORE_COLOR && VAR_IND_UNS_BACK_COLOR){
2342 if(start == 2)
2343 ielem = ifield->ielem->next;
2344 else
2345 ielem = ifield->ielem->next->next->next;
2347 ielem->freecolor = 1;
2348 ielem->color = new_color_pair(VAR_IND_UNS_FORE_COLOR, VAR_IND_UNS_BACK_COLOR);
2352 if(str[4] == 'A' || str[4] == 'a'){
2353 if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
2354 if(start == 2)
2355 ielem = ifield->ielem->next->next;
2356 else
2357 ielem = ifield->ielem->next->next->next->next;
2359 ielem->freecolor = 1;
2360 ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
2364 if(str[5] == 'D' || str[5] == 'd'){
2365 if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
2366 if(start == 2)
2367 ielem = ifield->ielem->next->next->next;
2368 else
2369 ielem = ifield->ielem->next->next->next->next->next;
2371 ielem->freecolor = 1;
2372 ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
2378 break;
2380 case iMessNo:
2382 * This is a special case. The message number is
2383 * generated on the fly in the painting routine.
2384 * But the data array is allocated here in case it
2385 * is useful for the paint routine.
2387 snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
2388 break;
2390 case iArrow:
2391 snprintf(str, sizeof(str), "%-*.*s", ifield->width, ifield->width, " ");
2392 if(VAR_IND_ARR_FORE_COLOR && VAR_IND_ARR_BACK_COLOR){
2393 ifield->leftadj = 1;
2394 ielem = new_ielem(&ifield->ielem);
2395 ielem->freedata = 1;
2396 ielem->data = cpystr(str);
2397 ielem->datalen = strlen(str);
2398 set_print_format(ielem, ifield->width, ifield->leftadj);
2399 ielem->freecolor = 1;
2400 ielem->color = new_color_pair(VAR_IND_ARR_FORE_COLOR,
2401 VAR_IND_ARR_BACK_COLOR);
2404 break;
2406 case iScore:
2407 score = get_msg_score(idata->stream, idata->rawno);
2408 if(score == SCORE_UNDEF){
2409 SEARCHSET *ss = NULL;
2411 ss = mail_newsearchset();
2412 ss->first = ss->last = (unsigned long) idata->rawno;
2413 if(ss){
2415 * This looks like it might be expensive to get the
2416 * score for each message when needed but it shouldn't
2417 * be too bad because we know we have the envelope
2418 * data cached. We can't calculate all of the scores
2419 * we need for the visible messages right here in
2420 * one fell swoop because we don't have the other
2421 * envelopes yet. And we can't get the other
2422 * envelopes at this point because we may be in
2423 * the middle of a c-client callback (pine_imap_env).
2424 * (Actually we could, because we know whether or
2425 * not we're in the callback because of the no_fetch
2426 * parameter.)
2427 * We have another problem if the score rules depend
2428 * on something other than envelope data. I guess they
2429 * only do that if they have an alltext (search the
2430 * text of the message) definition. So, we're going
2431 * to pass no_fetch to calculate_scores so that it
2432 * can return an error if we need the text data but
2433 * can't get it because of no_fetch. Setting bogus
2434 * will cause us to do the scores calculation later
2435 * when we are no longer in the callback.
2437 idata->bogus =
2438 (calculate_some_scores(idata->stream,
2439 ss, idata->no_fetch) == 0)
2440 ? 1 : 0;
2441 score = get_msg_score(idata->stream, idata->rawno);
2442 mail_free_searchset(&ss);
2446 snprintf(str, sizeof(str), "%ld", score != SCORE_UNDEF ? score : 0L);
2447 break;
2449 case iDate: case iMonAbb: case iLDate:
2450 case iSDate: case iSTime:
2451 case iS1Date: case iS2Date: case iS3Date: case iS4Date:
2452 case iDateIso: case iDateIsoS: case iTime24: case iTime12:
2453 case iSDateIsoS: case iSDateIso:
2454 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
2455 case iSDateTime:
2456 case iSDateTimeIsoS: case iSDateTimeIso:
2457 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
2458 case iSDateTime24:
2459 case iSDateTimeIsoS24: case iSDateTimeIso24:
2460 case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
2461 case iTimezone: case iYear: case iYear2Digit:
2462 case iRDate: case iDay: case iDay2Digit: case iMon2Digit:
2463 case iDayOrdinal: case iMon: case iMonLong:
2464 case iDayOfWeekAbb: case iDayOfWeek:
2465 case iPrefDate: case iPrefTime: case iPrefDateTime:
2466 date_str(fetch_date(idata), cdesc->ctype, 0, str, sizeof(str), cdesc->monabb_width);
2467 break;
2469 case iFromTo:
2470 case iFromToNotNews:
2471 case iFrom:
2472 case iAddress:
2473 case iMailbox:
2474 fromfield++;
2475 from_str(cdesc->ctype, idata, str, sizeof(str), ice);
2476 break;
2478 case iTo:
2479 if(((field = ((addr = fetch_to(idata))
2480 ? "To"
2481 : (addr = fetch_cc(idata))
2482 ? "Cc"
2483 : NULL))
2484 && !set_index_addr(idata, field, addr, NULL, BIGWIDTH, str))
2485 || !field)
2486 if((newsgroups = fetch_newsgroups(idata)) != NULL)
2487 snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
2489 break;
2491 case iCc:
2492 set_index_addr(idata, "Cc", fetch_cc(idata), NULL, BIGWIDTH, str);
2493 break;
2495 case iRecips:
2496 toaddr = fetch_to(idata);
2497 ccaddr = fetch_cc(idata);
2498 for(last_to = toaddr;
2499 last_to && last_to->next;
2500 last_to = last_to->next)
2503 /* point end of to list temporarily at cc list */
2504 if(last_to)
2505 last_to->next = ccaddr;
2507 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2509 if(last_to)
2510 last_to->next = NULL;
2512 break;
2514 case iSender:
2515 fromfield++;
2516 if((addr = fetch_sender(idata)) != NULL)
2517 set_index_addr(idata, "Sender", addr, NULL, BIGWIDTH, str);
2519 break;
2521 case iInit:
2522 {ADDRESS *addr;
2524 if((addr = fetch_from(idata)) && addr->personal){
2525 char *name, *initials = NULL;
2527 name = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
2528 SIZEOF_20KBUF, addr->personal);
2529 if(name == addr->personal){
2530 strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
2531 tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
2532 name = (char *) tmp_20k_buf;
2535 if(name && *name){
2536 initials = reply_quote_initials(name);
2537 snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
2542 break;
2544 case iSize:
2545 /* 0 ... 9999 */
2546 if((l = fetch_size(idata)) < 10*1000L)
2547 snprintf(str, sizeof(str), "(%lu)", l);
2548 /* 10K ... 999K */
2549 else if(l < 1000L*1000L - 1000L/2){
2550 l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2551 snprintf(str, sizeof(str), "(%luK)", l);
2553 /* 1.0M ... 99.9M */
2554 else if(l < 1000L*100L*1000L - 100L*1000L/2){
2555 l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
2556 ? 1L : 0L);
2557 snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
2559 /* 100M ... 2000M */
2560 else if(l <= 2*1000L*1000L*1000L){
2561 l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2562 ? 1L : 0L);
2563 snprintf(str, sizeof(str), "(%luM)", l);
2565 else
2566 snprintf(str, sizeof(str), "(HUGE!)");
2568 break;
2570 case iSizeComma:
2571 /* 0 ... 99,999 */
2572 if((l = fetch_size(idata)) < 100*1000L)
2573 snprintf(str, sizeof(str), "(%s)", comatose(l));
2574 /* 100K ... 9,999K */
2575 else if(l < 10L*1000L*1000L - 1000L/2){
2576 l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2577 snprintf(str, sizeof(str), "(%sK)", comatose(l));
2579 /* 10.0M ... 999.9M */
2580 else if(l < 1000L*1000L*1000L - 100L*1000L/2){
2581 l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
2582 ? 1L : 0L);
2583 snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
2585 /* 1,000M ... 2,000M */
2586 else if(l <= 2*1000L*1000L*1000L){
2587 l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2588 ? 1L : 0L);
2589 snprintf(str, sizeof(str), "(%sM)", comatose(l));
2591 else
2592 snprintf(str, sizeof(str), "(HUGE!)");
2594 break;
2596 case iSizeNarrow:
2597 /* 0 ... 999 */
2598 if((l = fetch_size(idata)) < 1000L)
2599 snprintf(str, sizeof(str), "(%lu)", l);
2600 /* 1K ... 99K */
2601 else if(l < 100L*1000L - 1000L/2){
2602 l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2603 snprintf(str, sizeof(str), "(%luK)", l);
2605 /* .1M ... .9M */
2606 else if(l < 1000L*1000L - 100L*1000L/2){
2607 l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
2608 ? 1L : 0L);
2609 snprintf(str, sizeof(str), "(.%luM)", l);
2611 /* 1M ... 99M */
2612 else if(l < 1000L*100L*1000L - 1000L*1000L/2){
2613 l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2614 ? 1L : 0L);
2615 snprintf(str, sizeof(str), "(%luM)", l);
2617 /* .1G ... .9G */
2618 else if(l < 1000L*1000L*1000L - 100L*1000L*1000L/2){
2619 l = l/(100L*1000L*1000L) + (l%(100L*1000L*1000L) >=
2620 (100L*1000L*1000L/2) ? 1L : 0L);
2621 snprintf(str, sizeof(str), "(.%luG)", l);
2623 /* 1G ... 2G */
2624 else if(l <= 2*1000L*1000L*1000L){
2625 l = l/(1000L*1000L*1000L) + (l%(1000L*1000L*1000L) >=
2626 (1000L*1000L*1000L/2) ? 1L : 0L);
2627 snprintf(str, sizeof(str), "(%luG)", l);
2629 else
2630 snprintf(str, sizeof(str), "(HUGE!)");
2632 break;
2634 /* From Carl Jacobsen <carl@ucsd.edu> */
2635 case iKSize:
2636 l = fetch_size(idata);
2637 l = (l / 1024L) + (l % 1024L != 0 ? 1 : 0);
2639 if(l < 1024L) { /* 0k .. 1023k */
2640 snprintf(str, sizeof(str), "(%luk)", l);
2642 } else if (l < 100L * 1024L){ /* 1.0M .. 99.9M */
2643 snprintf(str, sizeof(str), "(%lu.M)", (l * 10L) / 1024L);
2644 if ((p = strchr(str, '.')) != NULL) {
2645 p--; p[1] = p[0]; p[0] = '.'; /* swap last digit & . */
2647 } else if (l <= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2648 snprintf(str, sizeof(str), "(%luM)", l / 1024L);
2649 } else {
2650 snprintf(str, sizeof(str), "(HUGE!)");
2653 break;
2655 case iDescripSize:
2656 if((body = fetch_body(idata)) != NULL)
2657 switch(body->type){
2658 case TYPETEXT:
2660 mc = (idata->rawno > 0L && idata->stream
2661 && idata->rawno <= idata->stream->nmsgs)
2662 ? mail_elt(idata->stream, idata->rawno) : NULL;
2663 if(mc && mc->rfc822_size < 6000)
2664 snprintf(str, sizeof(str), "(short )");
2665 else if(mc && mc->rfc822_size < 25000)
2666 snprintf(str, sizeof(str), "(medium )");
2667 else if(mc && mc->rfc822_size < 100000)
2668 snprintf(str, sizeof(str), "(long )");
2669 else
2670 snprintf(str, sizeof(str), "(huge )");
2673 break;
2675 case TYPEMULTIPART:
2676 if(strucmp(body->subtype, "MIXED") == 0){
2677 int x;
2679 x = body->nested.part
2680 ? body->nested.part->body.type
2681 : TYPETEXT + 1000;
2682 switch(x){
2683 case TYPETEXT:
2684 if(body->nested.part->body.size.bytes < 6000)
2685 snprintf(str, sizeof(str), "(short+ )");
2686 else if(body->nested.part->body.size.bytes
2687 < 25000)
2688 snprintf(str, sizeof(str), "(medium+)");
2689 else if(body->nested.part->body.size.bytes
2690 < 100000)
2691 snprintf(str, sizeof(str), "(long+ )");
2692 else
2693 snprintf(str, sizeof(str), "(huge+ )");
2694 break;
2696 default:
2697 snprintf(str, sizeof(str), "(multi )");
2698 break;
2701 else if(strucmp(body->subtype, "DIGEST") == 0)
2702 snprintf(str, sizeof(str), "(digest )");
2703 else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
2704 snprintf(str, sizeof(str), "(mul/alt)");
2705 else if(strucmp(body->subtype, "PARALLEL") == 0)
2706 snprintf(str, sizeof(str), "(mul/par)");
2707 else
2708 snprintf(str, sizeof(str), "(multi )");
2710 break;
2712 case TYPEMESSAGE:
2713 snprintf(str, sizeof(str), "(message)");
2714 break;
2716 case TYPEAPPLICATION:
2717 snprintf(str, sizeof(str), "(applica)");
2718 break;
2720 case TYPEAUDIO:
2721 snprintf(str, sizeof(str), "(audio )");
2722 break;
2724 case TYPEIMAGE:
2725 snprintf(str, sizeof(str), "(image )");
2726 break;
2728 case TYPEVIDEO:
2729 snprintf(str, sizeof(str), "(video )");
2730 break;
2732 default:
2733 snprintf(str, sizeof(str), "(other )");
2734 break;
2737 break;
2739 case iAtt:
2740 str[0] = SPACE;
2741 str[1] = '\0';
2742 if((body = fetch_body(idata)) &&
2743 body->type == TYPEMULTIPART &&
2744 strucmp(body->subtype, "ALTERNATIVE") != 0){
2745 PART *part;
2746 int atts = 0;
2748 part = body->nested.part; /* 1st part, don't count */
2749 while(part && part->next && atts < 10){
2750 atts++;
2751 part = part->next;
2754 if(atts > 9)
2755 str[0] = '*';
2756 else if(atts > 0)
2757 str[0] = '0' + atts;
2760 break;
2762 case iSubject:
2763 subj_str(idata, str, sizeof(str), NoKW, 0, 0, ice);
2764 break;
2766 case iShortSubject:
2767 subj_str(idata, str, sizeof(str), NoKW, 0, 1, ice);
2768 break;
2770 case iSubjectText:
2771 subj_str(idata, str, sizeof(str), NoKW, 1, 0, ice);
2772 break;
2774 case iSubjKey:
2775 subj_str(idata, str, sizeof(str), KW, 0, 0, ice);
2776 break;
2778 case iShortSubjKey:
2779 subj_str(idata, str, sizeof(str), KW, 0, 1, ice);
2780 break;
2782 case iSubjKeyText:
2783 subj_str(idata, str, sizeof(str), KW, 1, 0, ice);
2784 break;
2786 case iSubjKeyInit:
2787 subj_str(idata, str, sizeof(str), KWInit, 0, 0, ice);
2788 break;
2790 case iShortSubjKeyInit:
2791 subj_str(idata, str, sizeof(str), KWInit, 0, 1, ice);
2792 break;
2794 case iSubjKeyInitText:
2795 subj_str(idata, str, sizeof(str), KWInit, 1, 0, ice);
2796 break;
2798 case iOpeningText:
2799 case iOpeningTextNQ:
2800 if(idata->no_fetch)
2801 idata->bogus = 1;
2802 else{
2803 char *first_text;
2805 first_text = fetch_firsttext(idata, cdesc->ctype == iOpeningTextNQ);
2807 if(first_text){
2808 strncpy(str, first_text, BIGWIDTH);
2809 str[BIGWIDTH] = '\0';
2810 fs_give((void **) &first_text);
2814 break;
2816 case iKey:
2817 key_str(idata, KW, ice);
2818 break;
2820 case iKeyInit:
2821 key_str(idata, KWInit, ice);
2822 break;
2824 case iNews:
2825 if((newsgroups = fetch_newsgroups(idata)) != NULL){
2826 strncpy(str, newsgroups, BIGWIDTH);
2827 str[BIGWIDTH] = '\0';
2830 break;
2832 case iNewsAndTo:
2833 if((newsgroups = fetch_newsgroups(idata)) != NULL)
2834 strncpy(str, newsgroups, sizeof(str));
2836 if((l = strlen(str)) < sizeof(str)){
2837 if(sizeof(str) - l < 6)
2838 strncpy(str+l, "...", sizeof(str)-l);
2839 else{
2840 if(l > 0){
2841 strncpy(str+l, " and ", sizeof(str)-l);
2842 set_index_addr(idata, "To", fetch_to(idata),
2843 NULL, BIGWIDTH-l-5, str+l+5);
2844 if(!str[l+5])
2845 str[l] = '\0';
2847 else
2848 set_index_addr(idata, "To", fetch_to(idata),
2849 NULL, BIGWIDTH, str);
2853 break;
2855 case iToAndNews:
2856 set_index_addr(idata, "To", fetch_to(idata),
2857 NULL, BIGWIDTH, str);
2858 if((l = strlen(str)) < sizeof(str) &&
2859 (newsgroups = fetch_newsgroups(idata))){
2860 if(sizeof(str) - l < 6)
2861 strncpy(str+l, "...", sizeof(str)-l);
2862 else{
2863 if(l > 0)
2864 strncpy(str+l, " and ", sizeof(str)-l);
2866 if(l > 0)
2867 strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2868 else
2869 strncpy(str, newsgroups, BIGWIDTH);
2873 break;
2875 case iNewsAndRecips:
2876 if((newsgroups = fetch_newsgroups(idata)) != NULL)
2877 strncpy(str, newsgroups, BIGWIDTH);
2879 if((l = strlen(str)) < BIGWIDTH){
2880 if(BIGWIDTH - l < 6)
2881 strncpy(str+l, "...", BIGWIDTH-l);
2882 else{
2883 toaddr = fetch_to(idata);
2884 ccaddr = fetch_cc(idata);
2885 for(last_to = toaddr;
2886 last_to && last_to->next;
2887 last_to = last_to->next)
2890 /* point end of to list temporarily at cc list */
2891 if(last_to)
2892 last_to->next = ccaddr;
2894 if(l > 0){
2895 strncpy(str+l, " and ", sizeof(str)-l);
2896 set_index_addr(idata, "To", toaddr,
2897 NULL, BIGWIDTH-l-5, str+l+5);
2898 if(!str[l+5])
2899 str[l] = '\0';
2901 else
2902 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2904 if(last_to)
2905 last_to->next = NULL;
2909 break;
2911 case iRecipsAndNews:
2912 toaddr = fetch_to(idata);
2913 ccaddr = fetch_cc(idata);
2914 for(last_to = toaddr;
2915 last_to && last_to->next;
2916 last_to = last_to->next)
2919 /* point end of to list temporarily at cc list */
2920 if(last_to)
2921 last_to->next = ccaddr;
2923 set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
2925 if(last_to)
2926 last_to->next = NULL;
2928 if((l = strlen(str)) < BIGWIDTH &&
2929 (newsgroups = fetch_newsgroups(idata))){
2930 if(BIGWIDTH - l < 6)
2931 strncpy(str+l, "...", BIGWIDTH-l);
2932 else{
2933 if(l > 0)
2934 strncpy(str+l, " and ", sizeof(str)-l);
2936 if(l > 0)
2937 strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2938 else
2939 strncpy(str, newsgroups, BIGWIDTH);
2943 break;
2945 case iPrio:
2946 case iPrioAlpha:
2947 case iPrioBang:
2948 prio_str(idata, cdesc->ctype, ice);
2949 break;
2951 case iHeader:
2952 header_str(idata, cdesc->hdrtok, ice);
2953 break;
2955 case iText:
2956 strncpy(str, (cdesc->hdrtok && cdesc->hdrtok->hdrname) ? cdesc->hdrtok->hdrname : "", sizeof(str));
2957 str[sizeof(str)-1] = '\0';
2958 break;
2960 default:
2961 break;
2965 * If the element wasn't already filled in above, do it here.
2967 if(!ifield->ielem){
2968 ielem = new_ielem(&ifield->ielem);
2970 if((color = hdr_color(itokens[itokensinv[cdesc->ctype].ctype].name, NULL, ps_global->index_token_colors)) != NULL){
2971 if(pico_usingcolor()){
2972 ielem->color = new_color_pair(color->fg, color->bg);
2973 ielem->type = eTypeCol;
2975 free_color_pair(&color);
2978 ielem->freedata = 1;
2979 ielem->data = cpystr(str);
2980 ielem->datalen = strlen(str);
2982 if(fromfield && pico_usingcolor()
2983 && ps_global->VAR_IND_FROM_FORE_COLOR
2984 && ps_global->VAR_IND_FROM_BACK_COLOR){
2985 ielem->type = eTypeCol;
2986 ielem->freecolor = 1;
2987 ielem->color = new_color_pair(ps_global->VAR_IND_FROM_FORE_COLOR,
2988 ps_global->VAR_IND_FROM_BACK_COLOR);
2990 * This space is here so that if the text does
2991 * not extend all the way to the end of the field then
2992 * we'll switch the color back and paint the rest of the
2993 * field in the Normal color or the index line color.
2995 ielem = new_ielem(&ielem);
2996 ielem->freedata = 1;
2997 ielem->data = cpystr(" ");
2998 ielem->datalen = 1;
3000 else if((cdesc->ctype == iOpeningText || cdesc->ctype == iOpeningTextNQ)
3001 && pico_usingcolor()
3002 && ps_global->VAR_IND_OP_FORE_COLOR
3003 && ps_global->VAR_IND_OP_BACK_COLOR){
3004 ielem->type = eTypeCol;
3005 ielem->freecolor = 1;
3006 ielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR,
3007 ps_global->VAR_IND_OP_BACK_COLOR);
3009 * This space is here so that if the text does
3010 * not extend all the way to the end of the field then
3011 * we'll switch the color back and paint the rest of the
3012 * field in the Normal color or the index line color.
3014 ielem = new_ielem(&ielem);
3015 ielem->freedata = 1;
3016 ielem->data = cpystr(" ");
3017 ielem->datalen = 1;
3020 ifield->leftadj = (cdesc->adjustment == Left) ? 1 : 0;
3021 set_ielem_widths_in_field(ifield);
3025 ice->widths_done = 1;
3026 ice->id = ice_hash(ice);
3029 * Now we have to put the temporary copy of ice back as the
3030 * real thing.
3032 icep = fetch_ice_ptr(idata->stream, idata->rawno);
3033 if(icep){
3034 free_ice(icep); /* free what is already there */
3035 *icep = ice;
3038 return(ice);
3042 ICE_S *
3043 format_thread_index_line(INDEXDATA_S *idata)
3045 char *p, buffer[BIGWIDTH+1];
3046 int thdlen, space_left, i;
3047 PINETHRD_S *thrd = NULL;
3048 ICE_S *ice, *tice = NULL, **ticep = NULL;
3049 IFIELD_S *ifield;
3050 IELEM_S *ielem;
3051 int (*save_sfstr_func)(void);
3052 struct variable *vars = ps_global->vars;
3054 dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
3055 idata ? idata->msgno : -1, idata ? idata->rawno : -1));
3057 space_left = ps_global->ttyo->screen_cols;
3059 if(ps_global->msgmap->max_thrdno < 1000)
3060 thdlen = 3;
3061 else if(ps_global->msgmap->max_thrdno < 10000)
3062 thdlen = 4;
3063 else if(ps_global->msgmap->max_thrdno < 100000)
3064 thdlen = 5;
3065 else
3066 thdlen = 6;
3068 ice = fetch_ice(idata->stream, idata->rawno);
3070 thrd = fetch_thread(idata->stream, idata->rawno);
3072 if(!thrd || !ice) /* can't happen? */
3073 return(ice);
3075 if(!ice->tice){
3076 tice = (ICE_S *) fs_get(sizeof(*tice));
3077 memset(tice, 0, sizeof(*tice));
3078 ice->tice = tice;
3081 tice = ice->tice;
3083 if(!tice)
3084 return(ice);
3086 free_ifield(&tice->ifield);
3088 ticep = &ice->tice;
3089 tice = copy_ice(tice);
3091 if(space_left >= 3){
3092 char to_us, status;
3094 p = buffer;
3095 to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
3096 status = status_symbol_for_thread(idata->stream, thrd, iStatus);
3098 if((p-buffer)+3 < sizeof(buffer)){
3099 p[0] = to_us;
3100 p[1] = ' ';
3101 p[2] = status;
3102 p[3] = '\0';;
3105 space_left -= 3;
3107 ifield = new_ifield(&tice->ifield);
3108 ifield->ctype = iStatus;
3109 ifield->width = 3;
3110 ifield->leftadj = 1;
3111 for(i = 0; i < 3; i++){
3112 ielem = new_ielem(&ifield->ielem);
3113 ielem->freedata = 1;
3114 ielem->data = (char *) fs_get(2 * sizeof(char));
3115 ielem->data[0] = p[i];
3116 ielem->data[1] = '\0';
3117 ielem->datalen = 1;
3118 set_print_format(ielem, 1, ifield->leftadj);
3121 if(pico_usingcolor()){
3122 if(to_us == '*'
3123 && VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
3124 ielem = ifield->ielem;
3125 ielem->freecolor = 1;
3126 ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR,
3127 VAR_IND_IMP_BACK_COLOR);
3128 if(F_ON(F_COLOR_LINE_IMPORTANT, ps_global))
3129 tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR,
3130 VAR_IND_IMP_BACK_COLOR);
3132 else if((to_us == '+' || to_us == '-')
3133 && VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
3134 ielem = ifield->ielem;
3135 ielem->freecolor = 1;
3136 ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR,
3137 VAR_IND_PLUS_BACK_COLOR);
3140 if(status == 'D'
3141 && VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
3142 ielem = ifield->ielem->next->next;
3143 ielem->freecolor = 1;
3144 ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR,
3145 VAR_IND_DEL_BACK_COLOR);
3147 else if(status == 'N'
3148 && VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
3149 ielem = ifield->ielem->next->next;
3150 ielem->freecolor = 1;
3151 ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR,
3152 VAR_IND_NEW_BACK_COLOR);
3157 if(space_left >= thdlen+1){
3158 p = buffer;
3159 space_left--;
3161 snprintf(p, sizeof(buffer), "%*.*s", thdlen, thdlen, "");
3162 space_left -= thdlen;
3164 ifield = new_ifield(&tice->ifield);
3165 ifield->ctype = iMessNo;
3166 ifield->width = thdlen;
3167 ifield->leftadj = 0;
3168 ielem = new_ielem(&ifield->ielem);
3169 ielem->freedata = 1;
3170 ielem->data = cpystr(p);
3171 ielem->datalen = strlen(p);
3172 set_print_format(ielem, ifield->width, ifield->leftadj);
3175 if(space_left >= 7){
3177 p = buffer;
3178 space_left--;
3180 date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer), 0);
3181 if(sizeof(buffer) > 6)
3182 p[6] = '\0';
3184 if(strlen(p) < 6 && (sizeof(buffer)) > 6){
3185 char *q;
3187 for(q = p + strlen(p); q < p + 6; q++)
3188 *q = ' ';
3191 space_left -= 6;
3193 ifield = new_ifield(&tice->ifield);
3194 ifield->ctype = iDate;
3195 ifield->width = 6;
3196 ifield->leftadj = 1;
3197 ielem = new_ielem(&ifield->ielem);
3198 ielem->freedata = 1;
3199 ielem->data = cpystr(p);
3200 ielem->datalen = ifield->width;
3201 set_print_format(ielem, ifield->width, ifield->leftadj);
3205 if(space_left > 3){
3206 int from_width, subj_width, bigthread_adjust;
3207 long in_thread;
3208 char from[BIGWIDTH+1];
3209 char tcnt[50];
3211 space_left--;
3213 in_thread = count_lflags_in_thread(idata->stream, thrd,
3214 ps_global->msgmap, MN_NONE);
3216 p = buffer;
3217 if(in_thread == 1 && THRD_AUTO_VIEW())
3218 snprintf(tcnt, sizeof(tcnt), " ");
3219 else
3220 snprintf(tcnt, sizeof(tcnt), "(%ld)", in_thread);
3222 bigthread_adjust = MAX(0, strlen(tcnt) - 3);
3224 /* third of the rest */
3225 from_width = MAX((space_left-1)/3 - bigthread_adjust, 1);
3227 /* the rest */
3228 subj_width = space_left - from_width - 1;
3230 if(strlen(tcnt) > subj_width)
3231 tcnt[subj_width] = '\0';
3233 from[0] = '\0';
3234 save_sfstr_func = pith_opt_truncate_sfstr;
3235 pith_opt_truncate_sfstr = NULL;
3236 from_str(iFromTo, idata, from, sizeof(from), tice);
3237 pith_opt_truncate_sfstr = save_sfstr_func;
3239 ifield = new_ifield(&tice->ifield);
3240 ifield->leftadj = 1;
3241 ielem = new_ielem(&ifield->ielem);
3242 ielem->freedata = 1;
3243 ielem->type = eTypeCol;
3244 ielem->data = cpystr(from);
3245 ielem->datalen = strlen(from);
3246 ifield->width = from_width;
3247 set_print_format(ielem, ifield->width, ifield->leftadj);
3248 ifield->ctype = iFrom;
3249 if(from_width > 0 && pico_usingcolor()
3250 && VAR_IND_FROM_FORE_COLOR && VAR_IND_FROM_BACK_COLOR){
3251 ielem->freecolor = 1;
3252 ielem->color = new_color_pair(VAR_IND_FROM_FORE_COLOR,
3253 VAR_IND_FROM_BACK_COLOR);
3256 ifield = new_ifield(&tice->ifield);
3257 ifield->leftadj = 0;
3258 ielem = new_ielem(&ifield->ielem);
3259 ielem->freedata = 1;
3260 ielem->data = cpystr(tcnt);
3261 ielem->datalen = strlen(tcnt);
3262 ifield->width = ielem->datalen;
3263 set_print_format(ielem, ifield->width, ifield->leftadj);
3264 ifield->ctype = iAtt; /* not used, except that it isn't special */
3266 subj_width -= strlen(tcnt);
3268 if(subj_width > 0)
3269 subj_width--;
3271 if(subj_width > 0){
3272 if(idata->bogus){
3273 if(idata->bogus < 2)
3274 snprintf(buffer, sizeof(buffer), "%-.*s", BIGWIDTH,
3275 _("[ No Message Text Available ]"));
3277 else{
3278 buffer[0] = '\0';
3279 save_sfstr_func = pith_opt_truncate_sfstr;
3280 pith_opt_truncate_sfstr = NULL;
3281 subj_str(idata, buffer, sizeof(buffer), NoKW, 0, 0, NULL);
3282 pith_opt_truncate_sfstr = save_sfstr_func;
3285 ifield = new_ifield(&tice->ifield);
3286 ifield->leftadj = 1;
3287 ielem = new_ielem(&ifield->ielem);
3288 ielem->freedata = 1;
3289 ielem->type = eTypeCol;
3290 ielem->data = cpystr(buffer);
3291 ielem->datalen = strlen(buffer);
3292 ifield->width = subj_width;
3293 set_print_format(ielem, ifield->width, ifield->leftadj);
3294 ifield->ctype = iSubject;
3295 if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR && VAR_IND_SUBJ_BACK_COLOR){
3296 ielem->freecolor = 1;
3297 ielem->color = new_color_pair(VAR_IND_SUBJ_FORE_COLOR,
3298 VAR_IND_SUBJ_BACK_COLOR);
3302 else if(space_left > 1){
3303 snprintf(p, sizeof(buffer)-(p-buffer), "%-.*s", space_left-1, " ");
3304 ifield = new_ifield(&tice->ifield);
3305 ifield->leftadj = 1;
3306 ielem = new_ielem(&ifield->ielem);
3307 ielem->freedata = 1;
3308 ielem->data = cpystr(p);
3309 ielem->datalen = strlen(p);
3310 ifield->width = space_left-1;
3311 set_print_format(ielem, ifield->width, ifield->leftadj);
3312 ifield->ctype = iSubject;
3315 tice->widths_done = 1;
3316 tice->id = ice_hash(tice);
3318 if(ticep){
3319 free_ice(ticep); /* free what is already there */
3320 *ticep = tice;
3323 return(ice);
3328 * Print the fields of ice in buf with a single space between fields.
3330 * Args buf -- place to put the line
3331 * ice -- the data for the line
3332 * msgno -- this is the msgno to be used, blanks if <= 0
3334 * Returns a pointer to buf.
3336 char *
3337 simple_index_line(char *buf, size_t buflen, ICE_S *ice, long int msgno)
3339 char *p;
3340 IFIELD_S *ifield, *previfield = NULL;
3341 IELEM_S *ielem;
3343 if(!buf)
3344 alpine_panic("NULL buf in simple_index_line()");
3346 if(buflen > 0)
3347 buf[0] = '\0';
3349 p = buf;
3351 if(ice){
3353 for(ifield = ice->ifield; ifield && p-buf < buflen; ifield = ifield->next){
3355 /* space between fields */
3356 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
3357 *p++ = ' ';
3359 /* message number string is generated on the fly */
3360 if(ifield->ctype == iMessNo){
3361 ielem = ifield->ielem;
3362 if(ielem && ielem->datalen >= ifield->width){
3363 if(msgno > 0L)
3364 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
3365 else
3366 snprintf(ielem->data, ielem->datalen+1, "%*.*s", ifield->width, ifield->width, "");
3370 for(ielem = ifield->ielem;
3371 ielem && ielem->print_format && p-buf < buflen;
3372 ielem = ielem->next){
3373 char *src;
3374 size_t bytes_added;
3376 src = ielem->data;
3377 bytes_added = utf8_pad_to_width(p, src,
3378 buflen-(p-buf) * sizeof(char),
3379 ielem->wid, ifield->leftadj);
3380 p += bytes_added;
3383 previfield = ifield;
3386 if(p-buf < buflen)
3387 *p = '\0';
3390 buf[buflen-1] = '\0';
3392 return(buf);
3397 * Look in current mail_stream for matches for messages in the searchset
3398 * which match a color rule pattern. Return the color.
3399 * The searched bit will be set for all of the messages which match the
3400 * first pattern which has a match.
3402 * Args stream -- the mail stream
3403 * searchset -- restrict attention to this set of messages
3404 * pstate -- The pattern state. On the first call it will be Null.
3405 * Null means start over with a new first_pattern.
3406 * After that it will be pointing to our local PAT_STATE
3407 * so that next_pattern goes to the next one after the
3408 * ones we've already checked.
3410 * Returns 0 if no match, 1 if a match.
3411 * The color that goes with the matched rule in returned_color.
3412 * It may be NULL, which indicates default.
3415 get_index_line_color(MAILSTREAM *stream, SEARCHSET *searchset,
3416 PAT_STATE **pstate, COLOR_PAIR **returned_color)
3418 PAT_S *pat = NULL;
3419 long rflags = ROLE_INCOL;
3420 COLOR_PAIR *color = NULL;
3421 int match = 0;
3422 static PAT_STATE localpstate;
3424 dprint((7, "get_index_line_color\n"));
3426 if(returned_color)
3427 *returned_color = NULL;
3429 if(*pstate)
3430 pat = next_pattern(*pstate);
3431 else{
3432 *pstate = &localpstate;
3433 if(!nonempty_patterns(rflags, *pstate))
3434 *pstate = NULL;
3436 if(*pstate)
3437 pat = first_pattern(*pstate);
3440 if(*pstate){
3442 /* Go through the possible roles one at a time until we get a match. */
3443 while(!match && pat){
3444 if(match_pattern(pat->patgrp, stream, searchset, NULL,
3445 get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){
3446 if(!pat->action || pat->action->bogus)
3447 break;
3449 match++;
3450 if(pat->action && pat->action->incol)
3451 color = new_color_pair(pat->action->incol->fg,
3452 pat->action->incol->bg);
3454 else
3455 pat = next_pattern(*pstate);
3459 if(match && returned_color)
3460 *returned_color = color;
3462 return(match);
3470 index_in_overview(MAILSTREAM *stream)
3472 INDEX_COL_S *cdesc = NULL;
3474 if(!(stream->mailbox && IS_REMOTE(stream->mailbox)))
3475 return(FALSE); /* no point! */
3477 if(stream->dtb && stream->dtb->name && !strcmp(stream->dtb->name, "nntp")){
3479 if(THRD_INDX())
3480 return(TRUE);
3482 for(cdesc = ps_global->index_disp_format;
3483 cdesc->ctype != iNothing;
3484 cdesc++)
3485 switch(cdesc->ctype){
3486 case iTo: /* can't be satisfied by XOVER */
3487 case iSender: /* ... or specifically handled */
3488 case iDescripSize: /* ... in news case */
3489 case iAtt:
3490 return(FALSE);
3492 default :
3493 break;
3497 return(TRUE);
3503 * fetch_from - called to get a the index entry's "From:" field
3506 resent_to_us(INDEXDATA_S *idata)
3508 if(!idata->valid_resent_to){
3509 static char *fields[] = {"Resent-To", NULL};
3510 char *h;
3512 if(idata->no_fetch){
3513 idata->bogus = 1; /* don't do this */
3514 return(FALSE);
3517 if((h = pine_fetchheader_lines(idata->stream,idata->rawno,NULL,fields)) != NULL){
3518 idata->resent_to_us = parsed_resent_to_us(h);
3519 fs_give((void **) &h);
3522 idata->valid_resent_to = 1;
3525 return(idata->resent_to_us);
3530 parsed_resent_to_us(char *h)
3532 char *p, *q;
3533 ADDRESS *addr = NULL;
3534 int rv = FALSE;
3536 if((p = strindex(h, ':')) != NULL){
3537 for(q = ++p; (q = strpbrk(q, "\015\012")) != NULL; q++)
3538 *q = ' '; /* quash junk */
3540 rfc822_parse_adrlist(&addr, p, ps_global->maildomain);
3541 if(addr){
3542 rv = address_is_us(addr, ps_global);
3543 mail_free_address(&addr);
3547 return(rv);
3553 * fetch_from - called to get a the index entry's "From:" field
3555 ADDRESS *
3556 fetch_from(INDEXDATA_S *idata)
3558 if(idata->no_fetch) /* implies from is valid */
3559 return(idata->from);
3560 else if(idata->bogus)
3561 idata->bogus = 2;
3562 else{
3563 ENVELOPE *env;
3565 /* c-client call's just cache access at this point */
3566 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3567 return(env->from);
3569 idata->bogus = 1;
3572 return(NULL);
3577 * fetch_to - called to get a the index entry's "To:" field
3579 ADDRESS *
3580 fetch_to(INDEXDATA_S *idata)
3582 if(idata->no_fetch){ /* check for specific validity */
3583 if(idata->valid_to)
3584 return(idata->to);
3585 else
3586 idata->bogus = 1; /* can't give 'em what they want */
3588 else if(idata->bogus){
3589 idata->bogus = 2; /* elevate bogosity */
3591 else{
3592 ENVELOPE *env;
3594 /* c-client call's just cache access at this point */
3595 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3596 return(env->to);
3598 idata->bogus = 1;
3601 return(NULL);
3606 * fetch_cc - called to get a the index entry's "Cc:" field
3608 ADDRESS *
3609 fetch_cc(INDEXDATA_S *idata)
3611 if(idata->no_fetch){ /* check for specific validity */
3612 if(idata->valid_cc)
3613 return(idata->cc);
3614 else
3615 idata->bogus = 1; /* can't give 'em what they want */
3617 else if(idata->bogus){
3618 idata->bogus = 2; /* elevate bogosity */
3620 else{
3621 ENVELOPE *env;
3623 /* c-client call's just cache access at this point */
3624 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3625 return(env->cc);
3627 idata->bogus = 1;
3630 return(NULL);
3636 * fetch_sender - called to get a the index entry's "Sender:" field
3638 ADDRESS *
3639 fetch_sender(INDEXDATA_S *idata)
3641 if(idata->no_fetch){ /* check for specific validity */
3642 if(idata->valid_sender)
3643 return(idata->sender);
3644 else
3645 idata->bogus = 1; /* can't give 'em what they want */
3647 else if(idata->bogus){
3648 idata->bogus = 2; /* elevate bogosity */
3650 else{
3651 ENVELOPE *env;
3653 /* c-client call's just cache access at this point */
3654 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3655 return(env->sender);
3657 idata->bogus = 1;
3660 return(NULL);
3665 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3667 char *
3668 fetch_newsgroups(INDEXDATA_S *idata)
3670 if(idata->no_fetch){ /* check for specific validity */
3671 if(idata->valid_news)
3672 return(idata->newsgroups);
3673 else
3674 idata->bogus = 1; /* can't give 'em what they want */
3676 else if(idata->bogus){
3677 idata->bogus = 2; /* elevate bogosity */
3679 else{
3680 ENVELOPE *env;
3682 /* c-client call's just cache access at this point */
3683 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3684 return(env->newsgroups);
3686 idata->bogus = 1;
3689 return(NULL);
3694 * fetch_subject - called to get at the index entry's "Subject:" field
3696 char *
3697 fetch_subject(INDEXDATA_S *idata)
3699 if(idata->no_fetch) /* implies subject is valid */
3700 return(idata->subject);
3701 else if(idata->bogus)
3702 idata->bogus = 2;
3703 else{
3704 ENVELOPE *env;
3706 /* c-client call's just cache access at this point */
3707 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3708 return(env->subject);
3710 idata->bogus = 1;
3713 return(NULL);
3718 * Return an allocated copy of the first few characters from the body
3719 * of the message for possible use in the index screen.
3721 * Maybe we could figure out some way to do aggregate calls to get
3722 * this info for all the lines in view instead of all the one at a
3723 * time calls we're doing now.
3725 char *
3726 fetch_firsttext(INDEXDATA_S *idata, int delete_quotes)
3728 ENVELOPE *env;
3729 BODY *body = NULL;
3730 char *firsttext = NULL;
3731 STORE_S *so;
3732 gf_io_t pc;
3733 long partial_fetch_len = 0L;
3734 SEARCHSET *ss, **sset;
3736 try_again:
3739 * Prevent wild prefetch, just get the one we're after.
3740 * Can we get this somehow in the overview call in build_header_work?
3742 ss = mail_newsearchset();
3743 ss->first = idata->rawno;
3744 sset = (SEARCHSET **) mail_parameters(idata->stream,
3745 GET_FETCHLOOKAHEAD,
3746 (void *) idata->stream);
3747 if(sset)
3748 *sset = ss;
3750 if((env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)) != NULL){
3751 if(body){
3752 char *subtype = NULL;
3753 char *partno;
3755 if((body->type == TYPETEXT
3756 && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
3758 (body->type == TYPEMULTIPART && body->nested.part
3759 && body->nested.part->body.type == TYPETEXT
3760 && (subtype=body->nested.part->body.subtype)
3761 && ALLOWED_SUBTYPE(subtype))
3763 (body->type == TYPEMULTIPART && body->nested.part
3764 && body->nested.part->body.type == TYPEMULTIPART
3765 && body->nested.part->body.nested.part
3766 && body->nested.part->body.nested.part->body.type == TYPETEXT
3767 && (subtype=body->nested.part->body.nested.part->body.subtype)
3768 && ALLOWED_SUBTYPE(subtype))){
3770 if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3771 char buf[1025], *p;
3772 unsigned char c;
3773 int success;
3774 int one_space_done = 0;
3776 if(partial_fetch_len == 0L){
3777 if(subtype && !strucmp(subtype, "html"))
3778 partial_fetch_len = 1024L;
3779 else if(subtype && !strucmp(subtype, "plain"))
3780 partial_fetch_len = delete_quotes ? 128L : 64L;
3781 else
3782 partial_fetch_len = 256L;
3785 if((body->type == TYPETEXT
3786 && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
3788 (body->type == TYPEMULTIPART && body->nested.part
3789 && body->nested.part->body.type == TYPETEXT
3790 && (subtype=body->nested.part->body.subtype)
3791 && ALLOWED_SUBTYPE(subtype)))
3792 partno = "1";
3793 else
3794 partno = "1.1";
3796 gf_set_so_writec(&pc, so);
3797 success = get_body_part_text(idata->stream, body, idata->rawno,
3798 partno, partial_fetch_len, pc,
3799 NULL, NULL,
3800 GBPT_NOINTR | GBPT_PEEK |
3801 (delete_quotes ? GBPT_DELQUOTES : 0));
3802 gf_clear_so_writec(so);
3804 if(success){
3805 so_seek(so, 0L, 0);
3806 p = buf;
3807 while(p-buf < sizeof(buf)-1 && so_readc(&c, so)){
3808 /* delete leading whitespace */
3809 if(p == buf && isspace(c))
3811 /* and include just one space per run of whitespace */
3812 else if(isspace(c)){
3813 if(!one_space_done){
3814 *p++ = SPACE;
3815 one_space_done++;
3818 else{
3819 one_space_done = 0;
3820 *p++ = c;
3824 *p = '\0';
3826 if(p > buf){
3827 size_t l;
3829 l = strlen(buf);
3830 l += 100;
3831 firsttext = fs_get((l+1) * sizeof(char));
3832 firsttext[0] = '\0';
3833 iutf8ncpy(firsttext, buf, l);
3834 firsttext[l] = '\0';
3835 removing_trailing_white_space(firsttext);
3839 so_give(&so);
3841 /* first if means we didn't fetch all of the data */
3842 if(!(success > 1 && success < partial_fetch_len)){
3843 if(partial_fetch_len < 4096L
3844 && (!firsttext || utf8_width(firsttext) < 50)){
3845 if(firsttext)
3846 fs_give((void **) &firsttext);
3848 partial_fetch_len = 4096L;
3849 goto try_again;
3857 if(ss)
3858 mail_free_searchset(&ss);
3860 return(firsttext);
3865 * fetch_date - called to get at the index entry's "Date:" field
3867 char *
3868 fetch_date(INDEXDATA_S *idata)
3870 if(idata->no_fetch) /* implies date is valid */
3871 return(idata->date);
3872 else if(idata->bogus)
3873 idata->bogus = 2;
3874 else{
3875 ENVELOPE *env;
3877 /* c-client call's just cache access at this point */
3878 if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
3879 return((char *) env->date);
3881 idata->bogus = 1;
3884 return(NULL);
3889 * fetch_header - called to get at the index entry's "Hdrname:" field
3891 char *
3892 fetch_header(INDEXDATA_S *idata, char *hdrname)
3894 if(idata->no_fetch)
3895 idata->bogus = 1;
3896 else if(idata->bogus)
3897 idata->bogus = 2;
3898 else{
3899 char *h, *p, *q, *decoded, *fields[2];
3900 size_t retsize, decsize;
3901 char *ret = NULL;
3902 unsigned char *decode_buf = NULL;
3904 fields[0] = hdrname;
3905 fields[1] = NULL;
3906 if(hdrname && hdrname[0]
3907 && (h = pine_fetchheader_lines(idata->stream, idata->rawno,
3908 NULL, fields))){
3910 if(strlen(h) < strlen(hdrname) + 1){
3911 fs_give((void **) &h);
3912 return(cpystr(""));
3915 /* skip "hdrname:" */
3916 for(p = h + strlen(hdrname) + 1;
3917 *p && isspace((unsigned char)*p); p++)
3920 decsize = (4 * strlen(p)) + 1;
3921 decode_buf = (unsigned char *) fs_get(decsize * sizeof(unsigned char));
3922 decoded = (char *) rfc1522_decode_to_utf8(decode_buf, decsize, p);
3923 p = decoded;
3925 retsize = strlen(decoded);
3926 q = ret = (char *) fs_get((retsize+1) * sizeof(char));
3928 *q = '\0';
3929 while(q-ret < retsize && *p){
3930 if(*p == '\015' || *p == '\012')
3931 p++;
3932 else if(*p == '\t'){
3933 *q++ = SPACE;
3934 p++;
3936 else
3937 *q++ = *p++;
3940 *q = '\0';
3942 fs_give((void **) &h);
3943 if(decode_buf)
3944 fs_give((void **) &decode_buf);
3946 return(ret);
3949 idata->bogus = 1;
3952 return(NULL);
3957 * fetch_size - called to get at the index entry's "size" field
3959 long
3960 fetch_size(INDEXDATA_S *idata)
3962 if(idata->no_fetch) /* implies size is valid */
3963 return(idata->size);
3964 else if(idata->bogus)
3965 idata->bogus = 2;
3966 else{
3967 MESSAGECACHE *mc;
3969 if(idata->stream && idata->rawno > 0L
3970 && idata->rawno <= idata->stream->nmsgs
3971 && (mc = mail_elt(idata->stream, idata->rawno)))
3972 return(mc->rfc822_size);
3974 idata->bogus = 1;
3977 return(0L);
3982 * fetch_body - called to get a the index entry's body structure
3984 BODY *
3985 fetch_body(INDEXDATA_S *idata)
3987 BODY *body;
3989 if(idata->bogus || idata->no_fetch){
3990 idata->bogus = 2;
3991 return(NULL);
3994 if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
3995 return(body);
3997 idata->bogus = 1;
3998 return(NULL);
4003 * s is at least size width+1
4006 set_index_addr(INDEXDATA_S *idata,
4007 char *field,
4008 struct mail_address *addr,
4009 char *prefix,
4010 int width,
4011 char *s)
4013 ADDRESS *atmp;
4014 char *p, *stmp = NULL, *sptr;
4015 char *save_personal = NULL;
4016 int orig_width;
4018 s[0] = '\0';
4020 for(atmp = addr; idata->stream && atmp; atmp = atmp->next)
4021 if(atmp->host && atmp->host[0] == '.'){
4022 char *pref, *h, *fields[2];
4024 if(idata->no_fetch){
4025 idata->bogus = 1;
4026 return(TRUE);
4029 fields[0] = field;
4030 fields[1] = NULL;
4031 if((h = pine_fetchheader_lines(idata->stream, idata->rawno,
4032 NULL, fields)) != NULL){
4033 if(strlen(h) < strlen(field) + 1){
4034 p = h + strlen(h);
4036 else{
4037 /* skip "field:" */
4038 for(p = h + strlen(field) + 1;
4039 *p && isspace((unsigned char)*p); p++)
4043 orig_width = width;
4044 sptr = stmp = (char *) fs_get((orig_width+1) * sizeof(char));
4046 /* add prefix */
4047 for(pref = prefix; pref && *pref; pref++)
4048 if(width){
4049 *sptr++ = *pref;
4050 width--;
4052 else
4053 break;
4055 while(width--)
4056 if(*p == '\015' || *p == '\012')
4057 p++; /* skip CR LF */
4058 else if(!*p)
4059 *sptr++ = ' ';
4060 else if(*p == '\t'){
4061 *sptr++ = ' ';
4062 p++;
4064 else
4065 *sptr++ = *p++;
4067 *sptr = '\0'; /* tie off return string */
4069 if(stmp){
4070 iutf8ncpy(s, stmp, orig_width+1);
4071 s[orig_width] = '\0';
4072 fs_give((void **) &stmp);
4075 fs_give((void **) &h);
4076 return(TRUE);
4078 /* else fall thru and display what c-client gave us */
4081 if(addr && !addr->next /* only one address */
4082 && addr->host /* not group syntax */
4083 && addr->personal && addr->personal[0]){ /* there is a personal name */
4084 char buftmp[MAILTMPLEN];
4085 int l;
4087 if((l = prefix ? strlen(prefix) : 0) != 0)
4088 strncpy(s, prefix, width+1);
4090 snprintf(buftmp, sizeof(buftmp), "%s", addr->personal);
4091 p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
4092 SIZEOF_20KBUF, buftmp);
4093 removing_leading_and_trailing_white_space(p);
4095 iutf8ncpy(s + l, p, width - l);
4097 s[width] = '\0';
4099 if(*(s+l))
4100 return(TRUE);
4101 else{
4102 save_personal = addr->personal;
4103 addr->personal = NULL;
4107 if(addr){
4108 char *a_string;
4109 int l;
4111 a_string = addr_list_string(addr, NULL, 0);
4112 if(save_personal)
4113 addr->personal = save_personal;
4115 if((l = prefix ? strlen(prefix) : 0) != 0)
4116 strncpy(s, prefix, width+1);
4118 iutf8ncpy(s + l, a_string, width - l);
4119 s[width] = '\0';
4121 fs_give((void **)&a_string);
4123 return(TRUE);
4126 if(save_personal)
4127 addr->personal = save_personal;
4129 return(FALSE);
4133 void
4134 index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
4136 if(!env){
4137 idata->bogus = 2;
4138 return;
4141 idata->from = env->from;
4142 idata->to = env->to;
4143 idata->cc = env->cc;
4144 idata->sender = env->sender;
4145 idata->subject = env->subject;
4146 idata->date = (char *) env->date;
4147 idata->newsgroups = env->newsgroups;
4149 idata->valid_to = 1; /* signal that everythings here */
4150 idata->valid_cc = 1;
4151 idata->valid_sender = 1;
4152 idata->valid_news = 1;
4157 * Put a string representing the date into str. The source date is
4158 * in the string datesrc. The format to be used is in type.
4159 * Notice that type is an IndexColType, but really only a subset of
4160 * IndexColType types are allowed.
4162 * Args datesrc -- The source date string
4163 * type -- What type of output we want
4164 * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
4165 * str -- Put the answer here.
4166 * str_len -- Length of str
4167 * monabb_width -- This is a hack to get dates to line up right. For
4168 * example, in French (but without accents here)
4169 * dec. 21
4170 * fevr. 23
4171 * mars 7
4172 * For this monabb_width would be 5.
4174 void
4175 date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len,
4176 int monabb_width)
4178 char year4[5], /* 4 digit year */
4179 yearzero[3], /* zero padded, 2-digit year */
4180 monzero[3], /* zero padded, 2-digit month */
4181 mon[3], /* 1 or 2-digit month, no pad */
4182 dayzero[3], /* zero padded, 2-digit day */
4183 day[3], /* 1 or 2-digit day, no pad */
4184 dayord[3], /* 2-letter ordinal label */
4185 monabb[10], /* 3-letter month abbrev */
4186 /* actually maybe not 3 if localized */
4187 hour24[3], /* 2-digit, 24 hour clock hour */
4188 hour12[3], /* 12 hour clock hour, no pad */
4189 minzero[3], /* zero padded, 2-digit minutes */
4190 timezone[6]; /* timezone, like -0800 or +... */
4191 int hr12;
4192 int curtype, lastmonthtype, lastyeartype, preftype;
4193 int sdatetimetype, sdatetime24type;
4194 struct date d;
4195 #define TODAYSTR N_("Today")
4197 curtype = (type == iCurDate ||
4198 type == iCurDateIso ||
4199 type == iCurDateIsoS ||
4200 type == iCurPrefDate ||
4201 type == iCurPrefDateTime ||
4202 type == iCurPrefTime ||
4203 type == iCurTime24 ||
4204 type == iCurTime12 ||
4205 type == iCurDay ||
4206 type == iCurDay2Digit ||
4207 type == iCurDayOfWeek ||
4208 type == iCurDayOfWeekAbb ||
4209 type == iCurMon ||
4210 type == iCurMon2Digit ||
4211 type == iCurMonLong ||
4212 type == iCurMonAbb ||
4213 type == iCurYear ||
4214 type == iCurYear2Digit);
4215 lastmonthtype = (type == iLstMon ||
4216 type == iLstMon2Digit ||
4217 type == iLstMonLong ||
4218 type == iLstMonAbb ||
4219 type == iLstMonYear ||
4220 type == iLstMonYear2Digit);
4221 lastyeartype = (type == iLstYear ||
4222 type == iLstYear2Digit);
4223 sdatetimetype = (type == iSDateTime ||
4224 type == iSDateTimeIso ||
4225 type == iSDateTimeIsoS ||
4226 type == iSDateTimeS1 ||
4227 type == iSDateTimeS2 ||
4228 type == iSDateTimeS3 ||
4229 type == iSDateTimeS4 ||
4230 type == iSDateTime24 ||
4231 type == iSDateTimeIso24 ||
4232 type == iSDateTimeIsoS24 ||
4233 type == iSDateTimeS124 ||
4234 type == iSDateTimeS224 ||
4235 type == iSDateTimeS324 ||
4236 type == iSDateTimeS424);
4237 sdatetime24type = (type == iSDateTime24 ||
4238 type == iSDateTimeIso24 ||
4239 type == iSDateTimeIsoS24 ||
4240 type == iSDateTimeS124 ||
4241 type == iSDateTimeS224 ||
4242 type == iSDateTimeS324 ||
4243 type == iSDateTimeS424);
4244 preftype = (type == iPrefDate ||
4245 type == iPrefDateTime ||
4246 type == iPrefTime ||
4247 type == iCurPrefDate ||
4248 type == iCurPrefDateTime ||
4249 type == iCurPrefTime);
4250 if(str_len > 0)
4251 str[0] = '\0';
4253 if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
4254 return;
4256 if(curtype || lastmonthtype || lastyeartype){
4257 char dbuf[200];
4259 rfc822_date(dbuf);
4260 parse_date(dbuf, &d);
4262 if(lastyeartype)
4263 d.year--;
4264 else if(lastmonthtype){
4265 d.month--;
4266 if(d.month <= 0){
4267 d.month = 12;
4268 d.year--;
4272 else{
4273 parse_date(F_ON(F_DATES_TO_LOCAL,ps_global)
4274 ? convert_date_to_local(datesrc) : datesrc, &d);
4275 if(d.year == -1 || d.month == -1 || d.day == -1){
4276 sdatetimetype = 0;
4277 sdatetime24type = 0;
4278 preftype = 0;
4279 switch(type){
4280 case iSDate: case iSDateTime: case iSDateTime24:
4281 type = iS1Date;
4282 break;
4284 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
4285 case iPrefDate: case iPrefTime: case iPrefDateTime:
4286 type = iDateIso;
4287 break;
4289 case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
4290 type = iDateIsoS;
4291 break;
4293 case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
4294 type = iS1Date;
4295 break;
4297 case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
4298 type = iS1Date;
4299 break;
4301 case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
4302 type = iS1Date;
4303 break;
4305 case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
4306 type = iS1Date;
4307 break;
4309 default:
4310 break;
4315 /* some special ones to start with */
4316 if(preftype){
4317 struct tm tm, *tmptr = NULL;
4318 time_t now;
4321 * Make sure we get the right one if we're using current time.
4323 if(curtype){
4324 now = time((time_t *) 0);
4325 if(now != (time_t) -1)
4326 tmptr = localtime(&now);
4329 if(!tmptr){
4330 memset(&tm, 0, sizeof(tm));
4331 tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
4332 tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
4333 tm.tm_mday = MIN(MAX(d.day, 1), 31);
4334 tm.tm_hour = MIN(MAX(d.hour, 0), 23);
4335 tm.tm_min = MIN(MAX(d.minute, 0), 59);
4336 tm.tm_wday = MIN(MAX(d.wkday, 0), 6);
4337 tmptr = &tm;
4340 switch(type){
4341 case iPrefDate:
4342 case iCurPrefDate:
4343 our_strftime(str, str_len, "%x", tmptr);
4344 break;
4345 case iPrefTime:
4346 case iCurPrefTime:
4347 our_strftime(str, str_len, "%X", tmptr);
4348 break;
4349 case iPrefDateTime:
4350 case iCurPrefDateTime:
4351 our_strftime(str, str_len, "%c", tmptr);
4352 break;
4353 default:
4354 assert(0);
4355 break;
4358 return;
4361 strncpy(monabb, (d.month > 0 && d.month < 13)
4362 ? month_abbrev_locale(d.month) : "", sizeof(monabb));
4363 monabb[sizeof(monabb)-1] = '\0';
4365 strncpy(mon, (d.month > 0 && d.month < 13)
4366 ? int2string(d.month) : "", sizeof(mon));
4367 mon[sizeof(mon)-1] = '\0';
4369 strncpy(day, (d.day > 0 && d.day < 32)
4370 ? int2string(d.day) : "", sizeof(day));
4371 day[sizeof(day)-1] = '\0';
4373 strncpy(dayord,
4374 (d.day <= 0 || d.day > 31) ? "" :
4375 (d.day == 1 || d.day == 21 || d.day == 31) ? "st" :
4376 (d.day == 2 || d.day == 22 ) ? "nd" :
4377 (d.day == 3 || d.day == 23 ) ? "rd" : "th", sizeof(dayord));
4379 dayord[sizeof(dayord)-1] = '\0';
4381 strncpy(year4, (d.year >= 1000 && d.year < 10000)
4382 ? int2string(d.year) : "????", sizeof(year4));
4383 year4[sizeof(year4)-1] = '\0';
4385 if(d.year >= 0){
4386 if((d.year % 100) < 10){
4387 yearzero[0] = '0';
4388 strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
4390 else
4391 strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
4393 else
4394 strncpy(yearzero, "??", sizeof(yearzero));
4396 yearzero[sizeof(yearzero)-1] = '\0';
4398 if(d.month > 0 && d.month < 10){
4399 monzero[0] = '0';
4400 strncpy(monzero+1, int2string(d.month), sizeof(monzero)-1);
4402 else if(d.month >= 10 && d.month <= 12)
4403 strncpy(monzero, int2string(d.month), sizeof(monzero));
4404 else
4405 strncpy(monzero, "??", sizeof(monzero));
4407 monzero[sizeof(monzero)-1] = '\0';
4409 if(d.day > 0 && d.day < 10){
4410 dayzero[0] = '0';
4411 strncpy(dayzero+1, int2string(d.day), sizeof(dayzero)-1);
4413 else if(d.day >= 10 && d.day <= 31)
4414 strncpy(dayzero, int2string(d.day), sizeof(dayzero));
4415 else
4416 strncpy(dayzero, "??", sizeof(dayzero));
4418 dayzero[sizeof(dayzero)-1] = '\0';
4420 hr12 = (d.hour == 0) ? 12 :
4421 (d.hour > 12) ? (d.hour - 12) : d.hour;
4422 hour12[0] = '\0';
4423 if(hr12 > 0 && hr12 <= 12)
4424 strncpy(hour12, int2string(hr12), sizeof(hour12));
4426 hour12[sizeof(hour12)-1] = '\0';
4428 hour24[0] = '\0';
4429 if(d.hour >= 0 && d.hour < 10){
4430 hour24[0] = '0';
4431 strncpy(hour24+1, int2string(d.hour), sizeof(hour24)-1);
4433 else if(d.hour >= 10 && d.hour < 24)
4434 strncpy(hour24, int2string(d.hour), sizeof(hour24));
4436 hour24[sizeof(hour24)-1] = '\0';
4438 minzero[0] = '\0';
4439 if(d.minute >= 0 && d.minute < 10){
4440 minzero[0] = '0';
4441 strncpy(minzero+1, int2string(d.minute), sizeof(minzero)-1);
4443 else if(d.minute >= 10 && d.minute <= 60)
4444 strncpy(minzero, int2string(d.minute), sizeof(minzero));
4446 minzero[sizeof(minzero)-1] = '\0';
4448 if(sizeof(timezone) > 5){
4449 if(d.hours_off_gmt <= 0){
4450 timezone[0] = '-';
4451 d.hours_off_gmt *= -1;
4452 d.min_off_gmt *= -1;
4454 else
4455 timezone[0] = '+';
4457 timezone[1] = '\0';
4458 if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
4459 timezone[1] = '0';
4460 strncpy(timezone+2, int2string(d.hours_off_gmt), sizeof(timezone)-2);
4462 else if(d.hours_off_gmt >= 10 && d.hours_off_gmt < 24)
4463 strncpy(timezone+1, int2string(d.hours_off_gmt), sizeof(timezone)-1);
4464 else{
4465 timezone[1] = '0';
4466 timezone[2] = '0';
4469 timezone[3] = '\0';
4470 if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
4471 timezone[3] = '0';
4472 strncpy(timezone+4, int2string(d.min_off_gmt), sizeof(timezone)-4);
4474 else if(d.min_off_gmt >= 10 && d.min_off_gmt <= 60)
4475 strncpy(timezone+3, int2string(d.min_off_gmt), sizeof(timezone)-3);
4476 else{
4477 timezone[3] = '0';
4478 timezone[4] = '0';
4481 timezone[5] = '\0';
4482 timezone[sizeof(timezone)-1] = '\0';
4485 switch(type){
4486 case iRDate:
4487 /* this one is not locale-specific */
4488 snprintf(str, str_len, "%s%s%s %s %s",
4489 (d.wkday != -1) ? day_abbrev(d.wkday) : "",
4490 (d.wkday != -1) ? ", " : "",
4491 day,
4492 (d.month > 0 && d.month < 13) ? month_abbrev(d.month) : "",
4493 year4);
4494 break;
4495 case iDayOfWeekAbb:
4496 case iCurDayOfWeekAbb:
4497 strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_abbrev_locale(d.wkday) : "", str_len);
4498 str[str_len-1] = '\0';
4499 break;
4500 case iDayOfWeek:
4501 case iCurDayOfWeek:
4502 strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_name_locale(d.wkday) : "", str_len);
4503 str[str_len-1] = '\0';
4504 break;
4505 case iYear:
4506 case iCurYear:
4507 case iLstYear:
4508 case iLstMonYear:
4509 strncpy(str, year4, str_len);
4510 break;
4511 case iDay2Digit:
4512 case iCurDay2Digit:
4513 strncpy(str, dayzero, str_len);
4514 break;
4515 case iMon2Digit:
4516 case iCurMon2Digit:
4517 case iLstMon2Digit:
4518 strncpy(str, monzero, str_len);
4519 break;
4520 case iYear2Digit:
4521 case iCurYear2Digit:
4522 case iLstYear2Digit:
4523 case iLstMonYear2Digit:
4524 strncpy(str, yearzero, str_len);
4525 break;
4526 case iTimezone:
4527 strncpy(str, timezone, str_len);
4528 break;
4529 case iDay:
4530 case iCurDay:
4531 strncpy(str, day, str_len);
4532 break;
4533 case iDayOrdinal:
4534 snprintf(str, str_len, "%s%s", day, dayord);
4535 break;
4536 case iMon:
4537 case iCurMon:
4538 case iLstMon:
4539 if(d.month > 0 && d.month <= 12)
4540 strncpy(str, int2string(d.month), str_len);
4542 break;
4543 case iMonAbb:
4544 case iCurMonAbb:
4545 case iLstMonAbb:
4546 strncpy(str, monabb, str_len);
4547 break;
4548 case iMonLong:
4549 case iCurMonLong:
4550 case iLstMonLong:
4551 strncpy(str, (d.month > 0 && d.month < 13)
4552 ? month_name_locale(d.month) : "", str_len);
4553 break;
4554 case iDate:
4555 case iCurDate:
4556 if(v)
4557 snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
4558 else{
4559 if(monabb_width > 0)
4560 utf8_snprintf(str, str_len, "%-*.*w %2s",
4561 monabb_width, monabb_width, monabb, day);
4562 else
4563 snprintf(str, str_len, "%s %2s", monabb, day);
4566 break;
4567 case iLDate:
4568 if(v)
4569 snprintf(str, str_len, "%s%s%s%s%s", monabb,
4570 (monabb[0] && day[0]) ? " " : "", day,
4571 ((monabb[0] || day[0]) && year4[0]) ? ", " : "",
4572 year4);
4573 else{
4574 if(monabb_width > 0)
4575 utf8_snprintf(str, str_len, "%-*.*w %2s%c %4s",
4576 monabb_width, monabb_width,
4577 monabb, day,
4578 (monabb[0] && day[0] && year4[0]) ? ',' : ' ', year4);
4579 else
4580 snprintf(str, str_len, "%s %2s%c %4s", monabb, day,
4581 (monabb[0] && day[0] && year4[0]) ? ',' : ' ',
4582 year4);
4585 break;
4586 case iS1Date:
4587 case iS2Date:
4588 case iS3Date:
4589 case iS4Date:
4590 case iDateIso:
4591 case iDateIsoS:
4592 case iCurDateIso:
4593 case iCurDateIsoS:
4594 if(monzero[0] == '?' && dayzero[0] == '?' &&
4595 yearzero[0] == '?')
4596 snprintf(str, str_len, "%8s", "");
4597 else{
4598 switch(type){
4599 case iS1Date:
4600 snprintf(str, str_len, "%2s/%2s/%2s",
4601 monzero, dayzero, yearzero);
4602 break;
4603 case iS2Date:
4604 snprintf(str, str_len, "%2s/%2s/%2s",
4605 dayzero, monzero, yearzero);
4606 break;
4607 case iS3Date:
4608 snprintf(str, str_len, "%2s.%2s.%2s",
4609 dayzero, monzero, yearzero);
4610 break;
4611 case iS4Date:
4612 snprintf(str, str_len, "%2s.%2s.%2s",
4613 yearzero, monzero, dayzero);
4614 break;
4615 case iDateIsoS:
4616 case iCurDateIsoS:
4617 snprintf(str, str_len, "%2s-%2s-%2s",
4618 yearzero, monzero, dayzero);
4619 break;
4620 case iDateIso:
4621 case iCurDateIso:
4622 snprintf(str, str_len, "%4s-%2s-%2s",
4623 year4, monzero, dayzero);
4624 break;
4625 default:
4626 break;
4630 break;
4631 case iTime24:
4632 case iCurTime24:
4633 snprintf(str, str_len, "%2s%c%2s",
4634 (hour24[0] && minzero[0]) ? hour24 : "",
4635 (hour24[0] && minzero[0]) ? ':' : ' ',
4636 (hour24[0] && minzero[0]) ? minzero : "");
4637 break;
4638 case iTime12:
4639 case iCurTime12:
4640 snprintf(str, str_len, "%s%c%2s%s",
4641 (hour12[0] && minzero[0]) ? hour12 : "",
4642 (hour12[0] && minzero[0]) ? ':' : ' ',
4643 (hour12[0] && minzero[0]) ? minzero : "",
4644 (hour12[0] && minzero[0] && d.hour < 12) ? "am" :
4645 (hour12[0] && minzero[0] && d.hour >= 12) ? "pm" :
4646 " ");
4647 break;
4648 case iSDate: case iSDateIso: case iSDateIsoS:
4649 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
4650 case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
4651 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
4652 case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
4653 case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
4654 { struct date now, last_day;
4655 char dbuf[200];
4656 int msg_day_of_year, now_day_of_year, today;
4657 int diff, ydiff, last_day_of_year;
4659 rfc822_date(dbuf);
4660 parse_date(dbuf, &now);
4661 today = day_of_week(&now) + 7;
4663 if(today >= 0+7 && today <= 6+7){
4664 now_day_of_year = day_of_year(&now);
4665 msg_day_of_year = day_of_year(&d);
4666 ydiff = now.year - d.year;
4668 if(msg_day_of_year == -1)
4669 diff = -100;
4670 else if(ydiff == 0)
4671 diff = now_day_of_year - msg_day_of_year;
4672 else if(ydiff == 1){
4673 last_day = d;
4674 last_day.month = 12;
4675 last_day.day = 31;
4676 last_day_of_year = day_of_year(&last_day);
4678 diff = now_day_of_year +
4679 (last_day_of_year - msg_day_of_year);
4681 else if(ydiff == -1){
4682 last_day = now;
4683 last_day.month = 12;
4684 last_day.day = 31;
4685 last_day_of_year = day_of_year(&last_day);
4687 diff = -1 * (msg_day_of_year +
4688 (last_day_of_year - now_day_of_year));
4690 else if(ydiff > 1)
4691 diff = 100;
4692 else
4693 diff = -100;
4695 if(diff == 0)
4696 strncpy(str, _(TODAYSTR), str_len);
4697 else if(diff == 1)
4698 strncpy(str, _("Yesterday"), str_len);
4699 else if(diff > 1 && diff < 7)
4700 snprintf(str, str_len, "%s", day_name_locale((today - diff) % 7));
4701 else if(diff == -1)
4702 strncpy(str, _("Tomorrow"), str_len);
4703 else if(diff < -1 && diff > -7)
4704 snprintf(str, str_len, _("Next %.3s!"),
4705 day_name_locale((today - diff) % 7));
4706 else if(diff > 0
4707 && (ydiff == 0
4708 || (ydiff == 1 && 12 + now.month - d.month < 6))){
4709 if(v)
4710 snprintf(str, str_len, "%s%s%s", monabb,
4711 (monabb[0] && day[0]) ? " " : "", day);
4712 else{
4713 if(monabb_width > 0)
4714 utf8_snprintf(str, str_len, "%-*.*w %2s",
4715 monabb_width, monabb_width, monabb, day);
4716 else
4717 snprintf(str, str_len, "%s %2s", monabb, day);
4720 else{
4721 if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
4722 type = iSDateTimeIsoS;
4724 switch(type){
4725 case iSDate: case iSDateTime: case iSDateTime24:
4727 struct tm tm;
4729 memset(&tm, 0, sizeof(tm));
4730 tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
4731 tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
4732 tm.tm_mday = MIN(MAX(d.day, 1), 31);
4733 tm.tm_hour = MIN(MAX(d.hour, 0), 23);
4734 tm.tm_min = MIN(MAX(d.minute, 0), 59);
4735 our_strftime(str, str_len, "%x", &tm);
4738 break;
4739 case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
4740 if(v)
4741 snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
4742 diff < 0 ? "!" : "");
4743 else
4744 snprintf(str, str_len, "%s%s/%s/%s%s",
4745 (mon[0] && mon[1]) ? "" : " ",
4746 mon, dayzero, yearzero,
4747 diff < 0 ? "!" : "");
4748 break;
4749 case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
4750 if(v)
4751 snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
4752 diff < 0 ? "!" : "");
4753 else
4754 snprintf(str, str_len, "%s%s/%s/%s%s",
4755 (day[0] && day[1]) ? "" : " ",
4756 day, monzero, yearzero,
4757 diff < 0 ? "!" : "");
4758 break;
4759 case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
4760 if(v)
4761 snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
4762 diff < 0 ? "!" : "");
4763 else
4764 snprintf(str, str_len, "%s%s.%s.%s%s",
4765 (day[0] && day[1]) ? "" : " ",
4766 day, monzero, yearzero,
4767 diff < 0 ? "!" : "");
4768 break;
4769 case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
4770 if(v)
4771 snprintf(str, str_len, "%s.%s.%s%s",
4772 yearzero, monzero, dayzero,
4773 diff < 0 ? "!" : "");
4774 else
4775 snprintf(str, str_len, "%s.%s.%s%s",
4776 yearzero, monzero, dayzero,
4777 diff < 0 ? "!" : "");
4778 break;
4779 case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
4780 snprintf(str, str_len, "%2s-%2s-%2s",
4781 yearzero, monzero, dayzero);
4782 break;
4783 case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
4784 snprintf(str, str_len, "%4s-%2s-%2s",
4785 year4, monzero, dayzero);
4786 break;
4787 default:
4788 break;
4793 else{
4794 if(v)
4795 snprintf(str, str_len, "%s%s%s", monabb,
4796 (monabb[0] && day[0]) ? " " : "", day);
4797 else{
4798 if(monabb_width > 0)
4799 utf8_snprintf(str, str_len, "%-*.*w %2s",
4800 monabb_width, monabb_width, monabb, day);
4801 else
4802 snprintf(str, str_len, "%s %2s", monabb, day);
4807 break;
4809 default:
4810 break;
4813 str[str_len-1] = '\0';
4815 if(type == iSTime ||
4816 (sdatetimetype && !strcmp(str, _(TODAYSTR)))){
4817 struct date now, last_day;
4818 char dbuf[200], *Ddd, *ampm;
4819 int daydiff;
4821 str[0] = '\0';
4822 rfc822_date(dbuf);
4823 parse_date(dbuf, &now);
4825 /* Figure out if message date lands in the past week */
4827 /* (if message dated this month or last month...) */
4828 if((d.year == now.year && d.month >= now.month - 1) ||
4829 (d.year == now.year - 1 && d.month == 12 && now.month == 1)){
4831 daydiff = day_of_year(&now) - day_of_year(&d);
4834 * If msg in end of last year (and we're in first bit of "this"
4835 * year), diff will be backwards; fix up by adding number of days
4836 * in last year (usually 365, but occasionally 366)...
4838 if(d.year == now.year - 1){
4839 last_day = d;
4840 last_day.month = 12;
4841 last_day.day = 31;
4843 daydiff += day_of_year(&last_day);
4846 else
4847 daydiff = -100; /* comfortably out of range (of past week) */
4849 /* Build 2-digit hour and am/pm indicator, used below */
4851 if(d.hour >= 0 && d.hour < 24){
4852 snprintf(hour12, sizeof(hour12), "%02d", (d.hour % 12 == 0) ? 12 : d.hour % 12);
4853 ampm = (d.hour < 12) ? "am" : "pm";
4854 snprintf(hour24, sizeof(hour24), "%02d", d.hour);
4856 else{
4857 strncpy(hour12, "??", sizeof(hour12));
4858 hour12[sizeof(hour12)-1] = '\0';
4859 ampm = "__";
4860 strncpy(hour24, "??", sizeof(hour24));
4861 hour24[sizeof(hour24)-1] = '\0';
4864 /* Build date/time in str, in format similar to that used by w(1) */
4866 if(daydiff == 0){ /* If date is today, "HH:MMap" */
4867 if(d.minute >= 0 && d.minute < 60)
4868 snprintf(minzero, sizeof(minzero), "%02d", d.minute);
4869 else{
4870 strncpy(minzero, "??", sizeof(minzero));
4871 minzero[sizeof(minzero)-1] = '\0';
4874 snprintf(str, str_len, "%s:%s%s", sdatetime24type ? hour24 : hour12,
4875 minzero, sdatetime24type ? "" : ampm);
4877 else if(daydiff >= 1 && daydiff < 6){ /* If <1wk ago, "DddHHap" */
4879 if(d.month >= 1 && d.day >= 1 && d.year >= 0 &&
4880 d.month <= 12 && d.day <= 31 && d.year <= 9999)
4881 Ddd = day_abbrev_locale(day_of_week(&d));
4882 else
4883 Ddd = "???";
4885 snprintf(str, str_len, "%s%s%s", Ddd, hour12, ampm);
4887 else{ /* date is old or future, "ddMmmyy" */
4888 strncpy(monabb, (d.month >= 1 && d.month <= 12)
4889 ? month_abbrev_locale(d.month) : "???", sizeof(monabb));
4890 monabb[sizeof(monabb)-1] = '\0';
4892 if(d.day >= 1 && d.day <= 31)
4893 snprintf(dayzero, sizeof(dayzero), "%02d", d.day);
4894 else{
4895 strncpy(dayzero, "??", sizeof(dayzero));
4896 dayzero[sizeof(dayzero)-1] = '\0';
4899 if(d.year >= 0 && d.year <= 9999)
4900 snprintf(yearzero, sizeof(yearzero), "%02d", d.year % 100);
4901 else{
4902 strncpy(yearzero, "??", sizeof(yearzero));
4903 yearzero[sizeof(yearzero)-1] = '\0';
4906 snprintf(str, str_len, "%s%s%s", dayzero, monabb, yearzero);
4909 if(str[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
4910 if(v)
4911 memmove(str, str + 1, strlen(str));
4912 else
4913 str[0] = ' ';
4920 * Format a string representing the keywords into ice.
4922 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
4924 * Args idata -- which message?
4925 * kwtype -- keywords or kw initials
4926 * ice -- index cache entry for message
4928 void
4929 key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
4931 int firstone = 1;
4932 KEYWORD_S *kw;
4933 char *word;
4934 COLOR_PAIR *color = NULL;
4935 SPEC_COLOR_S *sc = ps_global->kw_colors;
4936 IELEM_S *ielem = NULL;
4937 IFIELD_S *ourifield = NULL;
4939 if(ice && ice->ifield){
4940 /* move to last ifield, the one we're working */
4941 for(ourifield = ice->ifield;
4942 ourifield && ourifield->next;
4943 ourifield = ourifield->next)
4947 if(!ourifield)
4948 return;
4950 if(kwtype == KWInit){
4951 for(kw = ps_global->keywords; kw; kw = kw->next){
4952 if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
4953 word = (kw->nick && kw->nick[0]) ? kw->nick :
4954 (kw->kw && kw->kw[0]) ? kw->kw : "";
4957 * Pick off the first initial. Since word is UTF-8 it may
4958 * take more than one byte for the first initial.
4961 if(word && word[0]){
4962 UCS ucs;
4963 unsigned long remaining_octets;
4964 unsigned char *inputp;
4966 remaining_octets = strlen(word);
4967 inputp = (unsigned char *) word;
4968 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
4969 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
4970 ielem = new_ielem(&ourifield->ielem);
4971 ielem->freedata = 1;
4972 ielem->datalen = (unsigned) (inputp - (unsigned char *) word);
4973 ielem->data = (char *) fs_get((ielem->datalen + 1) * sizeof(char));
4974 strncpy(ielem->data, word, ielem->datalen);
4975 ielem->data[ielem->datalen] = '\0';
4977 if(pico_usingcolor()
4978 && ((kw->nick && kw->nick[0]
4979 && (color=hdr_color(kw->nick,NULL,sc)))
4980 || (kw->kw && kw->kw[0]
4981 && (color=hdr_color(kw->kw,NULL,sc))))){
4982 ielem->color = color;
4983 color = NULL;
4988 if(color)
4989 free_color_pair(&color);
4993 else if(kwtype == KW){
4994 for(kw = ps_global->keywords; kw; kw = kw->next){
4995 if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
4997 if(!firstone){
4998 ielem = new_ielem(&ourifield->ielem);
4999 ielem->freedata = 1;
5000 ielem->data = cpystr(" ");
5001 ielem->datalen = 1;
5004 firstone = 0;
5006 word = (kw->nick && kw->nick[0]) ? kw->nick :
5007 (kw->kw && kw->kw[0]) ? kw->kw : "";
5009 if(word[0]){
5010 ielem = new_ielem(&ourifield->ielem);
5011 ielem->freedata = 1;
5012 ielem->data = cpystr(word);
5013 ielem->datalen = strlen(word);
5015 if(pico_usingcolor()
5016 && ((kw->nick && kw->nick[0]
5017 && (color=hdr_color(kw->nick,NULL,sc)))
5018 || (kw->kw && kw->kw[0]
5019 && (color=hdr_color(kw->kw,NULL,sc))))){
5020 ielem->color = color;
5021 color = NULL;
5025 if(color)
5026 free_color_pair(&color);
5032 * If we're coloring some of the fields then add a dummy field
5033 * at the end that can soak up the rest of the space after the last
5034 * colored keyword. Otherwise, the last one's color will extend to
5035 * the end of the field.
5037 if(pico_usingcolor()){
5038 ielem = new_ielem(&ourifield->ielem);
5039 ielem->freedata = 1;
5040 ielem->data = cpystr(" ");
5041 ielem->datalen = 1;
5044 ourifield->leftadj = 1;
5045 set_ielem_widths_in_field(ourifield);
5049 void
5050 prio_str(INDEXDATA_S *idata, IndexColType ctype, ICE_S *ice)
5052 IFIELD_S *ourifield = NULL;
5053 IELEM_S *ielem = NULL;
5054 char *hdrval;
5055 PRIORITY_S *p;
5056 int v;
5058 if(ice && ice->ifield){
5059 /* move to last ifield, the one we're working */
5060 for(ourifield = ice->ifield;
5061 ourifield && ourifield->next;
5062 ourifield = ourifield->next)
5066 if(!ourifield)
5067 return;
5069 hdrval = fetch_header(idata, PRIORITYNAME);
5071 if(hdrval && hdrval[0] && isdigit(hdrval[0])){
5072 v = atoi(hdrval);
5073 if(v >= 1 && v <= 5 && v != 3){
5075 ielem = new_ielem(&ourifield->ielem);
5076 ielem->freedata = 1;
5078 switch(ctype){
5079 case iPrio:
5080 ielem->data = (char *) fs_get(2 * sizeof(char));
5081 ielem->data[0] = hdrval[0];
5082 ielem->data[1] = '\0';
5083 break;
5085 case iPrioAlpha:
5086 for(p = priorities; p && p->desc; p++)
5087 if(p->val == v)
5088 break;
5090 if(p && p->desc)
5091 ielem->data = cpystr(p->desc);
5093 break;
5095 case iPrioBang:
5096 ielem->data = (char *) fs_get(2 * sizeof(char));
5097 ielem->data[0] = '\0';
5098 switch(v){
5099 case 1: case 2:
5100 ielem->data[0] = '!';
5101 break;
5103 case 4: case 5:
5105 * We could put a Unicode downarrow in here but
5106 * we have no way of knowing if the user's font
5107 * will have it (I think).
5109 ielem->data[0] = 'v';
5110 break;
5113 ielem->data[1] = '\0';
5115 break;
5117 default:
5118 alpine_panic("Unhandled case in prio_str");
5119 break;
5122 if(!ielem->data)
5123 ielem->data = cpystr("");
5125 if(ielem && ielem->data)
5126 ielem->datalen = strlen(ielem->data);
5128 if((v == 1 || v == 2) && pico_usingcolor()
5129 && ps_global->VAR_IND_HIPRI_FORE_COLOR
5130 && ps_global->VAR_IND_HIPRI_BACK_COLOR){
5131 ielem->type = eTypeCol;
5132 ielem->freecolor = 1;
5133 ielem->color = new_color_pair(ps_global->VAR_IND_HIPRI_FORE_COLOR, ps_global->VAR_IND_HIPRI_BACK_COLOR);
5135 else if((v == 4 || v == 5) && pico_usingcolor()
5136 && ps_global->VAR_IND_LOPRI_FORE_COLOR
5137 && ps_global->VAR_IND_LOPRI_BACK_COLOR){
5138 ielem->type = eTypeCol;
5139 ielem->freecolor = 1;
5140 ielem->color = new_color_pair(ps_global->VAR_IND_LOPRI_FORE_COLOR, ps_global->VAR_IND_LOPRI_BACK_COLOR);
5143 ourifield->leftadj = 1;
5144 set_ielem_widths_in_field(ourifield);
5147 fs_give((void **) &hdrval);
5152 void
5153 header_str(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok, ICE_S *ice)
5155 IFIELD_S *ourifield = NULL;
5156 IELEM_S *ielem = NULL;
5157 char *fieldval = NULL;
5159 if(ice && ice->ifield){
5160 /* move to last ifield, the one we're working */
5161 for(ourifield = ice->ifield;
5162 ourifield && ourifield->next;
5163 ourifield = ourifield->next)
5167 if(!ourifield)
5168 return;
5170 fieldval = get_fieldval(idata, hdrtok);
5172 if(fieldval){
5173 ielem = new_ielem(&ourifield->ielem);
5174 ielem->freedata = 1;
5175 ielem->data = fieldval;
5176 ielem->datalen = strlen(fieldval);
5177 fieldval = NULL;
5178 ourifield->leftadj = (hdrtok->adjustment == Left) ? 1 : 0;
5181 set_ielem_widths_in_field(ourifield);
5185 char *
5186 get_fieldval(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok)
5188 int sep, fieldnum;
5189 char *hdrval = NULL, *testval;
5190 char *fieldval = NULL, *firstval;
5191 char *retfieldval = NULL;
5193 if(!hdrtok)
5194 return(retfieldval);
5196 if(hdrtok && hdrtok->hdrname && hdrtok->hdrname[0])
5197 hdrval = fetch_header(idata, hdrtok ? hdrtok->hdrname : "");
5199 /* find start of fieldnum'th field */
5200 fieldval = hdrval;
5201 for(fieldnum = MAX(hdrtok->fieldnum-1, 0);
5202 fieldnum > 0 && fieldval && *fieldval; fieldnum--){
5204 firstval = NULL;
5205 for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
5206 testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
5207 if(testval && (!firstval || testval < firstval))
5208 firstval = testval;
5211 fieldval = firstval;
5212 if(fieldval && *fieldval)
5213 fieldval++;
5216 /* tie off end of field */
5217 if(fieldval && *fieldval && hdrtok->fieldnum > 0){
5218 firstval = NULL;
5219 for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
5220 testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
5221 if(testval && (!firstval || testval < firstval))
5222 firstval = testval;
5225 if(firstval)
5226 *firstval = '\0';
5229 if(!fieldval)
5230 fieldval = "";
5232 retfieldval = cpystr(fieldval);
5234 if(hdrval)
5235 fs_give((void **) &hdrval);
5237 return(retfieldval);
5241 long
5242 scorevalfrommsg(MAILSTREAM *stream, MsgNo rawno, HEADER_TOK_S *hdrtok, int no_fetch)
5244 INDEXDATA_S idata;
5245 MESSAGECACHE *mc;
5246 char *fieldval = NULL;
5247 long retval = 0L;
5249 memset(&idata, 0, sizeof(INDEXDATA_S));
5250 idata.stream = stream;
5251 idata.no_fetch = no_fetch;
5252 idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
5253 idata.rawno = rawno;
5254 if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
5255 && (mc = mail_elt(stream, idata.rawno))){
5256 idata.size = mc->rfc822_size;
5257 index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
5259 else
5260 idata.bogus = 2;
5262 fieldval = get_fieldval(&idata, hdrtok);
5264 if(fieldval){
5265 retval = atol(fieldval);
5266 fs_give((void **) &fieldval);
5269 return(retval);
5273 * Put a string representing the subject into str. Idata tells us which
5274 * message we are referring to.
5276 * This means we should ensure that all data ends up being UTF-8 data.
5277 * That covers the data in ice ielems and str.
5279 * Args idata -- which message?
5280 * str -- destination buffer
5281 * strsize -- size of str buffer
5282 * kwtype -- prepend keywords or kw initials before the subject
5283 * opening -- add first text from body of message if there's room
5284 * shorten -- if on, shorten the subject.
5285 * ice -- index cache entry for message
5287 void
5288 subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int opening, int shorten, ICE_S *ice)
5290 char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
5291 char *p, *border, *q = NULL, *free_subj = NULL;
5292 char *sp;
5293 size_t len;
5294 int width = -1;
5295 int depth = 0, mult = 2;
5296 int save;
5297 int do_subj = 0, truncated_tree = 0;
5298 PINETHRD_S *thd, *thdorig;
5299 IELEM_S *ielem = NULL, *subjielem = NULL;
5300 IFIELD_S *ourifield = NULL;
5302 if(strsize <= 0)
5303 return;
5306 * If we need the data at the start of the message and we're in
5307 * a c-client callback, defer the data lookup until later.
5309 if(opening && idata->no_fetch){
5310 idata->bogus = 1;
5311 return;
5314 if(ice && ice->ifield){
5315 /* move to last ifield, the one we're working on */
5316 for(ourifield = ice->ifield;
5317 ourifield && ourifield->next;
5318 ourifield = ourifield->next)
5322 str[0] = str[strsize-1] = '\0';
5323 origstr = str;
5324 rawsubj = fetch_subject(idata);
5325 if(!rawsubj)
5326 rawsubj = "";
5329 * Before we do anything else, decode the character set in the subject and
5330 * work with the result.
5332 sp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
5333 SIZEOF_20KBUF, rawsubj);
5335 len = strlen(sp);
5336 len += 100; /* for possible charset, escaped characters */
5337 origsubj = fs_get((len+1) * sizeof(unsigned char));
5338 origsubj[0] = '\0';
5340 iutf8ncpy(origsubj, sp, len);
5342 origsubj[len] = '\0';
5343 replace_tabs_by_space(origsubj);
5344 removing_trailing_white_space(origsubj);
5347 * origsubj is the original subject but it has been decoded. We need
5348 * to free it at the end of this routine.
5351 if(shorten)
5352 shorten_subject(origsubj);
5355 * prepend_keyword will put the keyword stuff before the subject
5356 * and split the subject up into its colored parts in subjielem.
5357 * Subjielem is a local ielem which will have to be fit into the
5358 * real ifield->ielem later. The print_format strings in subjielem will
5359 * not be filled in by prepend_keyword because of the fact that we
5360 * may have to adjust things for threading below.
5361 * We use subjielem in case we want to insert some threading information
5362 * at the front of the subject.
5364 if(kwtype == KW || kwtype == KWInit){
5365 subject = prepend_keyword_subject(idata->stream, idata->rawno,
5366 origsubj, kwtype,
5367 ourifield ? &subjielem : NULL,
5368 ps_global->VAR_KW_BRACES);
5369 free_subj = subject;
5371 else{
5372 subject = origsubj;
5373 if(ourifield){
5374 subjielem = new_ielem(&subjielem);
5375 subjielem->type = eTypeCol;
5376 subjielem->freedata = 1;
5377 subjielem->data = cpystr(subject);
5378 subjielem->datalen = strlen(subject);
5379 if(pico_usingcolor()
5380 && ps_global->VAR_IND_SUBJ_FORE_COLOR
5381 && ps_global->VAR_IND_SUBJ_BACK_COLOR){
5382 subjielem->freecolor = 1;
5383 subjielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
5389 * This space is here so that if the subject does
5390 * not extend all the way to the end of the field then
5391 * we'll switch the color back and paint the rest of the
5392 * field in the Normal color or the index line color.
5394 if(!opening){
5395 ielem = new_ielem(&subjielem);
5396 ielem->freedata = 1;
5397 ielem->data = cpystr(" ");
5398 ielem->datalen = 1;
5401 if(!subject)
5402 subject = "";
5404 if(THREADING()
5405 && (ps_global->thread_disp_style == THREAD_STRUCT
5406 || ps_global->thread_disp_style == THREAD_MUTTLIKE
5407 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
5408 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
5411 * Why do we want to truncate the subject and from strs?
5412 * It's so we can put the [5] thread count things in below when
5413 * we are threading and the thread structure runs off the right
5414 * hand edge of the screen. This routine doesn't know that it
5415 * is running off the edge unless it knows the actual width
5416 * that we have to draw in.
5418 if(pith_opt_truncate_sfstr
5419 && (*pith_opt_truncate_sfstr)()
5420 && ourifield
5421 && ourifield->width > 0)
5422 width = ourifield->width;
5424 if(width < 0)
5425 width = strsize-1;
5427 width = MIN(width, strsize-1);
5430 * We're counting on the fact that this initial part of the
5431 * string is ascii and we have one octet per character and
5432 * characters are width 1 on the screen.
5434 border = str + width;
5436 thdorig = thd = fetch_thread(idata->stream, idata->rawno);
5438 if(pith_opt_condense_thread_cue)
5439 width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
5440 thd && thd->next
5441 && get_lflag(idata->stream,
5442 NULL,idata->rawno,
5443 MN_COLL));
5446 * width is < available strsize and
5447 * border points to something less than or equal
5448 * to the end of the buffer.
5451 sptr = str;
5453 if(thd)
5454 while(thd->parent &&
5455 (thd = fetch_thread(idata->stream, thd->parent)))
5456 depth++;
5458 if(depth > 0){
5459 if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
5460 mult = 1;
5462 sptr += (mult*depth);
5463 for(thd = thdorig, p = str + mult*depth - mult;
5464 thd && thd->parent && p >= str;
5465 thd = fetch_thread(idata->stream, thd->parent), p -= mult){
5466 if(p + mult >= border && !q){
5467 if(width >= 4 && depth < 100){
5468 snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
5469 q = str + width-4;
5471 else if(width >= 5 && depth < 1000){
5472 snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
5473 q = str + width-5;
5475 else{
5476 snprintf(str, width+1, "%s", repeat_char(width, '.'));
5477 q = str;
5480 border = q;
5481 truncated_tree++;
5484 if(p < border){
5485 p[0] = ' ';
5486 if(p + 1 < border)
5487 p[1] = ' ';
5489 if(ps_global->thread_disp_style == THREAD_STRUCT
5490 || ps_global->thread_disp_style == THREAD_MUTTLIKE){
5492 * WARNING!
5493 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
5494 * is ascii.
5496 if(thd == thdorig && !thd->branch)
5497 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
5498 else if(thd == thdorig || thd->branch)
5499 p[0] = '|';
5501 if(p + 1 < border && thd == thdorig)
5502 p[1] = '-';
5508 if(sptr && !truncated_tree){
5510 * Look to see if the subject is the same as the previous
5511 * message in the thread, if any. If it is the same, don't
5512 * reprint the subject.
5514 * Note that when we're prepending keywords to the subject,
5515 * and the user changes a keyword, we do invalidate
5516 * the index cache for that message but we don't go to the
5517 * trouble of invalidating the index cache for the the child
5518 * of that node in the thread, so the MUTT subject line
5519 * display for the child may be wrong. That is, it may show
5520 * it is the same as this subject even though it no longer
5521 * is, or vice versa.
5523 if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
5524 if(depth == 0)
5525 do_subj++;
5526 else{
5527 if(thdorig->parent &&
5528 (thd = fetch_thread(idata->stream, thdorig->parent))
5529 && thd->rawno){
5530 char *this_orig = NULL,
5531 *prev_orig = NULL,
5532 *free_prev_orig = NULL,
5533 *this_prep = NULL, /* includes prepend */
5534 *prev_prep = NULL;
5535 ENVELOPE *env;
5536 mailcache_t mc;
5537 SORTCACHE *sc = NULL;
5539 /* get the stripped subject of previous message */
5540 mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
5541 if(mc)
5542 sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
5544 if(sc && sc->subject)
5545 prev_orig = sc->subject;
5546 else{
5547 char *stripthis;
5549 env = pine_mail_fetchenvelope(idata->stream,
5550 thd->rawno);
5551 stripthis = (env && env->subject)
5552 ? env->subject : "";
5554 mail_strip_subject(stripthis, &prev_orig);
5556 free_prev_orig = prev_orig;
5559 mail_strip_subject(rawsubj, &this_orig);
5561 if(kwtype == KW || kwtype == KWInit){
5562 prev_prep = prepend_keyword_subject(idata->stream,
5563 thd->rawno,
5564 prev_orig,
5565 kwtype, NULL,
5566 ps_global->VAR_KW_BRACES);
5568 this_prep = prepend_keyword_subject(idata->stream,
5569 idata->rawno,
5570 this_orig,
5571 kwtype, NULL,
5572 ps_global->VAR_KW_BRACES);
5574 if((this_prep || prev_prep)
5575 && ((this_prep && !prev_prep)
5576 || (prev_prep && !this_prep)
5577 || strucmp(this_prep, prev_prep)))
5578 do_subj++;
5580 else{
5581 if((this_orig || prev_orig)
5582 && ((this_orig && !prev_orig)
5583 || (prev_orig && !this_orig)
5584 || strucmp(this_orig, prev_orig)))
5585 do_subj++;
5589 * If some of the thread is zoomed out of view, we
5590 * want to display the subject of the first one that
5591 * is in view. If any of the parents or grandparents
5592 * etc of this message are visible, then we don't
5593 * need to worry about it. If all of the parents have
5594 * been zoomed away, then this is the first one.
5596 * When you're looking at a particular case where
5597 * some of the messages of a thread are selected it
5598 * seems like we should look at not only our
5599 * direct parents, but the siblings of the parent
5600 * too. But that's not really correct, because those
5601 * siblings are basically the starts of different
5602 * branches, separate from our branch. They could
5603 * have their own subjects, for example. This will
5604 * give us cases where it looks like we are showing
5605 * the subject too much, but it will be correct!
5607 * In zoom_index() we clear_index_cache_ent for
5608 * some lines which have subjects which might become
5609 * visible when we zoom, and also in set_lflags
5610 * where we might change subjects by unselecting
5611 * something when zoomed.
5613 if(!do_subj){
5614 while(thd){
5615 if(!msgline_hidden(idata->stream,
5616 sp_msgmap(idata->stream),
5617 mn_raw2m(sp_msgmap(idata->stream),
5618 (long) thd->rawno),
5619 0)){
5620 break; /* found a visible parent */
5623 if(thd && thd->parent)
5624 thd = fetch_thread(idata->stream,thd->parent);
5625 else
5626 thd = NULL;
5629 if(!thd) /* none were visible */
5630 do_subj++;
5633 if(this_orig)
5634 fs_give((void **) &this_orig);
5636 if(this_prep)
5637 fs_give((void **) &this_prep);
5639 if(free_prev_orig)
5640 fs_give((void **) &free_prev_orig);
5642 if(prev_prep)
5643 fs_give((void **) &prev_prep);
5645 else
5646 do_subj++;
5649 else
5650 do_subj++;
5652 if(do_subj){
5654 * We don't need to worry about truncating to width
5655 * here. If we go over the right hand edge it will be
5656 * truncated.
5658 strsize -= (sptr - str);
5660 strncpy(sptr, subject, strsize-1);
5661 sptr[strsize-1] = '\0';
5663 else if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
5664 strsize -= (sptr - str);
5666 if(strsize > 0){
5667 sptr[0] = '>';
5668 sptr++;
5672 * We decided we don't need the subject so we'd better
5673 * eliminate subjielem.
5675 free_ielem(&subjielem);
5678 else
5679 free_ielem(&subjielem); /* no room for actual subject */
5681 if(ourifield && sptr && sptr > origstr){
5682 ielem = new_ielem(&ourifield->ielem);
5683 ielem->type = eThreadInfo;
5684 ielem->freedata = 1;
5685 save = *sptr;
5686 *sptr = '\0';
5687 ielem->data = cpystr(origstr);
5688 ielem->datalen = strlen(origstr);
5689 *sptr = save;
5692 else{
5694 * Not much to do for the non-threading case. Just copy the
5695 * subject we have so far into str and truncate it.
5697 strncpy(str, subject, strsize-1);
5698 str[strsize-1] = '\0';
5701 if(ourifield){
5703 * We need to add subjielem to the end of the ourifield->ielem list.
5705 if(subjielem){
5706 if(ourifield->ielem){
5707 for(ielem = ourifield->ielem;
5708 ielem && ielem->next; ielem = ielem->next)
5711 ielem->next = subjielem;
5713 else
5714 ourifield->ielem = subjielem;
5717 ourifield->leftadj = 1;
5720 if(opening && ourifield){
5721 IELEM_S *ftielem = NULL;
5722 size_t len;
5723 char *first_text;
5725 first_text = fetch_firsttext(idata, 0);
5727 if(first_text){
5728 char sep[200];
5729 int seplen;
5731 strncpy(sep, ps_global->VAR_OPENING_SEP ? ps_global->VAR_OPENING_SEP : " - ",
5732 sizeof(sep));
5733 sep[sizeof(sep)-1] = '\0';
5734 removing_double_quotes(sep);
5735 seplen = strlen(sep);
5737 ftielem = new_ielem(&ftielem);
5738 ftielem->type = eTypeCol;
5739 ftielem->freedata = 1;
5740 len = strlen(first_text) + seplen;
5741 ftielem->data = (char *) fs_get((len + 1) * sizeof(char));
5743 strncpy(ftielem->data, sep, seplen);
5744 strncpy(ftielem->data+seplen, first_text, len+1-seplen);
5745 ftielem->data[len] = '\0';
5747 ftielem->datalen = strlen(ftielem->data);
5748 if(first_text)
5749 fs_give((void **) &first_text);
5751 if(ftielem){
5752 if(pico_usingcolor()
5753 && ps_global->VAR_IND_OP_FORE_COLOR
5754 && ps_global->VAR_IND_OP_BACK_COLOR){
5755 ftielem->freecolor = 1;
5756 ftielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR, ps_global->VAR_IND_OP_BACK_COLOR);
5759 * This space is here so that if the opening text does
5760 * not extend all the way to the end of the field then
5761 * we'll switch the color back and paint the rest of the
5762 * field in the Normal color or the index line color.
5764 ielem = new_ielem(&ftielem);
5765 ielem->freedata = 1;
5766 ielem->data = cpystr(" ");
5767 ielem->datalen = 1;
5770 if(ourifield->ielem){
5771 for(ielem = ourifield->ielem;
5772 ielem && ielem->next; ielem = ielem->next)
5775 ielem->next = ftielem;
5777 else
5778 ourifield->ielem = ftielem;
5781 ourifield->leftadj = 1;
5785 if(ourifield)
5786 set_ielem_widths_in_field(ourifield);
5788 if(origsubj)
5789 fs_give((void **) &origsubj);
5791 if(free_subj)
5792 fs_give((void **) &free_subj);
5797 * Returns an allocated string which is the passed in subject with a
5798 * list of keywords prepended.
5800 * If kwtype == KW you will end up with
5802 * {keyword1 keyword2} subject
5804 * (actually, keyword nicknames will be used instead of the actual keywords
5805 * in the case that the user defined nicknames)
5807 * If kwtype == KWInit you get
5809 * {AB} subject
5811 * where A is the first letter of the first keyword and B is the first letter
5812 * of the second defined keyword. No space between them. There could be more
5813 * than two.
5815 * If an ielemp is passed in it will be filled out with the data and colors
5816 * of the pieces of the subject but the print_format strings will not
5817 * be set.
5819 char *
5820 prepend_keyword_subject(MAILSTREAM *stream, long int rawno, char *subject,
5821 SubjKW kwtype, IELEM_S **ielemp, char *braces)
5823 char *p, *next_piece, *retsubj = NULL, *str;
5824 char *left_brace = NULL, *right_brace = NULL;
5825 size_t len;
5826 int some_set = 0, save;
5827 IELEM_S *ielem;
5828 KEYWORD_S *kw;
5829 COLOR_PAIR *color = NULL;
5830 SPEC_COLOR_S *sc = ps_global->kw_colors;
5832 if(!subject)
5833 subject = "";
5835 if(braces && *braces)
5836 get_pair(braces, &left_brace, &right_brace, 1, 0);
5838 len = (left_brace ? strlen(left_brace) : 0) +
5839 (right_brace ? strlen(right_brace) : 0);
5841 if(stream && rawno >= 0L && rawno <= stream->nmsgs){
5842 for(kw = ps_global->keywords; kw; kw = kw->next)
5843 if(user_flag_is_set(stream, rawno, kw->kw)){
5844 if(kwtype == KW){
5845 if(some_set)
5846 len++; /* space between keywords */
5848 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
5849 len += strlen(str);
5851 else if(kwtype == KWInit){
5852 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
5853 /* interested in only the first UTF-8 initial */
5854 if(str && str[0]){
5855 UCS ucs;
5856 unsigned long remaining_octets;
5857 unsigned char *inputp;
5859 remaining_octets = strlen(str);
5860 inputp = (unsigned char *) str;
5861 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
5862 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
5863 len += (unsigned) (inputp - (unsigned char *) str);
5868 some_set++;
5872 if((kwtype == KW || kwtype == KWInit) && some_set){
5873 len += strlen(subject); /* subject is already UTF-8 if needed */
5874 retsubj = (char *) fs_get((len + 1) * sizeof(*retsubj));
5875 memset(retsubj, 0, (len + 1) * sizeof(*retsubj));
5876 next_piece = p = retsubj;
5878 for(kw = ps_global->keywords; kw; kw = kw->next){
5879 if(user_flag_is_set(stream, rawno, kw->kw)){
5880 if(p == retsubj){
5881 if(left_brace && len > 0)
5882 sstrncpy(&p, left_brace, len);
5884 else if(kwtype == KW)
5885 *p++ = ' ';
5887 if(ielemp && p > next_piece){
5888 save = *p;
5889 *p = '\0';
5890 ielem = new_ielem(ielemp);
5891 ielem->freedata = 1;
5892 ielem->data = cpystr(next_piece);
5893 ielem->datalen = strlen(next_piece);
5894 *p = save;
5895 next_piece = p;
5898 str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
5900 if(kwtype == KWInit){
5901 if(str && str[0]){
5902 UCS ucs;
5903 unsigned long remaining_octets;
5904 unsigned char *inputp;
5906 remaining_octets = strlen(str);
5907 inputp = (unsigned char *) str;
5908 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
5909 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
5910 if(len-(p-retsubj) > 0){
5911 sstrncpy(&p, str, MIN(inputp - (unsigned char *) str,len-(p-retsubj)));
5912 if(p > next_piece && ielemp && pico_usingcolor()
5913 && ((kw->nick && kw->nick[0]
5914 && (color=hdr_color(kw->nick,NULL,sc)))
5915 || (kw->kw && kw->kw[0]
5916 && (color=hdr_color(kw->kw,NULL,sc))))){
5917 ielem = new_ielem(ielemp);
5918 ielem->freedata = 1;
5919 save = *p;
5920 *p = '\0';
5921 ielem->data = cpystr(next_piece);
5922 ielem->datalen = strlen(next_piece);
5923 ielem->color = color;
5924 color = NULL;
5925 *p = save;
5926 next_piece = p;
5931 if(color)
5932 free_color_pair(&color);
5935 else{
5936 if(len-(p-retsubj) > 0)
5937 sstrncpy(&p, str, len-(p-retsubj));
5939 if(p > next_piece && ielemp && pico_usingcolor()
5940 && ((kw->nick && kw->nick[0]
5941 && (color=hdr_color(kw->nick,NULL,sc)))
5942 || (kw->kw && kw->kw[0]
5943 && (color=hdr_color(kw->kw,NULL,sc))))){
5944 ielem = new_ielem(ielemp);
5945 ielem->freedata = 1;
5946 save = *p;
5947 *p = '\0';
5948 ielem->data = cpystr(next_piece);
5949 ielem->datalen = strlen(next_piece);
5950 ielem->color = color;
5951 color = NULL;
5952 *p = save;
5953 next_piece = p;
5956 if(color)
5957 free_color_pair(&color);
5962 if(len-(p-retsubj) > 0 && right_brace)
5963 sstrncpy(&p, right_brace, len-(p-retsubj));
5965 if(ielemp && p > next_piece){
5966 save = *p;
5967 *p = '\0';
5968 ielem = new_ielem(ielemp);
5969 ielem->freedata = 1;
5970 ielem->data = cpystr(next_piece);
5971 ielem->datalen = strlen(next_piece);
5972 *p = save;
5973 next_piece = p;
5976 if(len-(p-retsubj) > 0 && subject)
5977 sstrncpy(&p, subject, len-(p-retsubj));
5979 if(ielemp && p > next_piece){
5980 save = *p;
5981 *p = '\0';
5982 ielem = new_ielem(ielemp);
5983 ielem->type = eTypeCol;
5984 ielem->freedata = 1;
5985 ielem->data = cpystr(next_piece);
5986 ielem->datalen = strlen(next_piece);
5987 *p = save;
5988 next_piece = p;
5989 if(pico_usingcolor()
5990 && ps_global->VAR_IND_SUBJ_FORE_COLOR
5991 && ps_global->VAR_IND_SUBJ_BACK_COLOR){
5992 ielem->freecolor = 1;
5993 ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
5997 retsubj[len] = '\0'; /* just making sure */
5999 else{
6000 if(ielemp){
6001 ielem = new_ielem(ielemp);
6002 ielem->type = eTypeCol;
6003 ielem->freedata = 1;
6004 ielem->data = cpystr(subject);
6005 ielem->datalen = strlen(subject);
6006 if(pico_usingcolor()
6007 && ps_global->VAR_IND_SUBJ_FORE_COLOR
6008 && ps_global->VAR_IND_SUBJ_BACK_COLOR){
6009 ielem->freecolor = 1;
6010 ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
6014 retsubj = cpystr(subject);
6017 if(braces){
6018 if(left_brace)
6019 fs_give((void **) &left_brace);
6021 if(right_brace)
6022 fs_give((void **) &right_brace);
6025 return(retsubj);
6030 * This means we should ensure that all data ends up being UTF-8 data.
6031 * That covers the data in ice ielems and str.
6033 void
6034 from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_S *ice)
6036 char *field, *newsgroups, *border, *p, *fptr = NULL, *q = NULL;
6037 ADDRESS *addr;
6038 int width = -1;
6039 int depth = 0, mult = 2;
6040 PINETHRD_S *thd, *thdorig;
6042 if(THREADING()
6043 && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
6044 || ps_global->thread_disp_style == THREAD_INDENT_FROM2
6045 || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
6047 if(pith_opt_truncate_sfstr && (*pith_opt_truncate_sfstr)()){
6048 IFIELD_S *ourifield = NULL;
6050 if(ice && ice->ifield){
6051 /* move to last ifield, the one we're working on */
6052 for(ourifield = ice->ifield;
6053 ourifield && ourifield->next;
6054 ourifield = ourifield->next)
6058 if(ourifield && ourifield->width > 0)
6059 width = ourifield->width;
6062 if(width < 0)
6063 width = strsize-1;
6065 width = MIN(width, strsize-1);
6067 thdorig = thd = fetch_thread(idata->stream, idata->rawno);
6068 border = str + width;
6069 if(pith_opt_condense_thread_cue)
6070 width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
6071 thd && thd->next
6072 && get_lflag(idata->stream,
6073 NULL,idata->rawno,
6074 MN_COLL));
6076 fptr = str;
6078 if(thd)
6079 while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
6080 depth++;
6082 if(depth > 0){
6083 if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
6084 mult = 1;
6086 fptr += (mult*depth);
6087 for(thd = thdorig, p = str + mult*depth - mult;
6088 thd && thd->parent && p >= str;
6089 thd = fetch_thread(idata->stream, thd->parent), p -= mult){
6090 if(p + mult >= border && !q){
6091 if(width >= 4 && depth < 100){
6092 snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
6093 q = str + width-4;
6095 else if(width >= 5 && depth < 1000){
6096 snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
6097 q = str + width-5;
6099 else{
6100 snprintf(str, width+1, "%s", repeat_char(width, '.'));
6101 q = str;
6104 border = q;
6105 fptr = NULL;
6108 if(p + 1 < border){
6109 p[0] = p[1] = ' ';
6110 if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
6112 * WARNING!
6113 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6114 * is ascii.
6116 if(thd == thdorig && !thd->branch)
6117 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
6118 else if(thd == thdorig || thd->branch)
6119 p[0] = '|';
6121 if(thd == thdorig)
6122 p[1] = '-';
6125 else if(p < border){
6126 p[0] = ' ';
6127 if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
6129 * WARNING!
6130 * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
6131 * is ascii.
6133 if(thd == thdorig && !thd->branch)
6134 p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
6135 else if(thd == thdorig || thd->branch)
6136 p[0] = '|';
6142 else
6143 fptr = str;
6145 if(fptr){
6146 strsize -= (fptr - str);
6147 switch(ctype){
6148 case iFromTo:
6149 case iFromToNotNews:
6150 if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
6151 if(strsize-1 <= 4){
6152 strncpy(fptr, "To: ", strsize-1);
6153 fptr[strsize-1] = '\0';
6154 break;
6156 else{
6157 if((field = ((addr = fetch_to(idata))
6158 ? "To"
6159 : (addr = fetch_cc(idata))
6160 ? "Cc"
6161 : NULL))
6162 && set_index_addr(idata, field, addr, "To: ",
6163 strsize-1, fptr))
6164 break;
6166 if(ctype == iFromTo &&
6167 (newsgroups = fetch_newsgroups(idata)) &&
6168 *newsgroups){
6169 snprintf(fptr, strsize, "To: %-*.*s", (int)(strsize-1-4), (int)(strsize-1-4),
6170 newsgroups);
6171 break;
6174 /* else fall thru to From: */
6177 /* else fall thru to From: */
6179 if(idata->bogus)
6180 break;
6182 case iFrom:
6183 set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
6184 break;
6186 case iAddress:
6187 case iMailbox:
6188 if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
6189 char *mb = NULL, *hst = NULL, *at = NULL;
6190 size_t len;
6192 mb = addr->mailbox;
6193 if(ctype == iAddress && addr->host && addr->host[0]
6194 && addr->host[0] != '.'){
6195 at = "@";
6196 hst = addr->host;
6199 len = strlen(mb);
6200 if(!at || strsize-1 <= len)
6201 snprintf(fptr, strsize, "%-*.*s", (int)(strsize-1), (int)(strsize-1), mb);
6202 else
6203 snprintf(fptr, strsize, "%s@%-*.*s", mb, (int)(strsize-1-len-1), (int)(strsize-1-len-1), hst);
6206 break;
6208 default:
6209 break;
6212 replace_tabs_by_space(str);
6217 * Set up the elements contained in field so that they take up the
6218 * whole field width. Data is assumed to be UTF-8.
6220 void
6221 set_ielem_widths_in_field(IFIELD_S *ifield)
6223 IELEM_S *ielem = NULL;
6224 int datawidth, fmtwidth;
6226 if(!ifield)
6227 return;
6229 fmtwidth = ifield->width;
6231 for(ielem = ifield->ielem; ielem && fmtwidth > 0; ielem = ielem->next){
6232 if(!ifield->leftadj && ielem->next){
6233 dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield->ctype));
6234 assert(0);
6237 datawidth = (int) utf8_width(ielem->data);
6238 if(datawidth >= fmtwidth || !ielem->next){
6239 set_print_format(ielem, fmtwidth, ifield->leftadj);
6240 fmtwidth = 0;
6242 else{
6243 set_print_format(ielem, datawidth, ifield->leftadj);
6244 fmtwidth -= datawidth;
6251 * Simple hash function from K&R 2nd edition, p. 144.
6253 * This one is modified to never return 0 so we can use that as a special
6254 * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
6255 * as a special value that can't be returned by line_hash.
6257 unsigned long
6258 line_hash(char *s)
6260 unsigned long hashval;
6262 for(hashval = 0; *s != '\0'; s++)
6263 hashval = *s + 31 * hashval;
6265 hashval = hashval % LINE_HASH_N;
6267 if(!hashval)
6268 hashval++;
6270 return(hashval);
6275 * Returns nonzero if considered hidden, 0 if not considered hidden.
6278 msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
6280 int ret;
6282 if(flags & MH_ANYTHD){
6283 ret = ((any_lflagged(msgmap, MN_HIDE) > 0)
6284 && get_lflag(stream, msgmap, msgno, MN_HIDE));
6286 else if(flags & MH_THISTHD && THREADING() && sp_viewing_a_thread(stream)){
6287 ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
6288 || !get_lflag(stream, msgmap, msgno, MN_CHID2));
6290 else{
6291 if(THREADING() && sp_viewing_a_thread(stream)){
6292 ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
6293 || !get_lflag(stream, msgmap, msgno, MN_CHID2)
6294 || get_lflag(stream, msgmap, msgno, MN_CHID));
6296 else if(THRD_INDX()){
6298 * If this message is in the collapsed part of a thread,
6299 * it's hidden. It must be a top-level of a thread to be
6300 * considered visible. Even if it is top-level, it is only
6301 * visible if some message in the thread is not hidden.
6303 if(get_lflag(stream, msgmap, msgno, MN_CHID)) /* not top */
6304 ret = 1;
6305 else{
6306 unsigned long rawno;
6307 PINETHRD_S *thrd = NULL;
6309 rawno = mn_m2raw(msgmap, msgno);
6310 if(rawno)
6311 thrd = fetch_thread(stream, rawno);
6313 ret = !thread_has_some_visible(stream, thrd);
6316 else{
6317 ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
6318 && get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
6322 dprint((10,
6323 "msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
6325 return(ret);
6329 void
6330 adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
6332 long n, cur;
6333 int dir;
6335 cur = mn_get_cur(msgmap);
6337 /* if current is hidden, adjust */
6338 if(cur >= 1L && cur <= mn_get_total(msgmap)
6339 && msgline_hidden(stream, msgmap, cur, 0)){
6341 dir = mn_get_revsort(msgmap) ? -1 : 1;
6343 for(n = cur;
6344 ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
6345 && (n >= 1L && n <= mn_get_total(msgmap))
6346 && msgline_hidden(stream, msgmap, n, 0);
6347 n -= dir)
6350 if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
6351 && (n >= 1L && n <= mn_get_total(msgmap)))
6352 mn_reset_cur(msgmap, n);
6353 else{ /* no visible in that direction */
6354 for(n = cur;
6355 ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
6356 && (n >= 1L && n <= mn_get_total(msgmap))
6357 && msgline_hidden(stream, msgmap, n, 0);
6358 n += dir)
6361 if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
6362 && (n >= 1L && n <= mn_get_total(msgmap)))
6363 mn_reset_cur(msgmap, n);
6364 /* else trouble! */
6370 void
6371 setup_for_index_index_screen(void)
6373 format_index_line = format_index_index_line;
6374 setup_header_widths = setup_index_header_widths;
6378 void
6379 setup_for_thread_index_screen(void)
6381 format_index_line = format_thread_index_line;
6382 setup_header_widths = setup_thread_header_widths;
6386 unsigned long
6387 ice_hash(ICE_S *ice)
6389 char buf[MAX_SCREEN_COLS+1];
6391 buf[0] = '\0';
6393 if(ice)
6394 simple_index_line(buf, sizeof(buf), ice, 0L);
6396 buf[sizeof(buf) - 1] = '\0';
6398 return(line_hash(buf));
6402 char *
6403 left_adjust(int width)
6405 return(format_str(width, 1));
6409 char *
6410 right_adjust(int width)
6412 return(format_str(width, 0));
6417 * Returns allocated and filled in format string.
6419 char *
6420 format_str(int width, int left)
6422 char *format;
6423 size_t len;
6425 len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
6426 format = (char *) fs_get(len + 1);
6427 copy_format_str(width, left, format, len);
6428 format[len] = '\0';
6430 return(format);
6435 * Put the left or right adjusted format string of width width into
6436 * dest. Dest is of size n+1.
6438 char *
6439 copy_format_str(int width, int left, char *dest, int n)
6441 char *p;
6443 p = int2string(width);
6445 snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
6447 dest[n] = '\0';
6449 return(dest);
6454 * Sets up the print_format string to be width wide with left or right
6455 * adjust. Takes care of memory freeing and allocation.
6457 void
6458 set_print_format(IELEM_S *ielem, int width, int leftadj)
6460 if(ielem){
6461 ielem->wid = width;
6463 if(ielem->print_format){
6464 /* is there enough room? */
6465 if(ielem->freeprintf < PRINT_FORMAT_LEN(width,leftadj)+1){
6466 fs_resize((void **) &ielem->print_format,
6467 (PRINT_FORMAT_LEN(width,leftadj)+1) * sizeof(char));
6468 ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
6471 copy_format_str(width, leftadj, ielem->print_format,
6472 PRINT_FORMAT_LEN(width,leftadj));
6474 else{
6475 ielem->print_format = leftadj ? left_adjust(width)
6476 : right_adjust(width);
6477 ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);