1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: mailindx.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
5
/* ========================================================================
6
* Copyright 2006-2007 University of Washington
8
* Licensed under the Apache License, Version 2.0 (the "License");
9
* you may not use this file except in compliance with the License.
10
* You may obtain a copy of the License at
12
* http://www.apache.org/licenses/LICENSE-2.0
14
* ========================================================================
17
#include "../pith/headers.h"
18
#include "../pith/mailindx.h"
19
#include "../pith/mailview.h"
20
#include "../pith/flag.h"
21
#include "../pith/icache.h"
22
#include "../pith/msgno.h"
23
#include "../pith/thread.h"
24
#include "../pith/strlst.h"
25
#include "../pith/status.h"
26
#include "../pith/mailcmd.h"
27
#include "../pith/search.h"
28
#include "../pith/charset.h"
29
#include "../pith/reply.h"
30
#include "../pith/bldaddr.h"
31
#include "../pith/addrstring.h"
32
#include "../pith/news.h"
33
#include "../pith/util.h"
34
#include "../pith/pattern.h"
35
#include "../pith/sequence.h"
36
#include "../pith/color.h"
37
#include "../pith/stream.h"
38
#include "../pith/string.h"
39
#include "../pith/send.h"
40
#include "../pith/options.h"
42
#include "../pico/osdep/mswin.h"
46
* pointers to formatting functions
48
ICE_S *(*format_index_line)(INDEXDATA_S *);
49
void (*setup_header_widths)(MAILSTREAM *);
52
* pointer to optional load_overview functionality
54
void (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
57
* pointer to hook for saving index format state
59
void (*pith_opt_save_index_state)(int);
62
* hook to allow caller to insert cue that indicates a condensed
63
* thread relationship cue
65
int (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, int, int);
71
void setup_for_thread_index_screen(void);
72
ICE_S *format_index_index_line(INDEXDATA_S *);
73
ICE_S *format_thread_index_line(INDEXDATA_S *);
74
int set_index_addr(INDEXDATA_S *, char *, ADDRESS *, char *, int, char *, char **);
75
int ctype_is_fixed_length(IndexColType);
76
void setup_index_header_widths(MAILSTREAM *);
77
void setup_thread_header_widths(MAILSTREAM *);
78
int parse_index_format(char *, INDEX_COL_S **);
79
int index_in_overview(MAILSTREAM *);
80
ADDRESS *fetch_from(INDEXDATA_S *);
81
ADDRESS *fetch_sender(INDEXDATA_S *);
82
char *fetch_newsgroups(INDEXDATA_S *);
83
char *fetch_subject(INDEXDATA_S *);
84
char *fetch_date(INDEXDATA_S *);
85
long fetch_size(INDEXDATA_S *);
86
BODY *fetch_body(INDEXDATA_S *);
87
char *fetch_firsttext(INDEXDATA_S *idata);
88
void subj_str(INDEXDATA_S *, int, char *, SubjKW, int, ICE_S *);
89
void key_str(INDEXDATA_S *, SubjKW, ICE_S *);
90
void from_str(IndexColType, INDEXDATA_S *, int, char *, ICE_S *);
91
int day_of_week(struct date *);
92
int day_of_year(struct date *);
93
unsigned long ice_hash(ICE_S *);
94
char *left_adjust(int);
95
char *right_adjust(int);
96
char *format_str(int, int);
97
char *copy_format_str(int, int, char *, int);
98
void set_print_format(IELEM_S *, int, int);
99
void set_ielem_widths_in_field(IFIELD_S *);
102
#define BIGWIDTH 2047
105
/*----------------------------------------------------------------------
106
Initialize the index_disp_format array in ps_global from this
109
Args: format -- the string containing the format tokens
110
answer -- put the answer here, free first if there was a previous
114
init_index_format(char *format, INDEX_COL_S **answer)
119
* Record the fact that SCORE appears in some index format. This
120
* is a heavy-handed approach. It will stick at 1 if any format ever
121
* contains score during this session. This is ok since it will just
122
* cause recalculation if wrong and these things rarely change much.
124
if(!ps_global->a_format_contains_score && format
125
&& strstr(format, "SCORE")){
126
ps_global->a_format_contains_score = 1;
127
/* recalculate need for scores */
128
scores_are_used(SCOREUSE_INVALID);
131
set_need_format_setup(ps_global->mail_stream);
132
/* if custom format is specified, try it, else go with default */
133
if(!(format && *format && parse_index_format(format, answer))){
134
static INDEX_COL_S answer_default[] = {
136
{iMessNo, WeCalculate},
137
{iSDateTime, WeCalculate},
138
{iFromTo, Percent, 33}, /* percent of rest */
139
{iSizeNarrow, WeCalculate},
140
{iSubjKey, Percent, 67},
145
fs_give((void **)answer);
147
*answer = (INDEX_COL_S *)fs_get(sizeof(answer_default));
148
memcpy(*answer, answer_default, sizeof(answer_default));
152
* Fill in req_width's for WeCalculate items.
154
for(column = 0; (*answer)[column].ctype != iNothing; column++){
155
if((*answer)[column].wtype == WeCalculate){
156
switch((*answer)[column].ctype){
158
(*answer)[column].req_width = 1;
166
(*answer)[column].req_width = 2;
173
(*answer)[column].req_width = 3;
177
(*answer)[column].req_width = 4;
182
(*answer)[column].req_width = 5;
188
(*answer)[column].req_width = 6;
194
(*answer)[column].req_width = 7;
202
(*answer)[column].req_width = 8;
209
* Format a date to see how long it is.
210
* Make it as least as long as "Yesterday".
211
* We should really use the width of the longest
212
* of the translated yesterdays and friends but...
217
memset(&tm, 0, sizeof(tm));
221
strftime(ss, sizeof(ss), "%x", &tm);
222
(*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), 20);
227
case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
229
case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
230
case iSDateTimeIsoS24:
231
case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
235
* These SDates are 8 wide but they need to be 9 for "Yesterday".
237
(*answer)[column].req_width = 9;
240
case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
241
(*answer)[column].req_width = 10;
244
(*answer)[column].req_width = 12;
247
(*answer)[column].req_width = 16;
256
reset_index_format(void)
258
long rflags = ROLE_DO_OTHER;
263
if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
264
for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
265
if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL,
266
NULL, NULL, SO_NOSERVER|SE_NOPREFETCH))
270
if(pat && pat->action && !pat->action->bogus
271
&& pat->action->index_format){
273
init_index_format(pat->action->index_format,
274
&ps_global->index_disp_format);
279
init_index_format(ps_global->VAR_INDEX_FORMAT,
280
&ps_global->index_disp_format);
284
/* popular ones first to make it slightly faster */
285
static INDEX_PARSE_T itokens[] = {
286
{"STATUS", iStatus, FOR_INDEX},
287
{"MSGNO", iMessNo, FOR_INDEX},
288
{"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
289
{"FROMORTO", iFromTo, FOR_INDEX},
290
{"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX},
291
{"SIZE", iSize, FOR_INDEX},
292
{"SIZECOMMA", iSizeComma, FOR_INDEX},
293
{"SIZENARROW", iSizeNarrow, FOR_INDEX},
294
{"KSIZE", iKSize, FOR_INDEX},
295
{"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
296
{"FULLSTATUS", iFStatus, FOR_INDEX},
297
{"IMAPSTATUS", iIStatus, FOR_INDEX},
298
{"SUBJKEY", iSubjKey, FOR_INDEX},
299
{"SUBJKEYINIT", iSubjKeyInit, FOR_INDEX},
300
{"SUBJECTTEXT", iSubjectText, FOR_INDEX},
301
{"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX},
302
{"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX},
303
{"KEY", iKey, FOR_INDEX},
304
{"KEYINIT", iKeyInit, FOR_INDEX},
305
{"DESCRIPSIZE", iDescripSize, FOR_INDEX},
306
{"ATT", iAtt, FOR_INDEX},
307
{"SCORE", iScore, FOR_INDEX},
308
{"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
309
{"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
310
{"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
311
{"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
312
{"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
313
{"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
314
{"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
315
{"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
316
{"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
317
{"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
318
{"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
319
{"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
320
{"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
321
{"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
322
{"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
323
{"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
324
{"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
325
{"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
326
{"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
327
{"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
328
{"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
329
{"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
330
{"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
331
{"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
332
{"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
333
{"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
334
{"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
335
{"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
336
{"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
337
{"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
338
{"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
339
{"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
340
{"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
341
{"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
342
{"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
343
{"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
344
{"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
345
{"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
346
{"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
347
{"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
348
{"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
349
{"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
350
{"NEWSANDTO", iNewsAndTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
351
{"RECIPSANDNEWS", iRecipsAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
352
{"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
353
{"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE},
354
{"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE},
355
{"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
356
{"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
357
{"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
358
{"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
359
{"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
360
{"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
361
{"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
362
{"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
363
{"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
364
{"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
365
{"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
366
{"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE},
367
{"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
368
{"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
369
{"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
370
{"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
371
{"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
372
{"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
373
{"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
374
{"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
375
{"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
376
{"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
377
FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
378
{"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
379
{"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
380
{"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
381
{"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
382
{"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
383
{"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
384
{"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
385
{"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
386
{"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
387
{"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
388
{"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
389
{"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
390
FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
391
{"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
392
{"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
393
{"ARROW", iArrow, FOR_INDEX},
394
{"CURSORPOS", iCursorPos, FOR_TEMPLATE},
395
{NULL, iNothing, FOR_NOTHING}
401
return((i < sizeof(itokens) && itokens[i].name) ? &itokens[i] : NULL);
406
* Args txt -- The token being checked begins at the beginning
407
* of txt. The end of the token is delimited by a null, or
408
* white space, or an underscore if DELIM_USCORE is set,
409
* or a left paren if DELIM_PAREN is set.
410
* flags -- Flags contains the what_for value, and DELIM_ values.
412
* Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
415
itoktype(char *txt, int flags)
422
* Separate a copy of the possible token out of txt.
426
while(w < token+100 &&
428
!isspace((unsigned char)*v) &&
429
!(flags & DELIM_USCORE && *v == '_') &&
430
!(flags & DELIM_PAREN && *v == '('))
435
for(pt = itokens; pt->name; pt++)
436
if(pt->what_for & flags && !strucmp(pt->name, token))
444
parse_index_format(char *format_str, INDEX_COL_S **answer)
449
INDEX_COL_S cdesc[200]; /* plenty of temp storage for answer */
451
memset((void *)cdesc, 0, sizeof(cdesc));
454
while(p && *p && column < 200-1){
455
/* skip leading white space for next word */
456
p = skip_white_space(p);
457
pt = itoktype(p, FOR_INDEX | DELIM_PAREN);
459
/* ignore unrecognized word */
461
for(q = p; *p && !isspace((unsigned char)*p); p++)
468
"parse_index_format: unrecognized token: %s\n",
470
q_status_message1(SM_ORDER | SM_DING, 0, 3,
471
_("Unrecognized word in index-format: %s"), q);
475
cdesc[column].ctype = pt->ctype;
477
/* skip over name and look for parens */
478
p += strlen(pt->name);
482
while(p && *p && isdigit((unsigned char) *p))
485
if(p && *p && *p == ')' && p > q){
486
cdesc[column].wtype = Fixed;
487
cdesc[column].req_width = atoi(q);
489
else if(p && *p && *p == '%' && p > q){
490
cdesc[column].wtype = Percent;
491
cdesc[column].req_width = atoi(q);
494
cdesc[column].wtype = WeCalculate;
495
cdesc[column].req_width = 0;
499
cdesc[column].wtype = WeCalculate;
500
cdesc[column].req_width = 0;
504
/* skip text at end of word */
505
while(p && *p && !isspace((unsigned char)*p))
509
/* if, after all that, we didn't find anything recognizable, bitch */
511
dprint((1, "Completely unrecognizable index-format\n"));
512
q_status_message(SM_ORDER | SM_DING, 0, 3,
513
_("Configured \"index-format\" unrecognizable. Using default."));
517
/* Finish with Nothing column */
518
cdesc[column].ctype = iNothing;
520
/* free up old answer */
522
fs_give((void **)answer);
524
/* allocate space for new answer */
525
*answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
526
memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
527
/* copy answer to real place */
528
for(i = 0; i <= column; i++)
529
(*answer)[i] = cdesc[i];
536
* These types are basically fixed in width.
537
* The order is slightly significant. The ones towards the front of the
538
* list get space allocated sooner than the ones at the end of the list.
540
static IndexColType fixed_ctypes[] = {
541
iMessNo, iStatus, iFStatus, iIStatus, iDate, iSDate, iSDateTime, iSDateTime24,
543
iS1Date, iS2Date, iS3Date, iS4Date, iDateIso, iDateIsoS,
544
iSDateIso, iSDateIsoS,
545
iSDateS1, iSDateS2, iSDateS3, iSDateS4,
546
iSDateTimeIso, iSDateTimeIsoS,
547
iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
548
iSDateTimeIso24, iSDateTimeIsoS24,
549
iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
550
iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize,
551
iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit,
552
iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore
557
ctype_is_fixed_length(IndexColType ctype)
562
if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
565
if(ctype == fixed_ctypes[j])
573
/*----------------------------------------------------------------------
574
Setup the widths of the various columns in the index display
577
setup_index_header_widths(MAILSTREAM *stream)
579
int j, columns, some_to_calculate;
580
int space_left, screen_width, width, fix, col;
581
int keep_going, tot_pct, was_sl;
586
max_msgno = mn_get_total(ps_global->msgmap);
588
dprint((8, "=== setup_index_header_widths() ===\n"));
590
clear_icache_flags(stream);
591
screen_width = ps_global->ttyo->screen_cols;
592
space_left = screen_width;
593
columns = some_to_calculate = 0;
596
* Calculate how many fields there are so we know how many spaces
597
* between columns to reserve. Fill in Fixed widths now. Reserve
598
* special case WeCalculate with non-zero req_widths before doing
599
* Percent cases below.
601
for(cdesc = ps_global->index_disp_format;
602
cdesc->ctype != iNothing;
605
if(cdesc->wtype == Fixed){
606
cdesc->width = cdesc->req_width;
610
else if(cdesc->wtype == Percent){
611
cdesc->width = 0; /* calculated later */
614
else{ /* WeCalculate */
615
cdesc->width = cdesc->req_width; /* reserve this for now */
620
space_left -= cdesc->width;
623
space_left -= (columns - 1); /* space between columns */
625
ps_global->display_keywords_in_subject = 0;
626
ps_global->display_keywordinits_in_subject = 0;
629
* Set the actual lengths for the fixed width fields and set up
630
* the left or right adjustment for everything.
631
* There should be a case setting actual_length for all of the types
634
for(cdesc = ps_global->index_disp_format;
635
cdesc->ctype != iNothing;
638
wtype = cdesc->wtype;
640
if(cdesc->ctype == iSubjKey || cdesc->ctype == iSubjKeyText)
641
ps_global->display_keywords_in_subject = 1;
642
else if(cdesc->ctype == iSubjKeyInit || cdesc->ctype == iSubjKeyInitText)
643
ps_global->display_keywordinits_in_subject = 1;
645
if(wtype == WeCalculate || wtype == Percent || cdesc->width != 0){
646
if(ctype_is_fixed_length(cdesc->ctype)){
647
switch(cdesc->ctype){
649
cdesc->actual_length = 1;
650
cdesc->adjustment = Left;
656
cdesc->actual_length = 2;
657
cdesc->adjustment = Left;
661
cdesc->actual_length = 2;
662
cdesc->adjustment = Right;
668
cdesc->actual_length = 3;
669
cdesc->adjustment = Left;
673
set_format_includes_msgno(stream);
675
cdesc->actual_length = 3;
676
else if(max_msgno < 10000)
677
cdesc->actual_length = 4;
678
else if(max_msgno < 100000)
679
cdesc->actual_length = 5;
681
cdesc->actual_length = 6;
683
cdesc->adjustment = Right;
687
cdesc->actual_length = 4;
688
cdesc->adjustment = Left;
693
cdesc->actual_length = 5;
694
cdesc->adjustment = Left;
698
cdesc->actual_length = 5;
699
cdesc->adjustment = Right;
705
cdesc->actual_length = 6;
706
cdesc->adjustment = Left;
710
cdesc->actual_length = 6;
711
cdesc->adjustment = Right;
717
cdesc->actual_length = 7;
718
cdesc->adjustment = Right;
722
set_format_includes_smartdate(stream);
723
cdesc->actual_length = 7;
724
cdesc->adjustment = Left;
732
cdesc->actual_length = 8;
733
cdesc->adjustment = Left;
737
cdesc->actual_length = 8;
738
cdesc->adjustment = Right;
744
cdesc->actual_length = cdesc->req_width;
745
cdesc->adjustment = Left;
749
case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
751
case iSDateTimeIsoS24:
752
case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
753
case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
754
case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
755
set_format_includes_smartdate(stream);
756
if(cdesc->ctype == iSDateIso
757
|| cdesc->ctype == iSDateTimeIso
758
|| cdesc->ctype == iSDateTimeIso24)
759
cdesc->actual_length = 10;
761
cdesc->actual_length = 9;
763
cdesc->adjustment = Left;
767
cdesc->actual_length = 9;
768
cdesc->adjustment = Right;
772
cdesc->actual_length = 10;
773
cdesc->adjustment = Left;
777
cdesc->actual_length = 12;
778
cdesc->adjustment = Left;
782
panic("Unhandled fixed case in setup_index_header");
787
cdesc->adjustment = Left;
791
if(ps_global->display_keywords_in_subject)
792
ps_global->display_keywordinits_in_subject = 0;
794
/* if have reserved unneeded space for size, give it back */
795
for(cdesc = ps_global->index_disp_format;
796
cdesc->ctype != iNothing;
798
if(cdesc->ctype == iSize || cdesc->ctype == iKSize ||
799
cdesc->ctype == iSizeNarrow ||
800
cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){
801
if(cdesc->actual_length == 0){
802
if((fix=cdesc->width) > 0){ /* had this reserved */
807
space_left++; /* +1 for space between columns */
812
* Calculate the field widths that are basically fixed in width.
813
* Do them in this order in case we don't have enough space to go around.
814
* The set of fixed_ctypes here is the same as the set where we
815
* set the actual_lengths above.
817
for(j = 0; space_left > 0 && some_to_calculate; j++){
819
if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
822
for(cdesc = ps_global->index_disp_format;
823
cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
825
if(cdesc->ctype == fixed_ctypes[j] && cdesc->wtype == WeCalculate){
827
fix = MIN(cdesc->actual_length - cdesc->width, space_left);
834
* Fill in widths for Percent cases. If there are no more to calculate,
835
* use the percentages as relative numbers and use the rest of the space,
836
* else treat them as absolute percentages of the original avail screen.
839
if(some_to_calculate){
840
int tot_requested = 0;
843
* Requests are treated as percent of screen width. See if they
844
* will all fit. If not, trim them back proportionately.
846
for(cdesc = ps_global->index_disp_format;
847
cdesc->ctype != iNothing;
849
if(cdesc->wtype == Percent){
850
/* The 2, 200, and +100 are because we're rounding */
851
fix = ((2*cdesc->req_width *
852
(screen_width-(columns-1)))+100) / 200;
853
tot_requested += fix;
857
if(tot_requested > space_left){
858
int multiplier = (100 * space_left) / tot_requested;
860
for(cdesc = ps_global->index_disp_format;
861
cdesc->ctype != iNothing && space_left > 0;
863
if(cdesc->wtype == Percent){
864
/* The 2, 200, and +100 are because we're rounding */
865
fix = ((2*cdesc->req_width *
866
(screen_width-(columns-1)))+100) / 200;
867
fix = (2 * fix * multiplier + 100) / 200;
868
fix = MIN(fix, space_left);
875
for(cdesc = ps_global->index_disp_format;
876
cdesc->ctype != iNothing && space_left > 0;
878
if(cdesc->wtype == Percent){
879
/* The 2, 200, and +100 are because we're rounding */
880
fix = ((2*cdesc->req_width *
881
(screen_width-(columns-1)))+100) / 200;
882
fix = MIN(fix, space_left);
892
/* add up total percentages requested */
893
for(cdesc = ps_global->index_disp_format;
894
cdesc->ctype != iNothing;
896
if(cdesc->wtype == Percent)
897
tot_pct += cdesc->req_width;
899
/* give relative weight to requests */
900
for(cdesc = ps_global->index_disp_format;
901
cdesc->ctype != iNothing && space_left > 0 && tot_pct > 0;
903
if(cdesc->wtype == Percent){
904
fix = ((2*cdesc->req_width*was_sl)+tot_pct) / (2*tot_pct);
905
fix = MIN(fix, space_left);
913
/* split up rest, give twice as much to Subject */
915
while(space_left > 0 && keep_going){
917
for(cdesc = ps_global->index_disp_format;
918
cdesc->ctype != iNothing && space_left > 0;
920
if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
924
if(space_left > 0 && (cdesc->ctype == iSubject
925
|| cdesc->ctype == iSubjectText
926
|| cdesc->ctype == iSubjKey
927
|| cdesc->ctype == iSubjKeyText
928
|| cdesc->ctype == iSubjKeyInit
929
|| cdesc->ctype == iSubjKeyInitText)){
937
/* if still more, pad out percent's */
939
while(space_left > 0 && keep_going){
941
for(cdesc = ps_global->index_disp_format;
942
cdesc->ctype != iNothing && space_left > 0;
944
if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
952
/* if user made Fixed fields too big, give back space */
954
while(space_left < 0 && keep_going){
956
for(cdesc = ps_global->index_disp_format;
957
cdesc->ctype != iNothing && space_left < 0;
959
if(cdesc->wtype == Fixed && cdesc->width > 0){
967
if(pith_opt_save_index_state)
968
(*pith_opt_save_index_state)(FALSE);
973
setup_thread_header_widths(MAILSTREAM *stream)
975
clear_icache_flags(stream);
976
if(pith_opt_save_index_state)
977
(*pith_opt_save_index_state)(TRUE);
982
* load_overview - c-client call back to gather overview data
984
* Note: if we never get called, UID represents a hole
985
* if we're passed a zero UID, totally bogus overview data
986
* if we're passed a zero obuf, mostly bogus overview data
989
load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
991
if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
995
memset(&idata, 0, sizeof(INDEXDATA_S));
999
* Only really load the thing if we've got an NNTP stream
1000
* otherwise we're just using mail_fetch_overview to load the
1001
* IMAP envelope cache with the specific set of messages
1004
idata.stream = stream;
1005
idata.rawno = rawno;
1006
idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
1007
idata.size = obuf->optional.octets;
1008
idata.from = obuf->from;
1009
idata.date = obuf->date;
1010
idata.subject = obuf->subject;
1012
ice = (*format_index_line)(&idata);
1013
if(idata.bogus && ice){
1016
clear_ice(&ice->tice);
1021
else if(F_OFF(F_QUELL_NEWS_ENV_CB, ps_global)
1022
&& (!THRD_INDX() || (ice && ice->tice))
1023
&& !msgline_hidden(stream, sp_msgmap(stream), idata.msgno, 0)
1024
&& pith_opt_paint_index_hline){
1025
(*pith_opt_paint_index_hline)(stream, idata.msgno, ice);
1032
build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
1033
long int msgno, long int top_msgno, int msgcount, int *fetched)
1037
long n, i, cnt, rawno, visible, limit = -1L;
1039
rawno = mn_m2raw(msgmap, msgno);
1043
ice = fetch_ice(stream, rawno);
1044
if(ice->tice && ice->tice->ifield
1045
&& ice->tice->color_lookup_done && ice->tice->widths_done){
1047
char buf[MAX_SCREEN_COLS+1];
1048
simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice->tice, msgno);
1050
dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
1053
buf[0] ? strlen(buf) : 0));
1058
if((ice = fetch_ice(stream, rawno))->ifield
1059
&& ice->color_lookup_done && ice->widths_done){
1061
char buf[MAX_SCREEN_COLS+1];
1062
simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice, msgno);
1064
dprint((9, "Hit: Returning %p -> <%s (%d)\n",
1067
buf[0] ? strlen(buf) : 0));
1073
* If we are in THRD_INDX() and the width changed we don't currently
1074
* have a method of fixing just the widths and print_format strings.
1075
* Instead, we clear the index cache entry and start over.
1077
if(THRD_INDX() && ice && ice->tice && ice->tice->ifield
1078
&& !ice->tice->widths_done){
1079
clear_ice(&ice->tice);
1083
* Fetch everything we need to start filling in the index line
1084
* explicitly via mail_fetch_overview. On an nntp stream
1085
* this has the effect of building the index lines in the
1086
* load_overview callback. Under IMAP we're either getting
1087
* the envelope data via the imap_envelope callback or
1088
* preloading the cache. Either way, we're getting exactly
1089
* what we want rather than relying on linear lookahead sort
1092
if(!(fetched && *fetched) && index_in_overview(stream)
1093
&& ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1094
|| (!THRD_INDX() && !ice->ifield))){
1104
/* clear sequence bits */
1105
for(n = 1L; n <= stream->nmsgs; n++)
1106
if((mc = mail_elt(stream, n)) != NULL)
1110
* Light interesting bits
1111
* NOTE: not set above because m2raw's cheaper
1112
* than raw2m for every message
1116
* Unfortunately, it is expensive to calculate visible pages
1117
* in thread index if we are zoomed, so we don't try.
1119
if(THRD_INDX() && any_lflagged(msgmap, MN_HIDE))
1120
visible = msgmap->visible_threads;
1121
else if(THREADING() && sp_viewing_a_thread(stream)){
1123
* We know that all visible messages in the thread are marked
1126
for(visible = 0L, n = top_msgno;
1127
visible < msgcount && n <= msgcount;
1130
if(!get_lflag(stream, msgmap, n, MN_CHID2))
1133
if(!msgline_hidden(stream, msgmap, n, 0))
1139
visible = mn_get_total(msgmap)
1140
- any_lflagged(msgmap, MN_HIDE|MN_CHID);
1142
limit = MIN(visible, msgcount);
1147
thrd = fetch_thread(stream, mn_m2raw(msgmap, top_msgno));
1149
* Loop through visible threads, marking them for fetching.
1150
* Stop at end of screen or sooner if we run out of visible
1155
n = mn_raw2m(msgmap, thrd->rawno);
1157
&& n <= mn_get_total(msgmap)
1158
&& !((ic=fetch_ice(stream,thrd->rawno)->tice)
1160
count += mark_msgs_in_thread(stream, thrd, msgmap);
1166
/* find next thread which is visible */
1168
if(mn_get_revsort(msgmap) && thrd->prevthd)
1169
thrd = fetch_thread(stream, thrd->prevthd);
1170
else if(!mn_get_revsort(msgmap) && thrd->nextthd)
1171
thrd = fetch_thread(stream, thrd->nextthd);
1175
&& msgline_hidden(stream, msgmap,
1176
mn_raw2m(msgmap, thrd->rawno), 0));
1184
&& n <= mn_get_total(msgmap)
1185
&& !fetch_ice(stream, (rawno=mn_m2raw(msgmap,n)))->ifield){
1186
if(thrd = fetch_thread(stream, rawno)){
1188
* If we're doing a MUTTLIKE display the index line
1189
* may depend on the thread parent, and grandparent,
1190
* and further back. So just fetch the whole thread
1194
&& ps_global->thread_disp_style == THREAD_MUTTLIKE
1196
thrd = fetch_thread(stream, thrd->top);
1198
count += mark_msgs_in_thread(stream, thrd, msgmap);
1200
else if(rawno > 0L && rawno <= stream->nmsgs
1201
&& (mc = mail_elt(stream,rawno))
1202
&& !mc->private.msg.env){
1211
/* find next n which is visible */
1212
while(++n <= mn_get_total(msgmap)
1213
&& msgline_hidden(stream, msgmap, n, 0))
1219
seq = build_sequence(stream, NULL, NULL);
1221
ps_global->dont_count_flagchanges = 1;
1222
mail_fetch_overview_sequence(stream, seq,
1223
(stream->dtb && stream->dtb->name
1224
&& !strcmp(stream->dtb->name, "imap"))
1225
? NULL : load_overview);
1226
ps_global->dont_count_flagchanges = 0;
1227
fs_give((void **) &seq);
1232
* reassign ice from the cache as it may've been built
1233
* within the overview callback or it may have become stale
1234
* in the prior sequence bit setting loop ...
1236
rawno = mn_m2raw(msgmap, msgno);
1237
ice = fetch_ice(stream, rawno);
1240
if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
1241
|| (!THRD_INDX() && !ice->ifield)){
1245
* With pre-fetching/callback-formatting done and no success,
1246
* fall into formatting the requested line...
1248
memset(&idata, 0, sizeof(INDEXDATA_S));
1249
idata.stream = stream;
1250
idata.msgno = msgno;
1251
idata.rawno = mn_m2raw(msgmap, msgno);
1252
if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
1253
&& (mc = mail_elt(stream, idata.rawno))){
1254
idata.size = mc->rfc822_size;
1255
index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
1260
ice = (*format_index_line)(&idata);
1264
* If needed, reset the print_format strings so that they add up to
1265
* the right total width. The reset width functionality isn't implemented
1266
* for THRD_INDX() so we are just doing a complete rebuild in that
1267
* case. This is driven by the clear_ice() call in clear_index_cache_ent()
1268
* so it should never be the case that THRD_INDX() is true and only
1269
* widths_done needs to be fixed.
1271
if((!THRD_INDX() && ice->ifield && !ice->widths_done)){
1278
if(need_format_setup(stream))
1279
setup_header_widths(stream);
1282
working_ice = ice ? ice->tice : NULL;
1288
* First fix the ifield widths. The cdescs with nonzero widths
1289
* should correspond to the ifields that are defined.
1291
ifield = working_ice->ifield;
1292
for(cdesc = ps_global->index_disp_format;
1293
cdesc->ctype != iNothing && ifield; cdesc++){
1295
if(cdesc->ctype != ifield->ctype){
1296
dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno, (int) cdesc->ctype, (int) ifield->ctype));
1300
ifield->width = cdesc->width;
1301
ifield = ifield->next;
1305
/* fix the print_format strings and widths */
1306
for(ifield = working_ice->ifield; ifield; ifield = ifield->next)
1307
set_ielem_widths_in_field(ifield);
1310
working_ice->widths_done = 1;
1313
if(THRD_INDX() && ice->tice)
1314
ice->tice->color_lookup_done = 1;
1317
* Look for a color for this line (and other lines in the current
1318
* view). This does a SEARCH for each role which has a color until
1319
* it finds a match. This will be satisfied by the c-client
1320
* cache created by the mail_fetch_overview above if it is a header
1323
if(!THRD_INDX() && !ice->color_lookup_done){
1324
COLOR_PAIR *linecolor;
1327
PAT_STATE *pstate = NULL;
1329
if(pico_usingcolor()){
1331
if(THREADING() && sp_viewing_a_thread(stream)){
1332
for(visible = 0L, n = top_msgno;
1333
visible < msgcount && n <= mn_get_total(msgmap);
1336
if(!get_lflag(stream, msgmap, n, MN_CHID2))
1339
if(!msgline_hidden(stream, msgmap, n, 0))
1345
visible = mn_get_total(msgmap)
1346
- any_lflagged(msgmap, MN_HIDE|MN_CHID);
1348
limit = MIN(visible, msgcount);
1350
/* clear sequence bits */
1351
for(n = 1L; n <= stream->nmsgs; n++)
1352
if((mc = mail_elt(stream, n)) != NULL)
1359
&& n <= mn_get_total(msgmap)
1360
&& !fetch_ice(stream,(rawno = mn_m2raw(msgmap, n)))->color_lookup_done){
1362
if(rawno >= 1L && rawno <= stream->nmsgs
1363
&& (mc = mail_elt(stream, rawno))){
1372
/* find next n which is visible */
1373
while(++n <= mn_get_total(msgmap)
1374
&& msgline_hidden(stream, msgmap, n, 0))
1379
* Why is there a loop here? The first call to get_index_line_color
1380
* will return a set of messages which match one of the roles.
1381
* Then, we eliminate those messages from the search set and try
1382
* again. This time we'd get past that role and into a different
1383
* role. Because of that, we hang onto the state and don't reset
1384
* to the first_pattern on the second and subsequent times
1385
* through the loop, avoiding fruitless match_pattern calls in
1386
* get_index_line_color.
1387
* Before the first call, pstate should be set to NULL.
1390
ss = build_searchset(stream);
1395
colormatch = get_index_line_color(stream, ss, &pstate,
1399
* Assign this color to all matched msgno's and
1400
* turn off the sequence bit so we won't check
1404
for(s = ss; s; s = s->next){
1405
for(n = s->first; n <= s->last; n++){
1406
if(n >= 1L && n <= stream->nmsgs
1407
&& (mc = mail_elt(stream, n))
1411
ic = fetch_ice(stream, n);
1412
ic->color_lookup_done = 1;
1414
ic->linecolor = new_color_pair(linecolor->fg,
1421
free_color_pair(&linecolor);
1424
/* have to mark the rest of the lookups done */
1425
for(s = ss; s && cnt > 0; s = s->next){
1426
for(n = s->first; n <= s->last && cnt > 0; n++){
1427
if(n >= 1L && n <= stream->nmsgs
1428
&& (mc = mail_elt(stream, n))
1431
ic = fetch_ice(stream, n);
1432
ic->color_lookup_done = 1;
1437
/* just making sure */
1441
mail_free_searchset(&ss);
1447
ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
1450
ice->color_lookup_done = 1;
1453
return(ice); /* Return formatted index data */
1458
day_of_week(struct date *d)
1469
m -= 3; /* March is month 0 */
1471
return((d->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100))%7);
1475
static int daytab[2][13] = {
1476
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
1477
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
1480
static char *day_name[] = {N_("Sunday"),N_("Monday"),N_("Tuesday"),N_("Wednesday"),
1481
N_("Thursday"),N_("Friday"),N_("Saturday")};
1484
day_of_year(struct date *d)
1488
if(d->year <= 0 || d->month < 1 || d->month > 12)
1492
leap = d->year%4 == 0 && d->year%100 != 0 || d->year%400 == 0;
1493
for(i = 1; i < d->month; i++)
1494
doy += daytab[leap][i];
1501
/*----------------------------------------------------------------------
1502
Format a string summarizing the message header for index on screen
1504
Args: buffer -- buffer to place formatted line
1505
idata -- snot it takes to format the line
1507
Result: returns pointer given buffer IF entry formatted
1508
else NULL if there was a problem (but the buffer is
1509
still suitable for display)
1512
format_index_index_line(INDEXDATA_S *idata)
1514
char str[BIGWIDTH+1], to_us, status, *field,
1515
*buffer, *s_tmp, *p, *newsgroups;
1516
int i, j, smallest, collapsed = 0,
1521
ADDRESS *addr, *toaddr, *ccaddr, *last_to;
1522
PINETHRD_S *thrd = NULL;
1523
INDEX_COL_S *cdesc = NULL;
1527
struct variable *vars = ps_global->vars;
1529
dprint((8, "=== format_index_line(%ld,%ld) ===\n",
1530
idata ? idata->msgno : -1, idata ? idata->rawno : -1));
1533
ice = fetch_ice(idata->stream, idata->rawno);
1535
fs_give((void **) &ice->charset);
1537
free_ifield(&ice->ifield);
1539
/* is this a collapsed thread index line? */
1540
if(!idata->bogus && THREADING()){
1541
thrd = fetch_thread(idata->stream, idata->rawno);
1542
collapsed = thrd && thrd->next
1543
&& get_lflag(idata->stream, NULL,
1544
idata->rawno, MN_COLL);
1547
/* calculate contents of the required fields */
1548
for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
1550
memset(str, 0, sizeof(str));
1551
ifield = new_ifield(&ice->ifield);
1552
ifield->ctype = cdesc->ctype;
1553
ifield->width = cdesc->width;
1556
if(cdesc->ctype == iMessNo)
1557
snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
1558
else if(idata->bogus < 2 && (cdesc->ctype == iSubject
1559
|| cdesc->ctype == iSubjectText
1560
|| cdesc->ctype == iSubjKey
1561
|| cdesc->ctype == iSubjKeyText
1562
|| cdesc->ctype == iSubjKeyInit
1563
|| cdesc->ctype == iSubjKeyInitText))
1564
snprintf(str, sizeof(str), "%s", _("[ No Message Text Available ]"));
1567
switch(cdesc->ctype){
1569
to_us = status = ' ';
1571
thrd = fetch_thread(idata->stream, idata->rawno);
1572
to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
1573
status = status_symbol_for_thread(idata->stream, thrd,
1577
if((mc=mail_elt(idata->stream,idata->rawno)) && mc->flagged)
1578
to_us = '*'; /* simple */
1579
else if(!IS_NEWS(idata->stream)){
1580
for(addr = fetch_to(idata); addr; addr = addr->next)
1581
if(address_is_us(addr, ps_global)){
1589
if(to_us != '+' && resent_to_us(idata)){
1595
if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
1596
for(addr = fetch_cc(idata); addr; addr = addr->next)
1597
if(address_is_us(addr, ps_global)){
1604
status = (!idata->stream || !IS_NEWS(idata->stream)
1605
|| F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
1618
snprintf(str, sizeof(str), "%c %c", to_us, status);
1620
ifield->leftadj = 1;
1621
for(i = 0; i < 3; i++){
1622
ielem = new_ielem(&ifield->ielem);
1623
ielem->freedata = 1;
1624
ielem->data = (char *) fs_get(2 * sizeof(char));
1625
ielem->data[0] = str[i];
1626
ielem->data[1] = '\0';
1628
set_print_format(ielem, 1, ifield->leftadj);
1631
if(pico_usingcolor()){
1634
if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
1635
ielem = ifield->ielem;
1636
ielem->freecolor = 1;
1637
ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
1640
else if(str[0] == '+' || str[0] == '-'){
1641
if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
1642
ielem = ifield->ielem;
1643
ielem->freecolor = 1;
1644
ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
1649
if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
1650
ielem = ifield->ielem->next->next;
1651
ielem->freecolor = 1;
1652
ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
1655
else if(str[2] == 'A'){
1656
if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
1657
ielem = ifield->ielem->next->next;
1658
ielem->freecolor = 1;
1659
ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
1662
else if(str[2] == 'N'){
1663
if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
1664
ielem = ifield->ielem->next->next;
1665
ielem->freecolor = 1;
1666
ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
1676
char new, answered, deleted, flagged;
1679
thrd = fetch_thread(idata->stream, idata->rawno);
1680
to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
1684
if(!IS_NEWS(idata->stream)){
1685
for(addr = fetch_to(idata); addr; addr = addr->next)
1686
if(address_is_us(addr, ps_global)){
1691
if(to_us == ' ' && resent_to_us(idata))
1694
if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
1695
for(addr = fetch_cc(idata); addr; addr = addr->next)
1696
if(address_is_us(addr, ps_global)){
1703
new = answered = deleted = flagged = ' ';
1706
unsigned long save_branch, cnt, tot_in_thrd;
1709
* Branch is a sibling, not part of the thread, so
1710
* don't consider it when displaying this line.
1712
save_branch = thrd->branch;
1715
tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
1718
cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
1720
deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
1722
cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
1724
answered = (cnt == tot_in_thrd) ? 'A' : 'a';
1726
/* no lower case *, same thing for some or all */
1727
if(count_flags_in_thread(idata->stream, thrd, F_FLAG))
1730
new = status_symbol_for_thread(idata->stream, thrd,
1733
thrd->branch = save_branch;
1736
mc = (idata->rawno > 0L && idata->stream
1737
&& idata->rawno <= idata->stream->nmsgs)
1738
? mail_elt(idata->stream, idata->rawno) : NULL;
1739
if(mc && mc->valid){
1740
if(cdesc->ctype == iIStatus){
1742
new = mc->seen ? 'R' : 'N';
1747
&& (!IS_NEWS(idata->stream)
1748
|| F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
1763
snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
1766
ifield->leftadj = 1;
1767
for(i = 0; i < 6; i++){
1768
ielem = new_ielem(&ifield->ielem);
1769
ielem->freedata = 1;
1770
ielem->data = (char *) fs_get(2 * sizeof(char));
1771
ielem->data[0] = str[i];
1772
ielem->data[1] = '\0';
1774
set_print_format(ielem, 1, ifield->leftadj);
1777
if(pico_usingcolor()){
1779
if(str[0] == '+' || str[0] == '-'){
1780
if(VAR_IND_PLUS_FORE_COLOR
1781
&& VAR_IND_PLUS_BACK_COLOR){
1782
ielem = ifield->ielem;
1783
ielem->freecolor = 1;
1784
ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
1789
if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
1790
ielem = ifield->ielem->next->next;
1791
ielem->freecolor = 1;
1792
ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
1796
if(str[3] == 'N' || str[3] == 'n'){
1797
if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
1798
ielem = ifield->ielem->next->next->next;
1799
ielem->freecolor = 1;
1800
ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
1803
else if(str[3] == 'R' || str[3] == 'r'){
1804
if(VAR_IND_REC_FORE_COLOR && VAR_IND_REC_BACK_COLOR){
1805
ielem = ifield->ielem->next->next->next;
1806
ielem->freecolor = 1;
1807
ielem->color = new_color_pair(VAR_IND_REC_FORE_COLOR, VAR_IND_REC_BACK_COLOR);
1810
else if(str[3] == 'U' || str[3] == 'u'){
1811
if(VAR_IND_UNS_FORE_COLOR && VAR_IND_UNS_BACK_COLOR){
1812
ielem = ifield->ielem->next->next->next;
1813
ielem->freecolor = 1;
1814
ielem->color = new_color_pair(VAR_IND_UNS_FORE_COLOR, VAR_IND_UNS_BACK_COLOR);
1818
if(str[4] == 'A' || str[4] == 'a'){
1819
if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
1820
ielem = ifield->ielem->next->next->next->next;
1821
ielem->freecolor = 1;
1822
ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
1826
if(str[5] == 'D' || str[5] == 'd'){
1827
if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
1828
ielem = ifield->ielem->next->next->next->next->next;
1829
ielem->freecolor = 1;
1830
ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
1840
* This is a special case. The message number is
1841
* generated on the fly in the painting routine.
1842
* But the data array is allocated here in case it
1843
* is useful for the paint routine.
1845
snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
1849
snprintf(str, sizeof(str), "%-*.*s", ifield->width, ifield->width, " ");
1850
if(VAR_IND_ARR_FORE_COLOR && VAR_IND_ARR_BACK_COLOR){
1851
ifield->leftadj = 1;
1852
ielem = new_ielem(&ifield->ielem);
1853
ielem->freedata = 1;
1854
ielem->data = cpystr(str);
1855
ielem->datalen = strlen(str);
1856
set_print_format(ielem, ifield->width, ifield->leftadj);
1857
ielem->freecolor = 1;
1858
ielem->color = new_color_pair(VAR_IND_ARR_FORE_COLOR,
1859
VAR_IND_ARR_BACK_COLOR);
1865
score = get_msg_score(idata->stream, idata->rawno);
1866
if(score == SCORE_UNDEF){
1867
SEARCHSET *ss = NULL;
1869
ss = mail_newsearchset();
1870
ss->first = ss->last = (unsigned long) idata->rawno;
1873
* This looks like it might be expensive to get the
1874
* score for each message when needed but it shouldn't
1875
* be too bad because we know we have the envelope
1876
* data cached. We can't calculate all of the scores
1877
* we need for the visible messages right here in
1878
* one fell swoop because we don't have the other
1879
* envelopes yet. And we can't get the other
1880
* envelopes at this point because we may be in
1881
* the middle of a c-client callback (pine_imap_env).
1882
* (Actually we could, because we know whether or
1883
* not we're in the callback because of the no_fetch
1885
* We have another problem if the score rules depend
1886
* on something other than envelope data. I guess they
1887
* only do that if they have an alltext (search the
1888
* text of the message) definition. So, we're going
1889
* to pass no_fetch to calculate_scores so that it
1890
* can return an error if we need the text data but
1891
* can't get it because of no_fetch. Setting bogus
1892
* will cause us to do the scores calculation later
1893
* when we are no longer in the callback.
1896
(calculate_some_scores(idata->stream,
1897
ss, idata->no_fetch) == 0)
1899
score = get_msg_score(idata->stream, idata->rawno);
1900
mail_free_searchset(&ss);
1904
snprintf(str, sizeof(str), "%ld", score != SCORE_UNDEF ? score : 0L);
1907
case iDate: case iMonAbb: case iLDate:
1908
case iSDate: case iSTime:
1909
case iS1Date: case iS2Date: case iS3Date: case iS4Date:
1910
case iDateIso: case iDateIsoS: case iTime24: case iTime12:
1911
case iSDateIsoS: case iSDateIso:
1912
case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
1914
case iSDateTimeIsoS: case iSDateTimeIso:
1915
case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
1917
case iSDateTimeIsoS24: case iSDateTimeIso24:
1918
case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
1919
case iTimezone: case iYear: case iYear2Digit:
1920
case iRDate: case iDay: case iDay2Digit: case iMon2Digit:
1921
case iDayOrdinal: case iMon: case iMonLong:
1922
case iDayOfWeekAbb: case iDayOfWeek:
1923
date_str(fetch_date(idata), cdesc->ctype, 0, str, sizeof(str));
1927
case iFromToNotNews:
1931
from_str(cdesc->ctype, idata, BIGWIDTH, str, ice);
1935
if(((field = ((addr = fetch_to(idata))
1937
: (addr = fetch_cc(idata))
1940
&& !set_index_addr(idata, field, addr, NULL,
1941
BIGWIDTH, str, &ice->charset))
1943
if(newsgroups = fetch_newsgroups(idata))
1944
snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
1949
set_index_addr(idata, "Cc", fetch_cc(idata),
1950
NULL, BIGWIDTH, str,
1955
toaddr = fetch_to(idata);
1956
ccaddr = fetch_cc(idata);
1957
for(last_to = toaddr;
1958
last_to && last_to->next;
1959
last_to = last_to->next)
1962
/* point end of to list temporarily at cc list */
1964
last_to->next = ccaddr;
1966
set_index_addr(idata, "To", toaddr, NULL,
1967
BIGWIDTH, str, &ice->charset);
1970
last_to->next = NULL;
1975
if(addr = fetch_sender(idata))
1976
set_index_addr(idata, "Sender", addr, NULL,
1977
BIGWIDTH, str, &ice->charset);
1984
if((addr = fetch_from(idata)) && addr->personal){
1985
char *name, *initials = NULL, *dummy = NULL;
1988
name = (char *) rfc1522_decode((unsigned char *)tmp_20k_buf,
1990
addr->personal, &dummy);
1992
fs_give((void **)&dummy);
1994
if(name == addr->personal){
1995
strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
1996
tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
1997
name = (char *) tmp_20k_buf;
2001
initials = reply_quote_initials(name);
2002
snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
2011
if((l = fetch_size(idata)) < 10*1000L)
2012
snprintf(str, sizeof(str), "(%lu)", l);
2014
else if(l < 1000L*1000L - 1000L/2){
2015
l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2016
snprintf(str, sizeof(str), "(%luK)", l);
2018
/* 1.0M ... 99.9M */
2019
else if(l < 1000L*100L*1000L - 100L*1000L/2){
2020
l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
2022
snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
2024
/* 100M ... 2000M */
2025
else if(l <= 2*1000L*1000L*1000L){
2026
l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2028
snprintf(str, sizeof(str), "(%luM)", l);
2031
snprintf(str, sizeof(str), "(HUGE!)");
2037
if((l = fetch_size(idata)) < 100*1000L)
2038
snprintf(str, sizeof(str), "(%s)", comatose(l));
2039
/* 100K ... 9,999K */
2040
else if(l < 10L*1000L*1000L - 1000L/2){
2041
l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2042
snprintf(str, sizeof(str), "(%sK)", comatose(l));
2044
/* 10.0M ... 999.9M */
2045
else if(l < 1000L*1000L*1000L - 100L*1000L/2){
2046
l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
2048
snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
2050
/* 1,000M ... 2,000M */
2051
else if(l <= 2*1000L*1000L*1000L){
2052
l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2054
snprintf(str, sizeof(str), "(%sM)", comatose(l));
2057
snprintf(str, sizeof(str), "(HUGE!)");
2063
if((l = fetch_size(idata)) < 1000L)
2064
snprintf(str, sizeof(str), "(%lu)", l);
2066
else if(l < 100L*1000L - 1000L/2){
2067
l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
2068
snprintf(str, sizeof(str), "(%luK)", l);
2071
else if(l < 1000L*1000L - 100L*1000L/2){
2072
l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
2074
snprintf(str, sizeof(str), "(.%luM)", l);
2077
else if(l < 1000L*100L*1000L - 1000L*1000L/2){
2078
l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
2080
snprintf(str, sizeof(str), "(%luM)", l);
2083
else if(l < 1000L*1000L*1000L - 100L*1000L*1000L/2){
2084
l = l/(100L*1000L*1000L) + (l%(100L*1000L*1000L) >=
2085
(100L*1000L*1000L/2) ? 1L : 0L);
2086
snprintf(str, sizeof(str), "(.%luG)", l);
2089
else if(l <= 2*1000L*1000L*1000L){
2090
l = l/(1000L*1000L*1000L) + (l%(1000L*1000L*1000L) >=
2091
(1000L*1000L*1000L/2) ? 1L : 0L);
2092
snprintf(str, sizeof(str), "(%luG)", l);
2095
snprintf(str, sizeof(str), "(HUGE!)");
2099
/* From Carl Jacobsen <carl@ucsd.edu> */
2101
l = fetch_size(idata);
2102
l = (l / 1024L) + (l % 1024L != 0 ? 1 : 0);
2104
if(l < 1024L) { /* 0k .. 1023k */
2105
snprintf(str, sizeof(str), "(%luk)", l);
2107
} else if (l < 100L * 1024L){ /* 1.0M .. 99.9M */
2108
snprintf(str, sizeof(str), "(%lu.M)", (l * 10L) / 1024L);
2109
if ((p = strchr(str, '.')) != NULL) {
2110
p--; p[1] = p[0]; p[0] = '.'; /* swap last digit & . */
2112
} else if (l <= 2L * 1024L * 1024L) { /* 100M .. 2048 */
2113
snprintf(str, sizeof(str), "(%luM)", l / 1024L);
2115
snprintf(str, sizeof(str), "(HUGE!)");
2121
if(body = fetch_body(idata))
2125
mc = (idata->rawno > 0L && idata->stream
2126
&& idata->rawno <= idata->stream->nmsgs)
2127
? mail_elt(idata->stream, idata->rawno) : NULL;
2128
if(mc && mc->rfc822_size < 6000)
2129
snprintf(str, sizeof(str), "(short )");
2130
else if(mc && mc->rfc822_size < 25000)
2131
snprintf(str, sizeof(str), "(medium )");
2132
else if(mc && mc->rfc822_size < 100000)
2133
snprintf(str, sizeof(str), "(long )");
2135
snprintf(str, sizeof(str), "(huge )");
2141
if(strucmp(body->subtype, "MIXED") == 0){
2144
x = body->nested.part
2145
? body->nested.part->body.type
2149
if(body->nested.part->body.size.bytes < 6000)
2150
snprintf(str, sizeof(str), "(short+ )");
2151
else if(body->nested.part->body.size.bytes
2153
snprintf(str, sizeof(str), "(medium+)");
2154
else if(body->nested.part->body.size.bytes
2156
snprintf(str, sizeof(str), "(long+ )");
2158
snprintf(str, sizeof(str), "(huge+ )");
2162
snprintf(str, sizeof(str), "(multi )");
2166
else if(strucmp(body->subtype, "DIGEST") == 0)
2167
snprintf(str, sizeof(str), "(digest )");
2168
else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
2169
snprintf(str, sizeof(str), "(mul/alt)");
2170
else if(strucmp(body->subtype, "PARALLEL") == 0)
2171
snprintf(str, sizeof(str), "(mul/par)");
2173
snprintf(str, sizeof(str), "(multi )");
2178
snprintf(str, sizeof(str), "(message)");
2181
case TYPEAPPLICATION:
2182
snprintf(str, sizeof(str), "(applica)");
2186
snprintf(str, sizeof(str), "(audio )");
2190
snprintf(str, sizeof(str), "(image )");
2194
snprintf(str, sizeof(str), "(video )");
2198
snprintf(str, sizeof(str), "(other )");
2207
if((body = fetch_body(idata)) &&
2208
body->type == TYPEMULTIPART &&
2209
strucmp(body->subtype, "ALTERNATIVE") != 0){
2213
part = body->nested.part; /* 1st part, don't count */
2214
while(part && part->next && atts < 10){
2222
str[0] = '0' + atts;
2228
subj_str(idata, BIGWIDTH, str, NoKW, 0, ice);
2232
subj_str(idata, BIGWIDTH, str, NoKW, 1, ice);
2236
subj_str(idata, BIGWIDTH, str, KW, 0, ice);
2240
subj_str(idata, BIGWIDTH, str, KW, 1, ice);
2244
subj_str(idata, BIGWIDTH, str, KWInit, 0, ice);
2247
case iSubjKeyInitText:
2248
subj_str(idata, BIGWIDTH, str, KWInit, 1, ice);
2252
key_str(idata, KW, ice);
2256
key_str(idata, KWInit, ice);
2260
if(newsgroups = fetch_newsgroups(idata)){
2261
strncpy(str, newsgroups, BIGWIDTH);
2262
str[BIGWIDTH] = '\0';
2268
if(newsgroups = fetch_newsgroups(idata))
2269
strncpy(str, newsgroups, sizeof(str));
2271
if((l = strlen(str)) < sizeof(str)){
2272
if(sizeof(str) - l < 6)
2273
strncpy(str+l, "...", sizeof(str)-l);
2276
strncpy(str+l, " and ", sizeof(str)-l);
2277
set_index_addr(idata, "To", fetch_to(idata),
2278
NULL, BIGWIDTH-l-5, str+l+5,
2284
set_index_addr(idata, "To", fetch_to(idata),
2285
NULL, BIGWIDTH, str,
2293
set_index_addr(idata, "To", fetch_to(idata),
2294
NULL, BIGWIDTH, str,
2296
if((l = strlen(str)) < sizeof(str) &&
2297
(newsgroups = fetch_newsgroups(idata))){
2298
if(sizeof(str) - l < 6)
2299
strncpy(str+l, "...", sizeof(str)-l);
2302
strncpy(str+l, " and ", sizeof(str)-l);
2305
strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2307
strncpy(str, newsgroups, BIGWIDTH);
2313
case iNewsAndRecips:
2314
if(newsgroups = fetch_newsgroups(idata))
2315
strncpy(str, newsgroups, BIGWIDTH);
2317
if((l = strlen(str)) < BIGWIDTH){
2318
if(BIGWIDTH - l < 6)
2319
strncpy(str+l, "...", BIGWIDTH-l);
2321
toaddr = fetch_to(idata);
2322
ccaddr = fetch_cc(idata);
2323
for(last_to = toaddr;
2324
last_to && last_to->next;
2325
last_to = last_to->next)
2328
/* point end of to list temporarily at cc list */
2330
last_to->next = ccaddr;
2333
strncpy(str+l, " and ", sizeof(str)-l);
2334
set_index_addr(idata, "To", toaddr,
2335
NULL, BIGWIDTH-l-5, str+l+5,
2341
set_index_addr(idata, "To", toaddr, NULL,
2342
BIGWIDTH, str, &ice->charset);
2345
last_to->next = NULL;
2351
case iRecipsAndNews:
2352
toaddr = fetch_to(idata);
2353
ccaddr = fetch_cc(idata);
2354
for(last_to = toaddr;
2355
last_to && last_to->next;
2356
last_to = last_to->next)
2359
/* point end of to list temporarily at cc list */
2361
last_to->next = ccaddr;
2363
set_index_addr(idata, "To", toaddr, NULL,
2364
BIGWIDTH, str, &ice->charset);
2367
last_to->next = NULL;
2369
if((l = strlen(str)) < BIGWIDTH &&
2370
(newsgroups = fetch_newsgroups(idata))){
2371
if(BIGWIDTH - l < 6)
2372
strncpy(str+l, "...", BIGWIDTH-l);
2375
strncpy(str+l, " and ", sizeof(str)-l);
2378
strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
2380
strncpy(str, newsgroups, BIGWIDTH);
2389
* If the element wasn't already filled in above, do it here.
2392
ielem = new_ielem(&ifield->ielem);
2394
ielem->freedata = 1;
2395
ielem->data = cpystr(str);
2396
ielem->datalen = strlen(str);
2398
ifield->leftadj = (cdesc->adjustment == Left) ? 1 : 0;
2399
set_print_format(ielem, ifield->width, ifield->leftadj);
2403
ice->widths_done = 1;
2404
ice->id = ice_hash(ice);
2411
format_thread_index_line(INDEXDATA_S *idata)
2413
char *p, buffer[BIGWIDTH+1];
2414
int thdlen, space_left, i;
2415
PINETHRD_S *thrd = NULL;
2416
ICE_S *ice, *tice = NULL;
2420
dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
2421
idata ? idata->msgno : -1, idata ? idata->rawno : -1));
2423
space_left = ps_global->ttyo->screen_cols;
2425
if(ps_global->msgmap->max_thrdno < 1000)
2427
else if(ps_global->msgmap->max_thrdno < 10000)
2429
else if(ps_global->msgmap->max_thrdno < 100000)
2434
ice = fetch_ice(idata->stream, idata->rawno);
2435
if(ice && ice->charset)
2436
fs_give((void **) &ice->charset);
2438
thrd = fetch_thread(idata->stream, idata->rawno);
2440
if(!thrd || !ice) /* can't happen? */
2444
tice = (ICE_S *) fs_get(sizeof(*tice));
2445
memset(tice, 0, sizeof(*tice));
2455
fs_give((void **) &tice->charset);
2457
free_ifield(&tice->ifield);
2459
if(space_left >= 3){
2463
to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
2464
status = status_symbol_for_thread(idata->stream, thrd, iStatus);
2466
if((p-buffer)+3 < sizeof(buffer)){
2475
ifield = new_ifield(&tice->ifield);
2476
ifield->ctype = iStatus;
2478
ifield->leftadj = 1;
2479
for(i = 0; i < 3; i++){
2480
ielem = new_ielem(&ifield->ielem);
2481
ielem->freedata = 1;
2482
ielem->data = (char *) fs_get(2 * sizeof(char));
2483
ielem->data[0] = p[i];
2484
ielem->data[1] = '\0';
2486
set_print_format(ielem, 1, ifield->leftadj);
2489
if(pico_usingcolor()){
2490
struct variable *vars = ps_global->vars;
2493
&& VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
2494
ielem = ifield->ielem;
2495
ielem->freecolor = 1;
2496
ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR,
2497
VAR_IND_IMP_BACK_COLOR);
2498
if(F_ON(F_COLOR_LINE_IMPORTANT, ps_global))
2499
tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR,
2500
VAR_IND_IMP_BACK_COLOR);
2502
else if((to_us == '+' || to_us == '-')
2503
&& VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
2504
ielem = ifield->ielem;
2505
ielem->freecolor = 1;
2506
ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR,
2507
VAR_IND_PLUS_BACK_COLOR);
2511
&& VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
2512
ielem = ifield->ielem->next->next;
2513
ielem->freecolor = 1;
2514
ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR,
2515
VAR_IND_DEL_BACK_COLOR);
2517
else if(status == 'N'
2518
&& VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
2519
ielem = ifield->ielem->next->next;
2520
ielem->freecolor = 1;
2521
ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR,
2522
VAR_IND_NEW_BACK_COLOR);
2527
if(space_left >= thdlen+1){
2531
snprintf(p, sizeof(buffer), "%*.*s", thdlen, thdlen, "");
2532
space_left -= thdlen;
2534
ifield = new_ifield(&tice->ifield);
2535
ifield->ctype = iMessNo;
2536
ifield->width = thdlen;
2537
ifield->leftadj = 0;
2538
ielem = new_ielem(&ifield->ielem);
2539
ielem->freedata = 1;
2540
ielem->data = cpystr(p);
2541
ielem->datalen = strlen(p);
2542
set_print_format(ielem, ifield->width, ifield->leftadj);
2545
if(space_left >= 7){
2550
date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer));
2551
if(sizeof(buffer) > 6)
2554
if(strlen(p) < 6 && (sizeof(buffer)) > 6){
2557
for(q = p + strlen(p); q < p + 6; q++)
2563
ifield = new_ifield(&tice->ifield);
2564
ifield->ctype = iDate;
2566
ifield->leftadj = 1;
2567
ielem = new_ielem(&ifield->ielem);
2568
ielem->freedata = 1;
2569
ielem->data = cpystr(p);
2570
ielem->datalen = ifield->width;
2571
set_print_format(ielem, ifield->width, ifield->leftadj);
2576
int from_width, subj_width, bigthread_adjust;
2579
char from[BIGWIDTH+1];
2584
in_thread = count_lflags_in_thread(idata->stream, thrd,
2585
ps_global->msgmap, MN_NONE);
2588
snprintf(tcnt, sizeof(tcnt), "(%ld)", in_thread);
2589
bigthread_adjust = MAX(0, strlen(tcnt) - 3);
2591
/* third of the rest */
2592
from_width = MAX((space_left-1)/3 - bigthread_adjust, 1);
2595
subj_width = space_left - from_width - 1;
2597
if(strlen(tcnt) > subj_width)
2598
tcnt[subj_width] = '\0';
2601
from_str(iFromTo, idata, BIGWIDTH, from, tice);
2603
ifield = new_ifield(&tice->ifield);
2604
ifield->leftadj = 1;
2605
ielem = new_ielem(&ifield->ielem);
2606
ielem->freedata = 1;
2607
ielem->data = cpystr(from);
2608
ielem->datalen = strlen(from);
2609
ifield->width = from_width;
2610
set_print_format(ielem, ifield->width, ifield->leftadj);
2611
ifield->ctype = iFrom;
2613
ifield = new_ifield(&tice->ifield);
2614
ifield->leftadj = 0;
2615
ielem = new_ielem(&ifield->ielem);
2616
ielem->freedata = 1;
2617
ielem->data = cpystr(tcnt);
2618
ielem->datalen = strlen(tcnt);
2619
ifield->width = ielem->datalen;
2620
set_print_format(ielem, ifield->width, ifield->leftadj);
2621
ifield->ctype = iAtt; /* not used, except that it isn't special */
2623
subj_width -= strlen(tcnt);
2631
if(idata->bogus < 2)
2632
snprintf(p, sizeof(buffer), "%-.*s", BIGWIDTH,
2633
_("[ No Message Text Available ]"));
2637
subj_str(idata, BIGWIDTH, p, NoKW, 0, NULL);
2640
ifield = new_ifield(&tice->ifield);
2641
ifield->leftadj = 1;
2642
ielem = new_ielem(&ifield->ielem);
2643
ielem->freedata = 1;
2644
ielem->data = cpystr(p);
2645
ielem->datalen = strlen(p);
2646
ifield->width = subj_width;
2647
set_print_format(ielem, ifield->width, ifield->leftadj);
2648
ifield->ctype = iSubject;
2651
else if(space_left > 1){
2652
snprintf(p, sizeof(buffer)-(p-buffer), "%-.*s", space_left-1, " ");
2653
ifield = new_ifield(&tice->ifield);
2654
ifield->leftadj = 1;
2655
ielem = new_ielem(&ifield->ielem);
2656
ielem->freedata = 1;
2657
ielem->data = cpystr(p);
2658
ielem->datalen = strlen(p);
2659
ifield->width = space_left-1;
2660
set_print_format(ielem, ifield->width, ifield->leftadj);
2661
ifield->ctype = iSubject;
2664
tice->widths_done = 1;
2665
tice->id = ice_hash(tice);
2672
* Print the fields of ice in buf with a single space between
2673
* fields. Buf must have size at least n+1.
2675
* Args buf -- place to put the line
2676
* n -- the max length of the returned string. Buf has to be
2677
* at least n+1 in size.
2678
* ice -- the data for the line
2679
* msgno -- this is the msgno to be used, blanks if <= 0
2681
* Returns a pointer to buf.
2684
simple_index_line(char *buf, size_t buflen, int n, ICE_S *ice, long int msgno)
2690
if(n < 0 || n > 1000)
2691
panic("unreasonable n in simple_index_line()");
2694
panic("NULL buf in simple_index_line()");
2706
for(ifield = ice->ifield; ifield && p-buf < n; ifield = ifield->next){
2708
/* space between fields */
2709
if(ifield != ice->ifield && (p-buf) < buflen)
2712
/* message number string is generated on the fly */
2713
if(ifield->ctype == iMessNo){
2714
ielem = ifield->ielem;
2715
if(ielem && ielem->datalen >= ifield->width){
2717
snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
2719
snprintf(ielem->data, ielem->datalen+1, "%*.*s", ifield->width, ifield->width, "");
2721
ielem->data[MIN(ifield->width,ielem->datalen)] = '\0';
2725
for(ielem = ifield->ielem;
2726
ielem && ielem->print_format && p-buf < MIN(n,buflen);
2727
ielem = ielem->next){
2728
snprintf(p, MIN(n+1,buflen)-(p-buf), ielem->print_format, ielem->data);
2729
buf[MIN(n,buflen-1)] = '\0';
2735
buf[MIN(n,buflen-1)] = '\0';
2742
* Look in current mail_stream for matches for messages in the searchset
2743
* which match a color rule pattern. Return the color.
2744
* The searched bit will be set for all of the messages which match the
2745
* first pattern which has a match.
2747
* Args stream -- the mail stream
2748
* searchset -- restrict attention to this set of messages
2749
* pstate -- The pattern state. On the first call it will be Null.
2750
* Null means start over with a new first_pattern.
2751
* After that it will be pointing to our local PAT_STATE
2752
* so that next_pattern goes to the next one after the
2753
* ones we've already checked.
2755
* Returns 0 if no match, 1 if a match.
2756
* The color that goes with the matched rule in returned_color.
2757
* It may be NULL, which indicates default.
2760
get_index_line_color(MAILSTREAM *stream, struct search_set *searchset,
2761
PAT_STATE **pstate, COLOR_PAIR **returned_color)
2764
long rflags = ROLE_INCOL;
2765
COLOR_PAIR *color = NULL;
2767
static PAT_STATE localpstate;
2769
dprint((7, "get_index_line_color\n"));
2772
*returned_color = NULL;
2775
pat = next_pattern(*pstate);
2777
*pstate = &localpstate;
2778
if(!nonempty_patterns(rflags, *pstate))
2782
pat = first_pattern(*pstate);
2787
/* Go through the possible roles one at a time until we get a match. */
2788
while(!match && pat){
2789
if(match_pattern(pat->patgrp, stream, searchset, NULL,
2790
get_msg_score, SO_NOSERVER|SE_NOPREFETCH)){
2791
if(!pat->action || pat->action->bogus)
2795
if(pat->action && pat->action->incol)
2796
color = new_color_pair(pat->action->incol->fg,
2797
pat->action->incol->bg);
2800
pat = next_pattern(*pstate);
2804
if(match && returned_color)
2805
*returned_color = color;
2815
index_in_overview(MAILSTREAM *stream)
2817
INDEX_COL_S *cdesc = NULL;
2819
if(!(stream->mailbox && IS_REMOTE(stream->mailbox)))
2820
return(FALSE); /* no point! */
2822
if(stream->dtb && stream->dtb->name && !strcmp(stream->dtb->name, "nntp")){
2827
for(cdesc = ps_global->index_disp_format;
2828
cdesc->ctype != iNothing;
2830
switch(cdesc->ctype){
2831
case iTo: /* can't be satisfied by XOVER */
2832
case iSender: /* ... or specifically handled */
2833
case iDescripSize: /* ... in news case */
2848
* fetch_from - called to get a the index entry's "From:" field
2851
resent_to_us(INDEXDATA_S *idata)
2853
if(!idata->valid_resent_to){
2854
static char *fields[] = {"Resent-To", NULL};
2857
if(idata->no_fetch){
2858
idata->bogus = 1; /* don't do this */
2862
if(h = pine_fetchheader_lines(idata->stream,idata->rawno,NULL,fields)){
2863
idata->resent_to_us = parsed_resent_to_us(h);
2864
fs_give((void **) &h);
2867
idata->valid_resent_to = 1;
2870
return(idata->resent_to_us);
2875
parsed_resent_to_us(char *h)
2878
ADDRESS *addr = NULL;
2881
if(p = strindex(h, ':')){
2882
for(q = ++p; q = strpbrk(q, "\015\012"); q++)
2883
*q = ' '; /* quash junk */
2885
rfc822_parse_adrlist(&addr, p, ps_global->maildomain);
2887
rv = address_is_us(addr, ps_global);
2888
mail_free_address(&addr);
2898
* fetch_from - called to get a the index entry's "From:" field
2901
fetch_from(INDEXDATA_S *idata)
2903
if(idata->no_fetch) /* implies from is valid */
2904
return(idata->from);
2905
else if(idata->bogus)
2910
/* c-client call's just cache access at this point */
2911
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
2922
* fetch_to - called to get a the index entry's "To:" field
2925
fetch_to(INDEXDATA_S *idata)
2927
if(idata->no_fetch){ /* check for specific validity */
2931
idata->bogus = 1; /* can't give 'em what they want */
2933
else if(idata->bogus){
2934
idata->bogus = 2; /* elevate bogosity */
2939
/* c-client call's just cache access at this point */
2940
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
2951
* fetch_cc - called to get a the index entry's "Cc:" field
2954
fetch_cc(INDEXDATA_S *idata)
2956
if(idata->no_fetch){ /* check for specific validity */
2960
idata->bogus = 1; /* can't give 'em what they want */
2962
else if(idata->bogus){
2963
idata->bogus = 2; /* elevate bogosity */
2968
/* c-client call's just cache access at this point */
2969
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
2981
* fetch_sender - called to get a the index entry's "Sender:" field
2984
fetch_sender(INDEXDATA_S *idata)
2986
if(idata->no_fetch){ /* check for specific validity */
2987
if(idata->valid_sender)
2988
return(idata->sender);
2990
idata->bogus = 1; /* can't give 'em what they want */
2992
else if(idata->bogus){
2993
idata->bogus = 2; /* elevate bogosity */
2998
/* c-client call's just cache access at this point */
2999
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
3000
return(env->sender);
3010
* fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
3013
fetch_newsgroups(INDEXDATA_S *idata)
3015
if(idata->no_fetch){ /* check for specific validity */
3016
if(idata->valid_news)
3017
return(idata->newsgroups);
3019
idata->bogus = 1; /* can't give 'em what they want */
3021
else if(idata->bogus){
3022
idata->bogus = 2; /* elevate bogosity */
3027
/* c-client call's just cache access at this point */
3028
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
3029
return(env->newsgroups);
3039
* fetch_subject - called to get at the index entry's "Subject:" field
3042
fetch_subject(INDEXDATA_S *idata)
3044
if(idata->no_fetch) /* implies subject is valid */
3045
return(idata->subject);
3046
else if(idata->bogus)
3051
/* c-client call's just cache access at this point */
3052
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
3053
return(env->subject);
3063
* Return an allocated copy of the first few characters from the body
3064
* of the message for possible use in the index screen.
3067
fetch_firsttext(INDEXDATA_S *idata)
3071
char *firsttext = NULL;
3075
if(env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)){
3077
char *subtype = NULL;
3079
if((body->type == TYPETEXT
3080
&& (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
3082
(body->type == TYPEMULTIPART && body->nested.part
3083
&& body->nested.part->body.type == TYPETEXT
3084
&& (subtype=body->nested.part->body.subtype)
3085
&& ALLOWED_SUBTYPE(subtype))){
3087
if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
3091
int was_space_for_eol = 0;
3092
long partial_fetch_len;
3094
if(subtype && !strucmp(subtype, "html"))
3095
partial_fetch_len = 1024;
3096
else if(subtype && !strucmp(subtype, "plain"))
3097
partial_fetch_len = 128;
3099
partial_fetch_len = 256;
3101
gf_set_so_writec(&pc, so);
3102
success = get_body_part_text(idata->stream, body, idata->rawno,
3103
"1", partial_fetch_len,
3105
gf_clear_so_writec(so);
3110
while(p-buf < sizeof(buf)-1 && so_readc(&c, so)){
3111
if(p == buf && isspace(c))
3113
else if(c == '\r' || c == '\n'){
3114
if(!was_space_for_eol){
3116
was_space_for_eol++;
3120
was_space_for_eol = 0;
3128
firsttext = cpystr(buf);
3142
* fetch_date - called to get at the index entry's "Date:" field
3145
fetch_date(INDEXDATA_S *idata)
3147
if(idata->no_fetch) /* implies date is valid */
3148
return(idata->date);
3149
else if(idata->bogus)
3154
/* c-client call's just cache access at this point */
3155
if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
3156
return((char *) env->date);
3166
* fetch_size - called to get at the index entry's "size" field
3169
fetch_size(INDEXDATA_S *idata)
3171
if(idata->no_fetch) /* implies size is valid */
3172
return(idata->size);
3173
else if(idata->bogus)
3178
if(idata->stream && idata->rawno > 0L
3179
&& idata->rawno <= idata->stream->nmsgs
3180
&& (mc = mail_elt(idata->stream, idata->rawno)))
3181
return(mc->rfc822_size);
3191
* fetch_body - called to get a the index entry's body structure
3194
fetch_body(INDEXDATA_S *idata)
3198
if(idata->bogus || idata->no_fetch){
3203
if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
3212
* s is at least size width+1
3215
set_index_addr(INDEXDATA_S *idata, char *field, struct mail_address *addr,
3216
char *prefix, int width, char *s, char **cset)
3220
char *save_personal = NULL;
3222
for(atmp = addr; idata->stream && atmp; atmp = atmp->next)
3223
if(atmp->host && atmp->host[0] == '.'){
3224
char *pref, *h, *fields[2];
3226
if(idata->no_fetch){
3233
if(h = pine_fetchheader_lines(idata->stream, idata->rawno,
3236
for(p = h + strlen(field) + 1;
3237
*p && isspace((unsigned char)*p); p++)
3241
for(pref = prefix; pref && *pref; pref++)
3250
if(*p == '\015' || *p == '\012')
3251
p++; /* skip CR LF */
3257
*s = '\0'; /* tie off return string */
3259
fs_give((void **) &h);
3262
/* else fall thru and display what c-client gave us */
3265
if(addr && !addr->next /* only one address */
3266
&& addr->host /* not group syntax */
3267
&& addr->personal && addr->personal[0]){ /* there is a personal name */
3268
char *dummy = NULL, *free_this = NULL;
3269
char buftmp[MAILTMPLEN];
3272
if(l = prefix ? strlen(prefix) : 0)
3273
strncpy(s, prefix, width+1);
3275
snprintf(buftmp, sizeof(buftmp), "%s", addr->personal);
3276
p = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf,
3277
SIZEOF_20KBUF, buftmp, &dummy);
3279
/* convert the text to UTF-8 if needed */
3280
if(p == buftmp || (dummy && strucmp(dummy, "utf-8"))){
3281
free_this = convert_to_utf8(buftmp, dummy, 0);
3286
removing_leading_and_trailing_white_space(p);
3288
iutf8ncpy(s + l, p, width - l);
3291
fs_give((void **) &free_this);
3294
int dl = strlen(dummy);
3297
if(*cset && strucmp(*cset, dummy)){
3298
/* UH OH: MISMATCHED CHARSETS */
3299
if(strlen(*cset) < strlen(UNKNOWN_CHARSET))
3300
fs_resize((void **) cset,
3301
(strlen(UNKNOWN_CHARSET)+1) * sizeof(char));
3303
strncpy(*cset, UNKNOWN_CHARSET, strlen(UNKNOWN_CHARSET)+1);
3306
*cset = cpystr(dummy);
3309
fs_give((void **)&dummy);
3317
save_personal = addr->personal;
3318
addr->personal = NULL;
3326
a_string = addr_list_string(addr, NULL, 0);
3328
addr->personal = save_personal;
3330
if(l = prefix ? strlen(prefix) : 0)
3331
strncpy(s, prefix, width+1);
3333
iutf8ncpy(s + l, a_string, width - l);
3336
fs_give((void **)&a_string);
3342
addr->personal = save_personal;
3349
index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
3356
idata->from = env->from;
3357
idata->to = env->to;
3358
idata->cc = env->cc;
3359
idata->sender = env->sender;
3360
idata->subject = env->subject;
3361
idata->date = (char *) env->date;
3362
idata->newsgroups = env->newsgroups;
3364
idata->valid_to = 1; /* signal that everythings here */
3365
idata->valid_cc = 1;
3366
idata->valid_sender = 1;
3367
idata->valid_news = 1;
3372
* Put a string representing the date into str. The source date is
3373
* in the string datesrc. The format to be used is in type.
3374
* Notice that type is an IndexColType, but really only a subset of
3375
* IndexColType types are allowed.
3377
* Args datesrc -- The source date string
3378
* type -- What type of output we want
3379
* v -- If set, variable width output is ok. (Oct 9 not Oct 9)
3380
* str -- Put the answer here.
3383
date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len)
3385
char year4[5], /* 4 digit year */
3386
yearzero[3], /* zero padded, 2-digit year */
3387
monzero[3], /* zero padded, 2-digit month */
3388
mon[3], /* 1 or 2-digit month, no pad */
3389
dayzero[3], /* zero padded, 2-digit day */
3390
day[3], /* 1 or 2-digit day, no pad */
3391
dayord[3], /* 2-letter ordinal label */
3392
monabb[4], /* 3-letter month abbrev */
3393
hour24[3], /* 2-digit, 24 hour clock hour */
3394
hour12[3], /* 12 hour clock hour, no pad */
3395
minzero[3], /* zero padded, 2-digit minutes */
3396
timezone[6]; /* timezone, like -0800 or +... */
3398
int curtype, lastmonthtype, lastyeartype;
3399
int sdatetimetype, sdatetime24type;
3401
#define TODAYSTR N_("Today")
3403
curtype = (type == iCurDate ||
3404
type == iCurDateIso ||
3405
type == iCurDateIsoS ||
3406
type == iCurTime24 ||
3407
type == iCurTime12 ||
3409
type == iCurDay2Digit ||
3410
type == iCurDayOfWeek ||
3411
type == iCurDayOfWeekAbb ||
3413
type == iCurMon2Digit ||
3414
type == iCurMonLong ||
3415
type == iCurMonAbb ||
3417
type == iCurYear2Digit);
3418
lastmonthtype = (type == iLstMon ||
3419
type == iLstMon2Digit ||
3420
type == iLstMonLong ||
3421
type == iLstMonAbb ||
3422
type == iLstMonYear ||
3423
type == iLstMonYear2Digit);
3424
lastyeartype = (type == iLstYear ||
3425
type == iLstYear2Digit);
3426
sdatetimetype = (type == iSDateTime ||
3427
type == iSDateTimeIso ||
3428
type == iSDateTimeIsoS ||
3429
type == iSDateTimeS1 ||
3430
type == iSDateTimeS2 ||
3431
type == iSDateTimeS3 ||
3432
type == iSDateTimeS4 ||
3433
type == iSDateTime24 ||
3434
type == iSDateTimeIso24 ||
3435
type == iSDateTimeIsoS24 ||
3436
type == iSDateTimeS124 ||
3437
type == iSDateTimeS224 ||
3438
type == iSDateTimeS324 ||
3439
type == iSDateTimeS424);
3440
sdatetime24type = (type == iSDateTime24 ||
3441
type == iSDateTimeIso24 ||
3442
type == iSDateTimeIsoS24 ||
3443
type == iSDateTimeS124 ||
3444
type == iSDateTimeS224 ||
3445
type == iSDateTimeS324 ||
3446
type == iSDateTimeS424);
3450
if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
3453
if(curtype || lastmonthtype || lastyeartype){
3457
parse_date(dbuf, &d);
3461
else if(lastmonthtype){
3470
parse_date(datesrc, &d);
3472
strncpy(monabb, (d.month > 0 && d.month < 13)
3473
? month_abbrev(d.month) : "", sizeof(monabb));
3475
strncpy(mon, (d.month > 0 && d.month < 13)
3476
? int2string(d.month) : "", sizeof(mon));
3478
strncpy(day, (d.day > 0 && d.day < 32)
3479
? int2string(d.day) : "", sizeof(day));
3482
(d.day <= 0 || d.day > 31) ? "" :
3483
(d.day == 1 || d.day == 21 || d.day == 31) ? "st" :
3484
(d.day == 2 || d.day == 22 ) ? "nd" :
3485
(d.day == 3 || d.day == 23 ) ? "rd" : "th", sizeof(dayord));
3487
monabb[sizeof(monabb)-1] = '\0';
3488
day[sizeof(day)-1] = '\0';
3489
dayord[sizeof(dayord)-1] = '\0';
3491
strncpy(year4, (d.year >= 1000 && d.year < 10000)
3492
? int2string(d.year) : "????", sizeof(year4));
3493
year4[sizeof(year4)-1] = '\0';
3496
if((d.year % 100) < 10){
3498
strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
3501
strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
3504
strncpy(yearzero, "??", sizeof(yearzero));
3506
yearzero[sizeof(yearzero)-1] = '\0';
3508
if(d.month > 0 && d.month < 10){
3510
strncpy(monzero+1, int2string(d.month), sizeof(monzero)-1);
3512
else if(d.month >= 10 && d.month <= 12)
3513
strncpy(monzero, int2string(d.month), sizeof(monzero));
3515
strncpy(monzero, "??", sizeof(monzero));
3517
monzero[sizeof(monzero)-1] = '\0';
3519
if(d.day > 0 && d.day < 10){
3521
strncpy(dayzero+1, int2string(d.day), sizeof(dayzero)-1);
3523
else if(d.day >= 10 && d.day <= 31)
3524
strncpy(dayzero, int2string(d.day), sizeof(dayzero));
3526
strncpy(dayzero, "??", sizeof(dayzero));
3528
dayzero[sizeof(dayzero)-1] = '\0';
3530
hr12 = (d.hour == 0) ? 12 :
3531
(d.hour > 12) ? (d.hour - 12) : d.hour;
3533
if(hr12 > 0 && hr12 <= 12)
3534
strncpy(hour12, int2string(hr12), sizeof(hour12));
3536
hour12[sizeof(hour12)-1] = '\0';
3539
if(d.hour >= 0 && d.hour < 10){
3541
strncpy(hour24+1, int2string(d.hour), sizeof(hour24)-1);
3543
else if(d.hour >= 10 && d.hour < 24)
3544
strncpy(hour24, int2string(d.hour), sizeof(hour24));
3546
hour24[sizeof(hour24)-1] = '\0';
3549
if(d.minute >= 0 && d.minute < 10){
3551
strncpy(minzero+1, int2string(d.minute), sizeof(minzero)-1);
3553
else if(d.minute >= 10 && d.minute <= 60)
3554
strncpy(minzero, int2string(d.minute), sizeof(minzero));
3556
minzero[sizeof(minzero)-1] = '\0';
3558
if(sizeof(timezone) > 5){
3559
if(d.hours_off_gmt <= 0){
3561
d.hours_off_gmt *= -1;
3562
d.min_off_gmt *= -1;
3568
if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
3570
strncpy(timezone+2, int2string(d.hours_off_gmt), sizeof(timezone)-2);
3572
else if(d.hours_off_gmt >= 10 && d.hours_off_gmt < 24)
3573
strncpy(timezone+1, int2string(d.hours_off_gmt), sizeof(timezone)-1);
3580
if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
3582
strncpy(timezone+4, int2string(d.min_off_gmt), sizeof(timezone)-4);
3584
else if(d.min_off_gmt >= 10 && d.min_off_gmt <= 60)
3585
strncpy(timezone+3, int2string(d.min_off_gmt), sizeof(timezone)-3);
3592
timezone[sizeof(timezone)-1] = '\0';
3597
snprintf(str, str_len, "%s%s%s %s %s",
3598
(d.wkday != -1) ? week_abbrev(d.wkday) : "",
3599
(d.wkday != -1) ? ", " : "",
3600
day, monabb, year4);
3603
case iCurDayOfWeekAbb:
3604
strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? week_abbrev(d.wkday) : "", str_len);
3605
str[str_len-1] = '\0';
3609
strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? _(day_name[d.wkday]) : "", str_len);
3610
str[str_len-1] = '\0';
3616
strncpy(str, year4, str_len);
3620
strncpy(str, dayzero, str_len);
3625
strncpy(str, monzero, str_len);
3628
case iCurYear2Digit:
3629
case iLstYear2Digit:
3630
case iLstMonYear2Digit:
3631
strncpy(str, yearzero, str_len);
3634
strncpy(str, timezone, str_len);
3638
strncpy(str, day, str_len);
3641
snprintf(str, str_len, "%s%s", day, dayord);
3646
if(d.month > 0 && d.month <= 12)
3647
strncpy(str, int2string(d.month), str_len);
3653
strncpy(str, monabb, str_len);
3658
strncpy(str, (d.month > 0 && d.month < 13)
3659
? month_name(d.month) : "", str_len);
3664
snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
3666
snprintf(str, str_len, "%3s %2s", monabb, day);
3671
snprintf(str, str_len, "%s%s%s%s%s", monabb,
3672
(monabb[0] && day[0]) ? " " : "", day,
3673
((monabb[0] || day[0]) && year4[0]) ? ", " : "",
3676
snprintf(str, str_len, "%3s %2s%c %4s", monabb, day,
3677
(monabb[0] && day[0] && year4[0]) ? ',' : ' ',
3688
if(monzero[0] == '?' && dayzero[0] == '?' &&
3690
snprintf(str, str_len, "%8s", "");
3694
snprintf(str, str_len, "%2s/%2s/%2s",
3695
monzero, dayzero, yearzero);
3698
snprintf(str, str_len, "%2s/%2s/%2s",
3699
dayzero, monzero, yearzero);
3702
snprintf(str, str_len, "%2s.%2s.%2s",
3703
dayzero, monzero, yearzero);
3706
snprintf(str, str_len, "%2s.%2s.%2s",
3707
yearzero, monzero, dayzero);
3711
snprintf(str, str_len, "%2s-%2s-%2s",
3712
yearzero, monzero, dayzero);
3716
snprintf(str, str_len, "%4s-%2s-%2s",
3717
year4, monzero, dayzero);
3725
snprintf(str, str_len, "%2s%c%2s",
3726
(hour24[0] && minzero[0]) ? hour24 : "",
3727
(hour24[0] && minzero[0]) ? ':' : ' ',
3728
(hour24[0] && minzero[0]) ? minzero : "");
3732
snprintf(str, str_len, "%s%c%2s%s",
3733
(hour12[0] && minzero[0]) ? hour12 : "",
3734
(hour12[0] && minzero[0]) ? ':' : ' ',
3735
(hour12[0] && minzero[0]) ? minzero : "",
3736
(hour12[0] && minzero[0] && d.hour < 12) ? "am" :
3737
(hour12[0] && minzero[0] && d.hour >= 12) ? "pm" :
3740
case iSDate: case iSDateIso: case iSDateIsoS:
3741
case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
3742
case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
3743
case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
3744
case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
3745
case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
3746
{ struct date now, last_day;
3748
int msg_day_of_year, now_day_of_year, today;
3749
int diff, ydiff, last_day_of_year;
3752
parse_date(dbuf, &now);
3753
today = day_of_week(&now) + 7;
3755
if(today >= 0+7 && today <= 6+7){
3756
now_day_of_year = day_of_year(&now);
3757
msg_day_of_year = day_of_year(&d);
3758
ydiff = now.year - d.year;
3760
if(msg_day_of_year == -1)
3763
diff = now_day_of_year - msg_day_of_year;
3764
else if(ydiff == 1){
3766
last_day.month = 12;
3768
last_day_of_year = day_of_year(&last_day);
3770
diff = now_day_of_year +
3771
(last_day_of_year - msg_day_of_year);
3773
else if(ydiff == -1){
3775
last_day.month = 12;
3777
last_day_of_year = day_of_year(&last_day);
3779
diff = -1 * (msg_day_of_year +
3780
(last_day_of_year - now_day_of_year));
3788
strncpy(str, _(TODAYSTR), str_len);
3790
strncpy(str, _("Yesterday"), str_len);
3791
else if(diff > 1 && diff < 7)
3792
snprintf(str, str_len, "%s", _(day_name[(today - diff) % 7]));
3794
strncpy(str, _("Tomorrow"), str_len);
3795
else if(diff < -1 && diff > -7)
3796
snprintf(str, str_len, _("Next %.3s!"),
3797
_(day_name[(today - diff) % 7]));
3800
|| (ydiff == 1 && 12 + now.month - d.month < 6))){
3802
snprintf(str, str_len, "%s%s%s", monabb,
3803
(monabb[0] && day[0]) ? " " : "", day);
3805
snprintf(str, str_len, "%3s %2s", monabb, day);
3808
if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
3809
type = iSDateTimeIsoS;
3812
case iSDate: case iSDateTime: case iSDateTime24:
3816
memset(&tm, 0, sizeof(tm));
3817
tm.tm_year = MAX(d.year-1900, 0);
3818
tm.tm_mon = d.month-1;
3820
strftime(str, str_len, "%x", &tm);
3824
case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
3826
snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
3827
diff < 0 ? "!" : "");
3829
snprintf(str, str_len, "%s%s/%s/%s%s",
3830
(mon[0] && mon[1]) ? "" : " ",
3831
mon, dayzero, yearzero,
3832
diff < 0 ? "!" : "");
3834
case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
3836
snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
3837
diff < 0 ? "!" : "");
3839
snprintf(str, str_len, "%s%s/%s/%s%s",
3840
(day[0] && day[1]) ? "" : " ",
3841
day, monzero, yearzero,
3842
diff < 0 ? "!" : "");
3844
case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
3846
snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
3847
diff < 0 ? "!" : "");
3849
snprintf(str, str_len, "%s%s.%s.%s%s",
3850
(day[0] && day[1]) ? "" : " ",
3851
day, monzero, yearzero,
3852
diff < 0 ? "!" : "");
3854
case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
3856
snprintf(str, str_len, "%s.%s.%s%s",
3857
yearzero, monzero, dayzero,
3858
diff < 0 ? "!" : "");
3860
snprintf(str, str_len, "%s.%s.%s%s",
3861
yearzero, monzero, dayzero,
3862
diff < 0 ? "!" : "");
3864
case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
3865
snprintf(str, str_len, "%2s-%2s-%2s",
3866
yearzero, monzero, dayzero);
3868
case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
3869
snprintf(str, str_len, "%4s-%2s-%2s",
3870
year4, monzero, dayzero);
3878
snprintf(str, str_len, "%s%s%s", monabb,
3879
(monabb[0] && day[0]) ? " " : "", day);
3881
snprintf(str, str_len, "%3s %2s", monabb, day);
3888
str[str_len-1] = '\0';
3890
if(type == iSTime ||
3891
(sdatetimetype && !strcmp(str, _(TODAYSTR)))){
3892
struct date now, last_day;
3893
char dbuf[200], *Ddd, *ampm;
3898
parse_date(dbuf, &now);
3900
/* Figure out if message date lands in the past week */
3902
/* (if message dated this month or last month...) */
3903
if((d.year == now.year && d.month >= now.month - 1) ||
3904
(d.year == now.year - 1 && d.month == 12 && now.month == 1)){
3906
daydiff = day_of_year(&now) - day_of_year(&d);
3909
* If msg in end of last year (and we're in first bit of "this"
3910
* year), diff will be backwards; fix up by adding number of days
3911
* in last year (usually 365, but occasionally 366)...
3913
if(d.year == now.year - 1){
3915
last_day.month = 12;
3918
daydiff += day_of_year(&last_day);
3922
daydiff = -100; /* comfortably out of range (of past week) */
3924
/* Build 2-digit hour and am/pm indicator, used below */
3926
if(d.hour >= 0 && d.hour < 24){
3927
snprintf(hour12, sizeof(hour12), "%02d", (d.hour % 12 == 0) ? 12 : d.hour % 12);
3928
ampm = (d.hour < 12) ? "am" : "pm";
3929
snprintf(hour24, sizeof(hour24), "%02d", d.hour);
3932
strncpy(hour12, "??", sizeof(hour12));
3933
hour12[sizeof(hour12)-1] = '\0';
3935
strncpy(hour24, "??", sizeof(hour24));
3936
hour24[sizeof(hour24)-1] = '\0';
3939
/* Build date/time in str, in format similar to that used by w(1) */
3941
if(daydiff == 0){ /* If date is today, "HH:MMap" */
3942
if(d.minute >= 0 && d.minute < 60)
3943
snprintf(minzero, sizeof(minzero), "%02d", d.minute);
3945
strncpy(minzero, "??", sizeof(minzero));
3946
minzero[sizeof(minzero)-1] = '\0';
3949
snprintf(str, str_len, "%s:%s%s", sdatetime24type ? hour24 : hour12,
3950
minzero, sdatetime24type ? "" : ampm);
3952
else if(daydiff >= 1 && daydiff < 6){ /* If <1wk ago, "DddHHap" */
3954
if(d.month >= 1 && d.day >= 1 && d.year >= 0 &&
3955
d.month <= 12 && d.day <= 31 && d.year <= 9999)
3956
Ddd = week_abbrev(day_of_week(&d));
3960
snprintf(str, str_len, "%s%s%s", Ddd, hour12, ampm);
3962
else{ /* date is old or future, "ddMmmyy" */
3963
strncpy(monabb, (d.month >= 1 && d.month <= 12)
3964
? month_abbrev(d.month) : "???", sizeof(monabb));
3965
monabb[sizeof(monabb)-1] = '\0';
3967
if(d.day >= 1 && d.day <= 31)
3968
snprintf(dayzero, sizeof(dayzero), "%02d", d.day);
3970
strncpy(dayzero, "??", sizeof(dayzero));
3971
dayzero[sizeof(dayzero)-1] = '\0';
3974
if(d.year >= 0 && d.year <= 9999)
3975
snprintf(yearzero, sizeof(yearzero), "%02d", d.year % 100);
3977
strncpy(yearzero, "??", sizeof(yearzero));
3978
yearzero[sizeof(yearzero)-1] = '\0';
3981
snprintf(str, str_len, "%s%s%s", dayzero, monabb, yearzero);
3984
if(str[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
3986
memmove(str, str + 1, strlen(str));
3995
* Format a string representing the keywords into ice.
3997
* This needs to be done in UTF-8, which may be tricky since it isn't labelled.
3999
* Args idata -- which message?
4000
* kwtype -- keywords or kw initials
4001
* ice -- index cache entry for message
4004
key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
4009
COLOR_PAIR *color = NULL;
4010
SPEC_COLOR_S *sc = ps_global->kw_colors;
4011
IELEM_S *ielem = NULL;
4012
IFIELD_S *ourifield = NULL;
4014
SIZEDTEXT src, result;
4016
if(ice && ice->ifield){
4017
/* move to last ifield, the one we're working */
4018
for(ourifield = ice->ifield;
4019
ourifield && ourifield->next;
4020
ourifield = ourifield->next)
4027
if(kwtype == KWInit){
4028
for(kw = ps_global->keywords; kw; kw = kw->next){
4029
if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
4030
word = (kw->nick && kw->nick[0]) ? kw->nick :
4031
(kw->kw && kw->kw[0]) ? kw->kw : "";
4034
* Pick off the first initial. Since word is UTF-8 it may
4035
* take more than one byte for the first initial.
4038
if(word && word[0]){
4040
unsigned long remaining_octets;
4041
unsigned char *inputp;
4043
remaining_octets = strlen(word);
4044
inputp = (unsigned char *) word;
4045
ucs = (UCS) utf8_get(&inputp, &remaining_octets);
4046
if(!(ucs & U8G_ERROR)){
4047
ielem = new_ielem(&ourifield->ielem);
4048
ielem->freedata = 1;
4049
ielem->datalen = (unsigned) (inputp - (unsigned char *) word);
4050
ielem->data = (char *) fs_get((ielem->datalen + 1) * sizeof(char));
4051
strncpy(ielem->data, word, ielem->datalen);
4052
ielem->data[ielem->datalen] = '\0';
4054
if(pico_usingcolor()
4055
&& ((kw->nick && kw->nick[0]
4056
&& (color=hdr_color(kw->nick,NULL,sc)))
4057
|| (kw->kw && kw->kw[0]
4058
&& (color=hdr_color(kw->kw,NULL,sc))))){
4059
ielem->color = color;
4066
free_color_pair(&color);
4070
else if(kwtype == KW){
4071
for(kw = ps_global->keywords; kw; kw = kw->next){
4072
if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
4075
ielem = new_ielem(&ourifield->ielem);
4076
ielem->freedata = 1;
4077
ielem->data = cpystr(" ");
4083
word = (kw->nick && kw->nick[0]) ? kw->nick :
4084
(kw->kw && kw->kw[0]) ? kw->kw : "";
4087
ielem = new_ielem(&ourifield->ielem);
4088
ielem->freedata = 1;
4089
ielem->data = cpystr(word);
4090
ielem->datalen = strlen(word);
4092
if(pico_usingcolor()
4093
&& ((kw->nick && kw->nick[0]
4094
&& (color=hdr_color(kw->nick,NULL,sc)))
4095
|| (kw->kw && kw->kw[0]
4096
&& (color=hdr_color(kw->kw,NULL,sc))))){
4097
ielem->color = color;
4103
free_color_pair(&color);
4109
* If we're coloring some of the fields then add a dummy field
4110
* at the end that can soak up the rest of the space after the last
4111
* colored keyword. Otherwise, the last one's color will extend to
4112
* the end of the field.
4114
if(pico_usingcolor()){
4115
ielem = new_ielem(&ourifield->ielem);
4116
ielem->freedata = 1;
4117
ielem->data = cpystr(" ");
4121
ourifield->leftadj = 1;
4122
set_ielem_widths_in_field(ourifield);
4127
* Put a string representing the subject into str. Idata tells us which
4128
* message we are referring to.
4130
* This means we should ensure that all data ends up being UTF-8 data.
4131
* That covers the data in ice ielems and str.
4133
* Args idata -- which message?
4134
* width -- desired maximum width of resulting string
4135
* str -- destination buffer (size >= width+1)
4136
* kwtype -- prepend keywords or kw initials before the subject
4137
* opening -- add first text from body of message if there's room
4138
* ice -- index cache entry for message
4141
subj_str(INDEXDATA_S *idata, int width, char *str, SubjKW kwtype, int opening, ICE_S *ice)
4143
char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
4144
char *p, *border, *q = NULL, *free_subj = NULL, *free_this = NULL;
4145
char *sp, *cset = NULL;
4147
int depth = 0, mult = 2, collapsed, i;
4150
PINETHRD_S *thd, *thdorig;
4151
IELEM_S *ielem = NULL, *anotherielem, *subjielem = NULL;
4152
IFIELD_S *ourifield = NULL;
4155
* If we need the data at the start of the message and we're in
4156
* a c-client callback, defer the data lookup until later.
4158
if(opening && idata->no_fetch){
4163
if(ice && ice->ifield){
4164
/* move to last ifield, the one we're working on */
4165
for(ourifield = ice->ifield;
4166
ourifield && ourifield->next;
4167
ourifield = ourifield->next)
4171
memset(str, 0, (width+1) * sizeof(*str));
4173
rawsubj = fetch_subject(idata);
4178
* Before we do anything else, decode the character set in the subject and
4179
* work with the result.
4181
sp = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf,
4182
SIZEOF_20KBUF, rawsubj, &cset);
4184
/* convert the text to UTF-8 if needed */
4185
if(sp == rawsubj || (cset && strucmp(cset, "UTF-8"))){
4186
free_this = convert_to_utf8(rawsubj, cset, 0);
4192
len += 100; /* for possible charset, escaped characters */
4193
origsubj = fs_get((len+1) * sizeof(unsigned char));
4196
iutf8ncpy(origsubj, sp, len);
4198
origsubj[len] = '\0';
4201
if(ice && ice->charset && strucmp(cset, ice->charset)){
4202
/* UH OH: MISMATCHED CHARSETS */
4203
if(strlen(ice->charset) < strlen(UNKNOWN_CHARSET))
4204
fs_resize((void **) &ice->charset,
4205
(strlen(UNKNOWN_CHARSET)+1) * sizeof(char));
4207
strncpy(ice->charset, UNKNOWN_CHARSET, strlen(UNKNOWN_CHARSET)+1);
4209
else if(ice && !ice->charset)
4210
ice->charset = cpystr(cset);
4212
fs_give((void **) &cset);
4217
* origsubj is the original subject but it has been decoded. We need
4218
* to free it at the end of this routine.
4223
* prepend_keyword will put the keyword stuff before the subject
4224
* and split the subject up into its colored parts in subjielem.
4225
* Subjielem is a local ielem which will have to be fit into the
4226
* real ifield->ielem later. The print_format strings in subjielem will
4227
* not be filled in by prepend_keyword because of the fact that we
4228
* may have to adjust things for threading below.
4229
* We use subjielem in case we want to insert some threading information
4230
* at the front of the subject.
4232
if(kwtype == KW || kwtype == KWInit){
4233
subject = prepend_keyword_subject(idata->stream, idata->rawno,
4235
ourifield ? &subjielem : NULL,
4236
ps_global->VAR_KW_BRACES);
4237
free_subj = subject;
4242
subjielem = new_ielem(&subjielem);
4243
subjielem->freedata = 1;
4244
subjielem->data = cpystr(subject);
4245
subjielem->datalen = strlen(subject);
4253
&& (ps_global->thread_disp_style == THREAD_STRUCT
4254
|| ps_global->thread_disp_style == THREAD_MUTTLIKE
4255
|| ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
4256
|| ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
4257
thdorig = thd = fetch_thread(idata->stream, idata->rawno);
4258
border = str + width;
4259
if(pith_opt_condense_thread_cue)
4260
width = (*pith_opt_condense_thread_cue)(thd, ice, &str, width,
4262
&& get_lflag(idata->stream,
4269
while(thd->parent &&
4270
(thd = fetch_thread(idata->stream, thd->parent)))
4274
if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
4277
sptr += (mult*depth);
4278
for(thd = thdorig, p = str + mult*depth - mult;
4279
thd && thd->parent && p >= str;
4280
thd = fetch_thread(idata->stream, thd->parent), p -= mult){
4281
if(p + 2 >= border && !q){
4282
if(width >= 4 && depth < 100){
4283
snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
4286
else if(width >= 5 && depth < 1000){
4287
snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
4291
snprintf(str, width+1, "%s", repeat_char(width, '.'), width);
4304
if(ps_global->thread_disp_style == THREAD_STRUCT
4305
|| ps_global->thread_disp_style == THREAD_MUTTLIKE){
4308
* There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
4311
if(thd == thdorig && !thd->branch)
4312
p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
4313
else if(thd == thdorig || thd->branch)
4316
if(p + 1 < border && thd == thdorig)
4325
* Look to see if the subject is the same as the previous
4326
* message in the thread, if any. If it is the same, don't
4327
* reprint the subject.
4329
* Note that when we're prepending keywords to the subject,
4330
* and the user changes a keyword, we do invalidate
4331
* the index cache for that message but we don't go to the
4332
* trouble of invalidating the index cache for the the child
4333
* of that node in the thread, so the MUTT subject line
4334
* display for the child may be wrong. That is, it may show
4335
* it is the same as this subject even though it no longer
4336
* is, or vice versa.
4338
if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
4342
if(thdorig->parent &&
4343
(thd = fetch_thread(idata->stream, thdorig->parent))
4345
char *this_orig = NULL,
4347
*free_prev_orig = NULL,
4348
*this_prep = NULL, /* includes prepend */
4351
char *prevsubj = NULL;
4353
SORTCACHE *sc = NULL;
4355
/* get the stripped subject of previous message */
4356
mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
4358
sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
4360
if(sc && sc->subject)
4361
prev_orig = sc->subject;
4365
env = pine_mail_fetchenvelope(idata->stream,
4367
stripthis = (env && env->subject)
4368
? env->subject : "";
4370
mail_strip_subject(stripthis, &prev_orig);
4372
free_prev_orig = prev_orig;
4375
mail_strip_subject(rawsubj, &this_orig);
4377
if(kwtype == KW || kwtype == KWInit){
4378
prev_prep = prepend_keyword_subject(idata->stream,
4382
ps_global->VAR_KW_BRACES);
4384
this_prep = prepend_keyword_subject(idata->stream,
4388
ps_global->VAR_KW_BRACES);
4390
if((this_prep || prev_prep)
4391
&& (this_prep && !prev_prep
4392
|| prev_prep && !this_prep
4393
|| strucmp(this_prep, prev_prep)))
4397
if((this_orig || prev_orig)
4398
&& (this_orig && !prev_orig
4399
|| prev_orig && !this_orig
4400
|| strucmp(this_orig, prev_orig)))
4405
* If some of the thread is zoomed out of view, we
4406
* want to display the subject of the first one that
4407
* is in view. If any of the parents or grandparents
4408
* etc of this message are visible, then we don't
4409
* need to worry about it. If all of the parents have
4410
* been zoomed away, then this is the first one.
4412
* When you're looking at a particular case where
4413
* some of the messages of a thread are selected it
4414
* seems like we should look at not only our
4415
* direct parents, but the siblings of the parent
4416
* too. But that's not really correct, because those
4417
* siblings are basically the starts of different
4418
* branches, separate from our branch. They could
4419
* have their own subjects, for example. This will
4420
* give us cases where it looks like we are showing
4421
* the subject too much, but it will be correct!
4423
* In zoom_index() we clear_index_cache_ent for
4424
* some lines which have subjects which might become
4425
* visible when we zoom, and also in set_lflags
4426
* where we might change subjects by unselecting
4427
* something when zoomed.
4431
if(!msgline_hidden(idata->stream,
4432
sp_msgmap(idata->stream),
4433
mn_raw2m(sp_msgmap(idata->stream),
4436
break; /* found a visible parent */
4439
if(thd && thd->parent)
4440
thd = fetch_thread(idata->stream,thd->parent);
4445
if(!thd) /* none were visible */
4450
fs_give((void **) &this_orig);
4453
fs_give((void **) &this_prep);
4456
fs_give((void **) &free_prev_orig);
4459
fs_give((void **) &prev_prep);
4469
width -= (sptr - str);
4471
strncpy(sptr, subject, width);
4474
else if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
4478
* We decided we don't need the subject so we'd better
4479
* eliminate subjielem.
4481
free_ielem(&subjielem);
4485
if(ourifield && sptr && sptr > origstr){
4486
ielem = new_ielem(&ourifield->ielem);
4487
ielem->type = eThreadInfo;
4488
ielem->freedata = 1;
4491
ielem->data = cpystr(origstr);
4492
ielem->datalen = strlen(origstr);
4498
* Not much to do for the non-threading case. Just copy the
4499
* subject we have so far into str and truncate it.
4501
strncpy(str, subject, width);
4507
* We need to add subjielem to the end of the ourifield->ielem list.
4510
if(ourifield->ielem){
4511
for(ielem = ourifield->ielem;
4512
ielem && ielem->next; ielem = ielem->next)
4515
ielem->next = subjielem;
4518
ourifield->ielem = subjielem;
4521
ourifield->leftadj = 1;
4524
if(opening && ourifield){
4525
IELEM_S *ftielem = NULL;
4529
first_text = fetch_firsttext(idata);
4532
ftielem = new_ielem(&ftielem);
4533
ftielem->type = eOpening;
4534
ftielem->freedata = 1;
4535
len = strlen(first_text) + 3;
4536
ftielem->data = (char *) fs_get((len + 1) * sizeof(char));
4537
strncpy(ftielem->data, " - ", 4);
4538
strncpy(ftielem->data+3, first_text, len+1-3);
4539
ftielem->data[len] = '\0';
4540
ftielem->datalen = strlen(ftielem->data);
4542
fs_give((void **) &first_text);
4545
if(pico_usingcolor()
4546
&& ps_global->VAR_IND_OP_FORE_COLOR
4547
&& ps_global->VAR_IND_OP_BACK_COLOR){
4548
ftielem->freecolor = 1;
4549
ftielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR, ps_global->VAR_IND_OP_BACK_COLOR);
4551
ielem = new_ielem(&ftielem);
4552
ielem->freedata = 1;
4553
ielem->data = cpystr(" ");
4557
if(ourifield->ielem){
4558
for(ielem = ourifield->ielem;
4559
ielem && ielem->next; ielem = ielem->next)
4562
ielem->next = ftielem;
4565
ourifield->ielem = ftielem;
4568
ourifield->leftadj = 1;
4573
set_ielem_widths_in_field(ourifield);
4576
fs_give((void **) &origsubj);
4579
fs_give((void **) &free_subj);
4582
fs_give((void **) &free_this);
4587
* Returns an allocated string which is the passed in subject with a
4588
* list of keywords prepended.
4590
* If kwtype == KW you will end up with
4592
* {keyword1 keyword2} subject
4594
* (actually, keyword nicknames will be used instead of the actual keywords
4595
* in the case that the user defined nicknames)
4597
* If kwtype == KWInit you get
4601
* where A is the first letter of the first keyword and B is the first letter
4602
* of the second defined keyword. No space between them. There could be more
4605
* If an ielemp is passed in it will be filled out with the data and colors
4606
* of the pieces of the subject but the print_format strings will not
4610
prepend_keyword_subject(MAILSTREAM *stream, long int rawno, char *subject,
4611
SubjKW kwtype, IELEM_S **ielemp, char *braces)
4614
char *p, *next_piece, *retsubj = NULL, *str;
4615
char *left_brace = NULL, *right_brace = NULL;
4617
int some_set = 0, save;
4620
COLOR_PAIR *color = NULL;
4621
SPEC_COLOR_S *sc = ps_global->kw_colors;
4627
if(braces && *braces)
4628
get_pair(braces, &left_brace, &right_brace, 1, 0);
4630
len = (left_brace ? strlen(left_brace) : 0) +
4631
(right_brace ? strlen(right_brace) : 0);
4633
if(stream && rawno >= 0L && rawno <= stream->nmsgs){
4634
for(kw = ps_global->keywords; kw; kw = kw->next)
4635
if(user_flag_is_set(stream, rawno, kw->kw)){
4638
len++; /* space between keywords */
4640
str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
4643
else if(kwtype == KWInit){
4644
str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
4645
/* interested in only the first UTF-8 initial */
4648
unsigned long remaining_octets;
4649
unsigned char *inputp;
4651
remaining_octets = strlen(str);
4652
inputp = (unsigned char *) str;
4653
ucs = (UCS) utf8_get(&inputp, &remaining_octets);
4654
if(!(ucs & U8G_ERROR)){
4655
len += (unsigned) (inputp - (unsigned char *) str);
4664
if((kwtype == KW || kwtype == KWInit) && some_set){
4665
len += strlen(subject); /* subject is already UTF-8 if needed */
4666
retsubj = (char *) fs_get((len + 1) * sizeof(*retsubj));
4667
memset(retsubj, 0, (len + 1) * sizeof(*retsubj));
4668
next_piece = p = retsubj;
4670
for(kw = ps_global->keywords; kw; kw = kw->next){
4671
if(user_flag_is_set(stream, rawno, kw->kw)){
4673
if(left_brace && len > 0)
4674
sstrncpy(&p, left_brace, len);
4676
else if(kwtype == KW)
4679
if(ielemp && p > next_piece){
4682
ielem = new_ielem(ielemp);
4683
ielem->freedata = 1;
4684
ielem->data = cpystr(next_piece);
4685
ielem->datalen = strlen(next_piece);
4690
str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
4692
if(kwtype == KWInit){
4695
unsigned long remaining_octets;
4696
unsigned char *inputp;
4698
remaining_octets = strlen(str);
4699
inputp = (unsigned char *) str;
4700
ucs = (UCS) utf8_get(&inputp, &remaining_octets);
4701
if(!(ucs & U8G_ERROR)){
4702
if(len-(p-retsubj) > 0){
4703
sstrncpy(&p, str, MIN(inputp - (unsigned char *) str,len-(p-retsubj)));
4704
if(p > next_piece && ielemp && pico_usingcolor()
4705
&& ((kw->nick && kw->nick[0]
4706
&& (color=hdr_color(kw->nick,NULL,sc)))
4707
|| (kw->kw && kw->kw[0]
4708
&& (color=hdr_color(kw->kw,NULL,sc))))){
4709
ielem = new_ielem(ielemp);
4710
ielem->freedata = 1;
4713
ielem->data = cpystr(next_piece);
4714
ielem->datalen = strlen(next_piece);
4715
ielem->color = color;
4724
free_color_pair(&color);
4728
if(len-(p-retsubj) > 0)
4729
sstrncpy(&p, str, len-(p-retsubj));
4731
if(p > next_piece && ielemp && pico_usingcolor()
4732
&& ((kw->nick && kw->nick[0]
4733
&& (color=hdr_color(kw->nick,NULL,sc)))
4734
|| (kw->kw && kw->kw[0]
4735
&& (color=hdr_color(kw->kw,NULL,sc))))){
4736
ielem = new_ielem(ielemp);
4737
ielem->freedata = 1;
4740
ielem->data = cpystr(next_piece);
4741
ielem->datalen = strlen(next_piece);
4742
ielem->color = color;
4749
free_color_pair(&color);
4754
if(len-(p-retsubj) > 0 && right_brace)
4755
sstrncpy(&p, right_brace, len-(p-retsubj));
4757
if(len-(p-retsubj) > 0 && subject)
4758
sstrncpy(&p, subject, len-(p-retsubj));
4760
if(ielemp && p > next_piece){
4763
ielem = new_ielem(ielemp);
4764
ielem->freedata = 1;
4765
ielem->data = cpystr(next_piece);
4766
ielem->datalen = strlen(next_piece);
4771
retsubj[len] = '\0'; /* just making sure */
4775
ielem = new_ielem(ielemp);
4776
ielem->freedata = 1;
4777
ielem->data = cpystr(subject);
4778
ielem->datalen = strlen(subject);
4781
retsubj = cpystr(subject);
4786
fs_give((void **) &left_brace);
4789
fs_give((void **) &right_brace);
4797
* This means we should ensure that all data ends up being UTF-8 data.
4798
* That covers the data in ice ielems and str.
4801
from_str(IndexColType ctype, INDEXDATA_S *idata, int width, char *str, ICE_S *ice)
4803
char *field, *newsgroups, *border, *p, *fptr = NULL, *q = NULL;
4805
int depth = 0, mult = 2;
4806
PINETHRD_S *thd, *thdorig;
4809
&& (ps_global->thread_disp_style == THREAD_INDENT_FROM1
4810
|| ps_global->thread_disp_style == THREAD_INDENT_FROM2
4811
|| ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
4812
thdorig = thd = fetch_thread(idata->stream, idata->rawno);
4813
border = str + width;
4814
if(pith_opt_condense_thread_cue)
4815
width = (*pith_opt_condense_thread_cue)(thd, ice, &str, width,
4817
&& get_lflag(idata->stream,
4824
while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
4828
if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
4831
fptr += (mult*depth);
4832
for(thd = thdorig, p = str + mult*depth - mult;
4833
thd && thd->parent && p >= str;
4834
thd = fetch_thread(idata->stream, thd->parent), p -= mult){
4835
if(p + 2 >= border && !q){
4836
if(width >= 4 && depth < 100){
4837
snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
4840
else if(width >= 5 && depth < 1000){
4841
snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
4845
snprintf(str, width+1, "%s", repeat_char(width, '.'), width);
4855
if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
4858
* There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
4861
if(thd == thdorig && !thd->branch)
4862
p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
4863
else if(thd == thdorig || thd->branch)
4870
else if(p < border){
4872
if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
4875
* There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
4878
if(thd == thdorig && !thd->branch)
4879
p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
4880
else if(thd == thdorig || thd->branch)
4891
width = (str + width) - fptr;
4894
case iFromToNotNews:
4895
if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
4897
strncpy(fptr, "To: ", width);
4902
if((field = ((addr = fetch_to(idata))
4904
: (addr = fetch_cc(idata))
4907
&& set_index_addr(idata, field, addr, "To: ",
4909
ice ? &ice->charset : NULL))
4912
if(ctype == iFromTo &&
4913
(newsgroups = fetch_newsgroups(idata)) &&
4915
snprintf(fptr, width, "To: %-*.*s", width-4, width-4,
4920
/* else fall thru to From: */
4923
/* else fall thru to From: */
4929
set_index_addr(idata, "From", fetch_from(idata),
4930
NULL, width, fptr, ice ? &ice->charset : NULL);
4935
if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
4936
char *mb = NULL, *hst = NULL, *at = NULL;
4940
if(ctype == iAddress && addr->host && addr->host[0]
4941
&& addr->host[0] != '.'){
4947
if(!at || width <= len)
4948
snprintf(fptr, width+1, "%-*.*s", width, width, mb);
4950
snprintf(fptr, width+1, "%s@%-*.*s", mb, width-len-1, width-len-1, hst);
4960
* Set up the elements contained in field so that they take up the
4961
* whole field width. Data is assumed to be UTF-8.
4964
set_ielem_widths_in_field(IFIELD_S *ifield)
4966
IELEM_S *ielem = NULL;
4967
int datawidth, fmtwidth;
4972
fmtwidth = ifield->width;
4974
for(ielem = ifield->ielem; ielem && fmtwidth > 0; ielem = ielem->next){
4975
if(!ifield->leftadj && ielem->next){
4976
dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield->ctype));
4980
datawidth = (int) utf8_width(ielem->data);
4981
if(datawidth >= fmtwidth || !ielem->next){
4982
set_print_format(ielem, fmtwidth, ifield->leftadj);
4986
set_print_format(ielem, datawidth, ifield->leftadj);
4987
fmtwidth -= datawidth;
4994
* Simple hash function from K&R 2nd edition, p. 144.
4996
* This one is modified to never return 0 so we can use that as a special
4997
* value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
4998
* as a special value that can't be returned by line_hash.
5003
unsigned long hashval;
5005
for(hashval = 0; *s != '\0'; s++)
5006
hashval = *s + 31 * hashval;
5008
hashval = hashval % LINE_HASH_N;
5018
* Returns nonzero if considered hidden, 0 if not considered hidden.
5021
msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
5025
if(flags & MH_ANYTHD){
5026
ret = ((any_lflagged(msgmap, MN_HIDE) > 0)
5027
&& get_lflag(stream, msgmap, msgno, MN_HIDE));
5029
else if(flags & MH_THISTHD && THREADING() && sp_viewing_a_thread(stream)){
5030
ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
5031
|| !get_lflag(stream, msgmap, msgno, MN_CHID2));
5034
if(THREADING() && sp_viewing_a_thread(stream)){
5035
ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
5036
|| !get_lflag(stream, msgmap, msgno, MN_CHID2)
5037
|| get_lflag(stream, msgmap, msgno, MN_CHID));
5039
else if(THRD_INDX()){
5041
* If this message is in the collapsed part of a thread,
5042
* it's hidden. It must be a top-level of a thread to be
5043
* considered visible. Even if it is top-level, it is only
5044
* visible if some message in the thread is not hidden.
5046
if(get_lflag(stream, msgmap, msgno, MN_CHID)) /* not top */
5049
unsigned long rawno;
5050
PINETHRD_S *thrd = NULL;
5052
rawno = mn_m2raw(msgmap, msgno);
5054
thrd = fetch_thread(stream, rawno);
5056
ret = !thread_has_some_visible(stream, thrd);
5060
ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
5061
&& get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
5066
"msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
5073
adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
5078
cur = mn_get_cur(msgmap);
5080
/* if current is hidden, adjust */
5081
if(cur >= 1L && cur <= mn_get_total(msgmap)
5082
&& msgline_hidden(stream, msgmap, cur, 0)){
5084
dir = mn_get_revsort(msgmap) ? -1 : 1;
5087
((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
5088
&& msgline_hidden(stream, msgmap, n, 0);
5092
if((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
5093
mn_reset_cur(msgmap, n);
5094
else{ /* no visible in that direction */
5096
((dir == 1 && n >= 1L)
5097
|| (dir == -1 && n <= mn_get_total(msgmap)))
5098
&& msgline_hidden(stream, msgmap, n, 0);
5102
if((dir == -1 && n >= 1L)
5103
|| (dir == 1 && n <= mn_get_total(msgmap)))
5104
mn_reset_cur(msgmap, n);
5112
setup_for_index_index_screen(void)
5114
format_index_line = format_index_index_line;
5115
setup_header_widths = setup_index_header_widths;
5120
setup_for_thread_index_screen(void)
5122
format_index_line = format_thread_index_line;
5123
setup_header_widths = setup_thread_header_widths;
5128
ice_hash(ICE_S *ice)
5130
char buf[MAX_SCREEN_COLS+1];
5135
simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice, 0L);
5137
buf[sizeof(buf) - 1] = '\0';
5139
return(line_hash(buf));
5144
left_adjust(int width)
5146
return(format_str(width, 1));
5151
right_adjust(int width)
5153
return(format_str(width, 0));
5158
* Returns allocated and filled in format string.
5161
format_str(int width, int left)
5166
len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
5167
format = (char *) fs_get(len + 1);
5168
copy_format_str(width, left, format, len);
5176
* Put the left or right adjusted format string of width width into
5177
* dest. Dest is of size n+1.
5180
copy_format_str(int width, int left, char *dest, int n)
5185
p = int2string(width);
5187
snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
5196
* Sets up the print_format string to be width wide with left or right
5197
* adjust. Takes care of memory freeing and allocation.
5200
set_print_format(IELEM_S *ielem, int width, int leftadj)
5205
if(ielem->print_format){
5206
/* is there enough room? */
5207
if(ielem->freeprintf < PRINT_FORMAT_LEN(width,leftadj)+1){
5208
fs_resize((void **) &ielem->print_format,
5209
(PRINT_FORMAT_LEN(width,leftadj)+1) * sizeof(char));
5210
ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
5213
copy_format_str(width, leftadj, ielem->print_format,
5214
PRINT_FORMAT_LEN(width,leftadj));
5217
ielem->print_format = leftadj ? left_adjust(width)
5218
: right_adjust(width);
5219
ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);