~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to pith/mailindx.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#if !defined(lint) && !defined(DOS)
 
2
static char rcsid[] = "$Id: mailindx.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
 
3
#endif
 
4
 
 
5
/* ========================================================================
 
6
 * Copyright 2006-2007 University of Washington
 
7
 *
 
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
 
11
 *
 
12
 *     http://www.apache.org/licenses/LICENSE-2.0
 
13
 *
 
14
 * ========================================================================
 
15
 */
 
16
 
 
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"
 
41
#ifdef _WINDOWS
 
42
#include "../pico/osdep/mswin.h"
 
43
#endif
 
44
 
 
45
/*
 
46
 * pointers to formatting functions 
 
47
 */
 
48
ICE_S          *(*format_index_line)(INDEXDATA_S *);
 
49
void            (*setup_header_widths)(MAILSTREAM *);
 
50
 
 
51
/*
 
52
 * pointer to optional load_overview functionality
 
53
 */
 
54
void    (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
 
55
 
 
56
/*
 
57
 * pointer to hook for saving index format state
 
58
 */
 
59
void    (*pith_opt_save_index_state)(int);
 
60
 
 
61
/*
 
62
 * hook to allow caller to insert cue that indicates a condensed
 
63
 * thread relationship cue
 
64
 */
 
65
int     (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, int, int);
 
66
 
 
67
 
 
68
/*
 
69
 * Internal prototypes
 
70
 */
 
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 *);
 
100
 
 
101
 
 
102
#define BIGWIDTH 2047
 
103
 
 
104
 
 
105
/*----------------------------------------------------------------------
 
106
      Initialize the index_disp_format array in ps_global from this
 
107
      format string.
 
108
 
 
109
   Args: format -- the string containing the format tokens
 
110
         answer -- put the answer here, free first if there was a previous
 
111
                    value here
 
112
 ----*/
 
113
void
 
114
init_index_format(char *format, INDEX_COL_S **answer)
 
115
{
 
116
    int column = 0;
 
117
 
 
118
    /*
 
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.
 
123
     */
 
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);
 
129
    }
 
130
 
 
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[] = {
 
135
            {iStatus, Fixed, 3},
 
136
            {iMessNo, WeCalculate},
 
137
            {iSDateTime, WeCalculate},
 
138
            {iFromTo, Percent, 33}, /* percent of rest */
 
139
            {iSizeNarrow, WeCalculate},
 
140
            {iSubjKey, Percent, 67},
 
141
            {iNothing}
 
142
        };
 
143
 
 
144
        if(*answer)
 
145
          fs_give((void **)answer);
 
146
 
 
147
        *answer = (INDEX_COL_S *)fs_get(sizeof(answer_default));
 
148
        memcpy(*answer, answer_default, sizeof(answer_default));
 
149
    }
 
150
 
 
151
    /*
 
152
     * Fill in req_width's for WeCalculate items.
 
153
     */
 
154
    for(column = 0; (*answer)[column].ctype != iNothing; column++){
 
155
        if((*answer)[column].wtype == WeCalculate){
 
156
            switch((*answer)[column].ctype){
 
157
              case iAtt:
 
158
                (*answer)[column].req_width = 1;
 
159
                break;
 
160
              case iYear2Digit:
 
161
              case iDay:
 
162
              case iMon:
 
163
              case iDay2Digit:
 
164
              case iMon2Digit:
 
165
              case iArrow:
 
166
                (*answer)[column].req_width = 2;
 
167
                break;
 
168
              case iStatus:
 
169
              case iMessNo:
 
170
              case iMonAbb:
 
171
              case iInit:
 
172
              case iDayOfWeekAbb:
 
173
                (*answer)[column].req_width = 3;
 
174
                break;
 
175
              case iYear:
 
176
              case iDayOrdinal:
 
177
                (*answer)[column].req_width = 4;
 
178
                break;
 
179
              case iTime24:
 
180
              case iTimezone:
 
181
              case iSizeNarrow:
 
182
                (*answer)[column].req_width = 5;
 
183
                break;
 
184
              case iFStatus:
 
185
              case iIStatus:
 
186
              case iDate:
 
187
              case iScore:
 
188
                (*answer)[column].req_width = 6;
 
189
                break;
 
190
              case iTime12:
 
191
              case iSTime:
 
192
              case iKSize:
 
193
              case iSize:
 
194
                (*answer)[column].req_width = 7;
 
195
                break;
 
196
              case iS1Date:
 
197
              case iS2Date:
 
198
              case iS3Date:
 
199
              case iS4Date:
 
200
              case iDateIsoS:
 
201
              case iSizeComma:
 
202
                (*answer)[column].req_width = 8;
 
203
                break;
 
204
              case iSDate:
 
205
              case iSDateTime:
 
206
              case iSDateTime24:
 
207
                {
 
208
                    /*
 
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...
 
213
                     */
 
214
                    struct tm tm;
 
215
                    char ss[100];
 
216
 
 
217
                    memset(&tm, 0, sizeof(tm));
 
218
                    tm.tm_year = 106;
 
219
                    tm.tm_mon = 11;
 
220
                    tm.tm_mday = 31;
 
221
                    strftime(ss, sizeof(ss), "%x", &tm);
 
222
                    (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), 20);
 
223
                }
 
224
                break;
 
225
              case iDescripSize:
 
226
              case iSDateIsoS:
 
227
              case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
 
228
              case iSDateTimeIsoS:
 
229
              case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
 
230
              case iSDateTimeIsoS24:
 
231
              case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
 
232
              case iMonLong:
 
233
              case iDayOfWeek:
 
234
                /*
 
235
                 * These SDates are 8 wide but they need to be 9 for "Yesterday".
 
236
                 */
 
237
                (*answer)[column].req_width = 9;
 
238
                break;
 
239
              case iDateIso:
 
240
              case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
 
241
                (*answer)[column].req_width = 10;
 
242
                break;
 
243
              case iLDate:
 
244
                (*answer)[column].req_width = 12;
 
245
                break;
 
246
              case iRDate:
 
247
                (*answer)[column].req_width = 16;
 
248
                break;
 
249
            }
 
250
        }
 
251
    }
 
252
}
 
253
 
 
254
 
 
255
void
 
256
reset_index_format(void)
 
257
{
 
258
    long rflags = ROLE_DO_OTHER;
 
259
    PAT_STATE     pstate;
 
260
    PAT_S        *pat;
 
261
    int           we_set_it = 0;
 
262
 
 
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))
 
267
              break;
 
268
        }
 
269
 
 
270
        if(pat && pat->action && !pat->action->bogus
 
271
           && pat->action->index_format){
 
272
            we_set_it++;
 
273
            init_index_format(pat->action->index_format,
 
274
                              &ps_global->index_disp_format);
 
275
        }
 
276
    }
 
277
 
 
278
    if(!we_set_it)
 
279
      init_index_format(ps_global->VAR_INDEX_FORMAT,
 
280
                        &ps_global->index_disp_format);
 
281
}
 
282
 
 
283
 
 
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}
 
396
};
 
397
 
 
398
INDEX_PARSE_T *
 
399
itoken(int i)
 
400
{
 
401
    return((i < sizeof(itokens) && itokens[i].name) ? &itokens[i] : NULL);
 
402
}
 
403
 
 
404
 
 
405
/*
 
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.
 
411
 *
 
412
 * Returns  A ptr to an INDEX_PARSE_T from itokens above, else NULL.
 
413
 */
 
414
INDEX_PARSE_T *
 
415
itoktype(char *txt, int flags)
 
416
{
 
417
    INDEX_PARSE_T *pt;
 
418
    char           token[100 + 1];
 
419
    char          *v, *w;
 
420
 
 
421
    /*
 
422
     * Separate a copy of the possible token out of txt.
 
423
     */
 
424
    v = txt;
 
425
    w = token;
 
426
    while(w < token+100 &&
 
427
          *v &&
 
428
          !isspace((unsigned char)*v) &&
 
429
          !(flags & DELIM_USCORE && *v == '_') &&
 
430
          !(flags & DELIM_PAREN && *v == '('))
 
431
      *w++ = *v++;
 
432
    
 
433
    *w = '\0';
 
434
 
 
435
    for(pt = itokens; pt->name; pt++)
 
436
      if(pt->what_for & flags && !strucmp(pt->name, token))
 
437
        return(pt);
 
438
    
 
439
    return(NULL);
 
440
}
 
441
 
 
442
 
 
443
int
 
444
parse_index_format(char *format_str, INDEX_COL_S **answer)
 
445
{
 
446
    int            i, column = 0;
 
447
    char          *p, *q;
 
448
    INDEX_PARSE_T *pt;
 
449
    INDEX_COL_S    cdesc[200]; /* plenty of temp storage for answer */
 
450
 
 
451
    memset((void *)cdesc, 0, sizeof(cdesc));
 
452
 
 
453
    p = format_str;
 
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);
 
458
        
 
459
        /* ignore unrecognized word */
 
460
        if(!pt){
 
461
            for(q = p; *p && !isspace((unsigned char)*p); p++)
 
462
              ;
 
463
 
 
464
            if(*p)
 
465
              *p++ = '\0';
 
466
 
 
467
            dprint((1,
 
468
                   "parse_index_format: unrecognized token: %s\n",
 
469
                   q ? q : "?"));
 
470
            q_status_message1(SM_ORDER | SM_DING, 0, 3,
 
471
                              _("Unrecognized word in index-format: %s"), q);
 
472
            continue;
 
473
        }
 
474
 
 
475
        cdesc[column].ctype = pt->ctype;
 
476
 
 
477
        /* skip over name and look for parens */
 
478
        p += strlen(pt->name);
 
479
        if(*p == '('){
 
480
            p++;
 
481
            q = p;
 
482
            while(p && *p && isdigit((unsigned char) *p))
 
483
              p++;
 
484
            
 
485
            if(p && *p && *p == ')' && p > q){
 
486
                cdesc[column].wtype = Fixed;
 
487
                cdesc[column].req_width = atoi(q);
 
488
            }
 
489
            else if(p && *p && *p == '%' && p > q){
 
490
                cdesc[column].wtype = Percent;
 
491
                cdesc[column].req_width = atoi(q);
 
492
            }
 
493
            else{
 
494
                cdesc[column].wtype = WeCalculate;
 
495
                cdesc[column].req_width = 0;
 
496
            }
 
497
        }
 
498
        else{
 
499
            cdesc[column].wtype     = WeCalculate;
 
500
            cdesc[column].req_width = 0;
 
501
        }
 
502
 
 
503
        column++;
 
504
        /* skip text at end of word */
 
505
        while(p && *p && !isspace((unsigned char)*p))
 
506
          p++;
 
507
    }
 
508
 
 
509
    /* if, after all that, we didn't find anything recognizable, bitch */
 
510
    if(!column){
 
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."));
 
514
        return(0);
 
515
    }
 
516
 
 
517
    /* Finish with Nothing column */
 
518
    cdesc[column].ctype = iNothing;
 
519
 
 
520
    /* free up old answer */
 
521
    if(*answer)
 
522
      fs_give((void **)answer);
 
523
 
 
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];
 
530
 
 
531
    return(1);
 
532
}
 
533
 
 
534
 
 
535
/*
 
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.
 
539
 */
 
540
static IndexColType fixed_ctypes[] = {
 
541
    iMessNo, iStatus, iFStatus, iIStatus, iDate, iSDate, iSDateTime, iSDateTime24,
 
542
    iSTime, iLDate,
 
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
 
553
};
 
554
 
 
555
 
 
556
int
 
557
ctype_is_fixed_length(IndexColType ctype)
 
558
{
 
559
    int j;
 
560
 
 
561
    for(j = 0; ; j++){
 
562
        if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
 
563
          break;
 
564
    
 
565
        if(ctype == fixed_ctypes[j])
 
566
          return 1;
 
567
    }
 
568
 
 
569
    return 0;
 
570
}
 
571
    
 
572
 
 
573
/*----------------------------------------------------------------------
 
574
      Setup the widths of the various columns in the index display
 
575
 ----*/
 
576
void
 
577
setup_index_header_widths(MAILSTREAM *stream)
 
578
{
 
579
    int          j, columns, some_to_calculate;
 
580
    int          space_left, screen_width, width, fix, col;
 
581
    int          keep_going, tot_pct, was_sl;
 
582
    long         max_msgno;
 
583
    WidthType    wtype;
 
584
    INDEX_COL_S *cdesc;
 
585
 
 
586
    max_msgno = mn_get_total(ps_global->msgmap);
 
587
 
 
588
    dprint((8, "=== setup_index_header_widths() ===\n"));
 
589
 
 
590
    clear_icache_flags(stream);
 
591
    screen_width = ps_global->ttyo->screen_cols;
 
592
    space_left   = screen_width;
 
593
    columns      = some_to_calculate = 0;
 
594
 
 
595
    /*
 
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.
 
600
     */
 
601
    for(cdesc = ps_global->index_disp_format;
 
602
        cdesc->ctype != iNothing;
 
603
        cdesc++){
 
604
 
 
605
        if(cdesc->wtype == Fixed){
 
606
          cdesc->width = cdesc->req_width;
 
607
          if(cdesc->width > 0)
 
608
            columns++;
 
609
        }
 
610
        else if(cdesc->wtype == Percent){
 
611
            cdesc->width = 0; /* calculated later */
 
612
            columns++;
 
613
        }
 
614
        else{ /* WeCalculate */
 
615
            cdesc->width = cdesc->req_width; /* reserve this for now */
 
616
            some_to_calculate++;
 
617
            columns++;
 
618
        }
 
619
 
 
620
        space_left -= cdesc->width;
 
621
    }
 
622
 
 
623
    space_left -= (columns - 1); /* space between columns */
 
624
 
 
625
    ps_global->display_keywords_in_subject = 0;
 
626
    ps_global->display_keywordinits_in_subject = 0;
 
627
 
 
628
    /*
 
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
 
632
     * in fixed_ctypes.
 
633
     */
 
634
    for(cdesc = ps_global->index_disp_format;
 
635
        cdesc->ctype != iNothing;
 
636
        cdesc++){
 
637
 
 
638
        wtype = cdesc->wtype;
 
639
 
 
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;
 
644
 
 
645
        if(wtype == WeCalculate || wtype == Percent || cdesc->width != 0){
 
646
            if(ctype_is_fixed_length(cdesc->ctype)){
 
647
                switch(cdesc->ctype){
 
648
                  case iAtt:
 
649
                    cdesc->actual_length = 1;
 
650
                    cdesc->adjustment = Left;
 
651
                    break;
 
652
 
 
653
                  case iYear2Digit:
 
654
                  case iDay2Digit:
 
655
                  case iMon2Digit:
 
656
                    cdesc->actual_length = 2;
 
657
                    cdesc->adjustment = Left;
 
658
                    break;
 
659
 
 
660
                  case iArrow:
 
661
                    cdesc->actual_length = 2;
 
662
                    cdesc->adjustment = Right;
 
663
                    break;
 
664
 
 
665
                  case iStatus:
 
666
                  case iMonAbb:
 
667
                  case iDayOfWeekAbb:
 
668
                    cdesc->actual_length = 3;
 
669
                    cdesc->adjustment = Left;
 
670
                    break;
 
671
 
 
672
                  case iMessNo:
 
673
                    set_format_includes_msgno(stream);
 
674
                    if(max_msgno < 1000)
 
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;
 
680
                    else
 
681
                      cdesc->actual_length = 6;
 
682
 
 
683
                    cdesc->adjustment = Right;
 
684
                    break;
 
685
 
 
686
                  case iYear:
 
687
                    cdesc->actual_length = 4;
 
688
                    cdesc->adjustment = Left;
 
689
                    break;
 
690
 
 
691
                  case iTime24:
 
692
                  case iTimezone:
 
693
                    cdesc->actual_length = 5;
 
694
                    cdesc->adjustment = Left;
 
695
                    break;
 
696
 
 
697
                  case iSizeNarrow:
 
698
                    cdesc->actual_length = 5;
 
699
                    cdesc->adjustment = Right;
 
700
                    break;
 
701
 
 
702
                  case iFStatus:
 
703
                  case iIStatus:
 
704
                  case iDate:
 
705
                    cdesc->actual_length = 6;
 
706
                    cdesc->adjustment = Left;
 
707
                    break;
 
708
 
 
709
                  case iScore:
 
710
                    cdesc->actual_length = 6;
 
711
                    cdesc->adjustment = Right;
 
712
                    break;
 
713
 
 
714
                  case iTime12:
 
715
                  case iSize:
 
716
                  case iKSize:
 
717
                    cdesc->actual_length = 7;
 
718
                    cdesc->adjustment = Right;
 
719
                    break;
 
720
 
 
721
                  case iSTime:
 
722
                    set_format_includes_smartdate(stream);
 
723
                    cdesc->actual_length = 7;
 
724
                    cdesc->adjustment = Left;
 
725
                    break;
 
726
 
 
727
                  case iS1Date:
 
728
                  case iS2Date:
 
729
                  case iS3Date:
 
730
                  case iS4Date:
 
731
                  case iDateIsoS:
 
732
                    cdesc->actual_length = 8;
 
733
                    cdesc->adjustment = Left;
 
734
                    break;
 
735
 
 
736
                  case iSizeComma:
 
737
                    cdesc->actual_length = 8;
 
738
                    cdesc->adjustment = Right;
 
739
                    break;
 
740
 
 
741
                  case iSDate:
 
742
                  case iSDateTime:
 
743
                  case iSDateTime24:
 
744
                    cdesc->actual_length = cdesc->req_width;
 
745
                    cdesc->adjustment = Left;
 
746
                    break;
 
747
 
 
748
                  case iSDateIsoS:
 
749
                  case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
 
750
                  case iSDateTimeIsoS:
 
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;
 
760
                    else
 
761
                      cdesc->actual_length = 9;
 
762
 
 
763
                    cdesc->adjustment = Left;
 
764
                    break;
 
765
 
 
766
                  case iDescripSize:
 
767
                    cdesc->actual_length = 9;
 
768
                    cdesc->adjustment = Right;
 
769
                    break;
 
770
 
 
771
                  case iDateIso:
 
772
                    cdesc->actual_length = 10;
 
773
                    cdesc->adjustment = Left;
 
774
                    break;
 
775
 
 
776
                  case iLDate:
 
777
                    cdesc->actual_length = 12;
 
778
                    cdesc->adjustment = Left;
 
779
                    break;
 
780
                  
 
781
                  default:
 
782
                    panic("Unhandled fixed case in setup_index_header");
 
783
                    break;
 
784
                }
 
785
            }
 
786
            else
 
787
              cdesc->adjustment = Left;
 
788
        }
 
789
    }
 
790
 
 
791
    if(ps_global->display_keywords_in_subject)
 
792
      ps_global->display_keywordinits_in_subject = 0;
 
793
 
 
794
    /* if have reserved unneeded space for size, give it back */
 
795
    for(cdesc = ps_global->index_disp_format;
 
796
        cdesc->ctype != iNothing;
 
797
        cdesc++)
 
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 */
 
803
                  cdesc->width = 0;
 
804
                  space_left += fix;
 
805
              }
 
806
 
 
807
              space_left++;  /* +1 for space between columns */
 
808
          }
 
809
      }
 
810
 
 
811
    /*
 
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.
 
816
     */
 
817
    for(j = 0; space_left > 0 && some_to_calculate; j++){
 
818
 
 
819
      if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
 
820
        break;
 
821
 
 
822
      for(cdesc = ps_global->index_disp_format;
 
823
          cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
 
824
          cdesc++)
 
825
        if(cdesc->ctype == fixed_ctypes[j] && cdesc->wtype == WeCalculate){
 
826
            some_to_calculate--;
 
827
            fix = MIN(cdesc->actual_length - cdesc->width, space_left);
 
828
            cdesc->width += fix;
 
829
            space_left -= fix;
 
830
        }
 
831
    }
 
832
 
 
833
    /*
 
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.
 
837
     */
 
838
    if(space_left > 0){
 
839
      if(some_to_calculate){
 
840
        int tot_requested = 0;
 
841
 
 
842
        /*
 
843
         * Requests are treated as percent of screen width. See if they
 
844
         * will all fit. If not, trim them back proportionately.
 
845
         */
 
846
        for(cdesc = ps_global->index_disp_format;
 
847
            cdesc->ctype != iNothing;
 
848
            cdesc++){
 
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;
 
854
          }
 
855
        }
 
856
 
 
857
        if(tot_requested > space_left){
 
858
          int multiplier = (100 * space_left) / tot_requested;
 
859
 
 
860
          for(cdesc = ps_global->index_disp_format;
 
861
              cdesc->ctype != iNothing && space_left > 0;
 
862
              cdesc++){
 
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);
 
869
                cdesc->width += fix;
 
870
                space_left -= fix;
 
871
            }
 
872
          }
 
873
        }
 
874
        else{
 
875
          for(cdesc = ps_global->index_disp_format;
 
876
              cdesc->ctype != iNothing && space_left > 0;
 
877
              cdesc++){
 
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);
 
883
                cdesc->width += fix;
 
884
                space_left -= fix;
 
885
            }
 
886
          }
 
887
        }
 
888
      }
 
889
      else{
 
890
        tot_pct = 0;
 
891
        was_sl = space_left;
 
892
        /* add up total percentages requested */
 
893
        for(cdesc = ps_global->index_disp_format;
 
894
            cdesc->ctype != iNothing;
 
895
            cdesc++)
 
896
          if(cdesc->wtype == Percent)
 
897
            tot_pct += cdesc->req_width;
 
898
 
 
899
        /* give relative weight to requests */
 
900
        for(cdesc = ps_global->index_disp_format;
 
901
            cdesc->ctype != iNothing && space_left > 0 && tot_pct > 0;
 
902
            cdesc++){
 
903
            if(cdesc->wtype == Percent){
 
904
                fix = ((2*cdesc->req_width*was_sl)+tot_pct) / (2*tot_pct);
 
905
                fix = MIN(fix, space_left);
 
906
                cdesc->width += fix;
 
907
                space_left -= fix;
 
908
            }
 
909
        }
 
910
      }
 
911
    }
 
912
 
 
913
    /* split up rest, give twice as much to Subject */
 
914
    keep_going = 1;
 
915
    while(space_left > 0 && keep_going){
 
916
      keep_going = 0;
 
917
      for(cdesc = ps_global->index_disp_format;
 
918
          cdesc->ctype != iNothing && space_left > 0;
 
919
          cdesc++){
 
920
        if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
 
921
          keep_going++;
 
922
          cdesc->width++;
 
923
          space_left--;
 
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)){
 
930
              cdesc->width++;
 
931
              space_left--;
 
932
          }
 
933
        }
 
934
      }
 
935
    }
 
936
 
 
937
    /* if still more, pad out percent's */
 
938
    keep_going = 1;
 
939
    while(space_left > 0 && keep_going){
 
940
      keep_going = 0;
 
941
      for(cdesc = ps_global->index_disp_format;
 
942
          cdesc->ctype != iNothing && space_left > 0;
 
943
          cdesc++){
 
944
        if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
 
945
          keep_going++;
 
946
          cdesc->width++;
 
947
          space_left--;
 
948
        }
 
949
      }
 
950
    }
 
951
 
 
952
    /* if user made Fixed fields too big, give back space */
 
953
    keep_going = 1;
 
954
    while(space_left < 0 && keep_going){
 
955
      keep_going = 0;
 
956
      for(cdesc = ps_global->index_disp_format;
 
957
          cdesc->ctype != iNothing && space_left < 0;
 
958
          cdesc++){
 
959
        if(cdesc->wtype == Fixed && cdesc->width > 0){
 
960
          keep_going++;
 
961
          cdesc->width--;
 
962
          space_left++;
 
963
        }
 
964
      }
 
965
    }
 
966
 
 
967
    if(pith_opt_save_index_state)
 
968
      (*pith_opt_save_index_state)(FALSE);
 
969
}
 
970
 
 
971
 
 
972
void
 
973
setup_thread_header_widths(MAILSTREAM *stream)
 
974
{
 
975
    clear_icache_flags(stream);
 
976
    if(pith_opt_save_index_state)
 
977
      (*pith_opt_save_index_state)(TRUE);
 
978
}
 
979
 
 
980
 
 
981
/*
 
982
 * load_overview - c-client call back to gather overview data
 
983
 *
 
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
 
987
 */
 
988
void
 
989
load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
 
990
{
 
991
    if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
 
992
        INDEXDATA_S  idata;
 
993
        ICE_S       *ice;
 
994
 
 
995
        memset(&idata, 0, sizeof(INDEXDATA_S));
 
996
        idata.no_fetch = 1;
 
997
 
 
998
        /*
 
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
 
1002
         * in a single RTT.
 
1003
         */
 
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;
 
1011
 
 
1012
        ice = (*format_index_line)(&idata);
 
1013
        if(idata.bogus && ice){
 
1014
            if(THRD_INDX()){
 
1015
                if(ice->tice)
 
1016
                  clear_ice(&ice->tice);
 
1017
            }
 
1018
            else
 
1019
              clear_ice(&ice);
 
1020
        }
 
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);
 
1026
        }
 
1027
    }
 
1028
}
 
1029
 
 
1030
 
 
1031
ICE_S *
 
1032
build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
 
1033
                  long int msgno, long int top_msgno, int msgcount, int *fetched)
 
1034
{
 
1035
    ICE_S        *ice;
 
1036
    MESSAGECACHE *mc;
 
1037
    long          n, i, cnt, rawno, visible, limit = -1L;
 
1038
 
 
1039
    rawno = mn_m2raw(msgmap, msgno);
 
1040
 
 
1041
    /* cache hit? */
 
1042
    if(THRD_INDX()){
 
1043
        ice = fetch_ice(stream, rawno);
 
1044
        if(ice->tice && ice->tice->ifield
 
1045
           && ice->tice->color_lookup_done && ice->tice->widths_done){
 
1046
#ifdef DEBUG
 
1047
            char buf[MAX_SCREEN_COLS+1];
 
1048
            simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice->tice, msgno);
 
1049
#endif
 
1050
            dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
 
1051
                       ice->tice,
 
1052
                       buf[0] ? buf : "?",
 
1053
                       buf[0] ? strlen(buf) : 0));
 
1054
            return(ice);
 
1055
        }
 
1056
    }
 
1057
    else{
 
1058
        if((ice = fetch_ice(stream, rawno))->ifield
 
1059
           && ice->color_lookup_done && ice->widths_done){
 
1060
#ifdef DEBUG
 
1061
            char buf[MAX_SCREEN_COLS+1];
 
1062
            simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice, msgno);
 
1063
#endif
 
1064
            dprint((9, "Hit: Returning %p -> <%s (%d)\n",
 
1065
                       ice,
 
1066
                       buf[0] ? buf : "?",
 
1067
                       buf[0] ? strlen(buf) : 0));
 
1068
            return(ice);
 
1069
        }
 
1070
    }
 
1071
 
 
1072
    /*
 
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.
 
1076
     */
 
1077
    if(THRD_INDX() && ice && ice->tice && ice->tice->ifield
 
1078
       && !ice->tice->widths_done){
 
1079
        clear_ice(&ice->tice);
 
1080
    }
 
1081
 
 
1082
    /*
 
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
 
1090
     * of prefetch...
 
1091
     */
 
1092
    if(!(fetched && *fetched) && index_in_overview(stream)
 
1093
       && ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
 
1094
           || (!THRD_INDX() && !ice->ifield))){
 
1095
        char         *seq, *p;
 
1096
        long          next;
 
1097
        int           count;
 
1098
        MESSAGECACHE *mc;
 
1099
        PINETHRD_S   *thrd;
 
1100
 
 
1101
        if(fetched)
 
1102
          (*fetched)++;
 
1103
 
 
1104
        /* clear sequence bits */
 
1105
        for(n = 1L; n <= stream->nmsgs; n++)
 
1106
          if((mc = mail_elt(stream, n)) != NULL)
 
1107
            mc->sequence = 0;
 
1108
 
 
1109
        /*
 
1110
         * Light interesting bits
 
1111
         * NOTE: not set above because m2raw's cheaper
 
1112
         * than raw2m for every message
 
1113
         */
 
1114
 
 
1115
        /*
 
1116
         * Unfortunately, it is expensive to calculate visible pages
 
1117
         * in thread index if we are zoomed, so we don't try.
 
1118
         */
 
1119
        if(THRD_INDX() && any_lflagged(msgmap, MN_HIDE))
 
1120
          visible = msgmap->visible_threads;
 
1121
        else if(THREADING() && sp_viewing_a_thread(stream)){
 
1122
            /*
 
1123
             * We know that all visible messages in the thread are marked
 
1124
             * with MN_CHID2.
 
1125
             */
 
1126
            for(visible = 0L, n = top_msgno;
 
1127
                visible < msgcount && n <= msgcount;
 
1128
                n++){
 
1129
 
 
1130
                if(!get_lflag(stream, msgmap, n, MN_CHID2))
 
1131
                  break;
 
1132
                
 
1133
                if(!msgline_hidden(stream, msgmap, n, 0))
 
1134
                  visible++;
 
1135
            }
 
1136
            
 
1137
        }
 
1138
        else
 
1139
          visible = mn_get_total(msgmap)
 
1140
                      - any_lflagged(msgmap, MN_HIDE|MN_CHID);
 
1141
 
 
1142
        limit = MIN(visible, msgcount);
 
1143
 
 
1144
        if(THRD_INDX()){
 
1145
            ICE_S *ic;
 
1146
 
 
1147
            thrd = fetch_thread(stream, mn_m2raw(msgmap, top_msgno));
 
1148
            /*
 
1149
             * Loop through visible threads, marking them for fetching.
 
1150
             * Stop at end of screen or sooner if we run out of visible
 
1151
             * threads.
 
1152
             */
 
1153
            count = i = 0;
 
1154
            while(thrd){
 
1155
                n = mn_raw2m(msgmap, thrd->rawno);
 
1156
                if(n >= msgno
 
1157
                   && n <= mn_get_total(msgmap)
 
1158
                   && !((ic=fetch_ice(stream,thrd->rawno)->tice)
 
1159
                   && ic->ifield)){
 
1160
                    count += mark_msgs_in_thread(stream, thrd, msgmap);
 
1161
                }
 
1162
 
 
1163
                if(++i >= limit)
 
1164
                  break;
 
1165
 
 
1166
                /* find next thread which is visible */
 
1167
                do{
 
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);
 
1172
                    else
 
1173
                      thrd = NULL;
 
1174
                } while(thrd
 
1175
                        && msgline_hidden(stream, msgmap,
 
1176
                                          mn_raw2m(msgmap, thrd->rawno), 0));
 
1177
            }
 
1178
        }
 
1179
        else{
 
1180
            count = i = 0;
 
1181
            n = top_msgno;
 
1182
            while(1){
 
1183
                if(n >= msgno
 
1184
                   && n <= mn_get_total(msgmap)
 
1185
                   && !fetch_ice(stream, (rawno=mn_m2raw(msgmap,n)))->ifield){
 
1186
                    if(thrd = fetch_thread(stream, rawno)){
 
1187
                        /*
 
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
 
1191
                         * in that case.
 
1192
                         */
 
1193
                        if(THREADING()
 
1194
                           && ps_global->thread_disp_style == THREAD_MUTTLIKE
 
1195
                           && thrd->top)
 
1196
                          thrd = fetch_thread(stream, thrd->top);
 
1197
 
 
1198
                        count += mark_msgs_in_thread(stream, thrd, msgmap);
 
1199
                    }
 
1200
                    else if(rawno > 0L && rawno <= stream->nmsgs
 
1201
                            && (mc = mail_elt(stream,rawno))
 
1202
                            && !mc->private.msg.env){
 
1203
                        mc->sequence = 1;
 
1204
                        count++;
 
1205
                    }
 
1206
                }
 
1207
 
 
1208
                if(++i >= limit)
 
1209
                  break;
 
1210
 
 
1211
                /* find next n which is visible */
 
1212
                while(++n <=  mn_get_total(msgmap)
 
1213
                      && msgline_hidden(stream, msgmap, n, 0))
 
1214
                  ;
 
1215
            }
 
1216
        }
 
1217
 
 
1218
        if(count){
 
1219
            seq = build_sequence(stream, NULL, NULL);
 
1220
            if(seq){
 
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);
 
1228
            }
 
1229
        }
 
1230
 
 
1231
        /*
 
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 ...
 
1235
         */
 
1236
        rawno = mn_m2raw(msgmap, msgno);
 
1237
        ice = fetch_ice(stream, rawno);
 
1238
    }
 
1239
 
 
1240
    if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
 
1241
       || (!THRD_INDX() && !ice->ifield)){
 
1242
        INDEXDATA_S idata;
 
1243
 
 
1244
        /*
 
1245
         * With pre-fetching/callback-formatting done and no success,
 
1246
         * fall into formatting the requested line...
 
1247
         */
 
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));
 
1256
        }
 
1257
        else
 
1258
          idata.bogus = 2;
 
1259
 
 
1260
        ice = (*format_index_line)(&idata);
 
1261
    }
 
1262
 
 
1263
    /*
 
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.
 
1270
     */
 
1271
    if((!THRD_INDX() && ice->ifield && !ice->widths_done)){
 
1272
        ICE_S       *working_ice;
 
1273
        IFIELD_S    *ifield;
 
1274
        IELEM_S     *ielem;
 
1275
        int          width;
 
1276
        INDEX_COL_S *cdesc;
 
1277
 
 
1278
        if(need_format_setup(stream))
 
1279
          setup_header_widths(stream);
 
1280
 
 
1281
        if(THRD_INDX())
 
1282
          working_ice = ice ? ice->tice : NULL;
 
1283
        else
 
1284
          working_ice = ice;
 
1285
 
 
1286
        if(working_ice){
 
1287
          /*
 
1288
           * First fix the ifield widths. The cdescs with nonzero widths
 
1289
           * should correspond to the ifields that are defined.
 
1290
           */
 
1291
          ifield = working_ice->ifield;
 
1292
          for(cdesc = ps_global->index_disp_format;
 
1293
              cdesc->ctype != iNothing && ifield; cdesc++){
 
1294
              if(cdesc->width){
 
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));
 
1297
                    assert(0);
 
1298
                  }
 
1299
 
 
1300
                  ifield->width = cdesc->width;
 
1301
                  ifield = ifield->next;
 
1302
              }
 
1303
          }
 
1304
 
 
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);
 
1308
        }
 
1309
 
 
1310
        working_ice->widths_done = 1;
 
1311
    }
 
1312
 
 
1313
    if(THRD_INDX() && ice->tice)
 
1314
      ice->tice->color_lookup_done = 1;
 
1315
 
 
1316
    /*
 
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
 
1321
     * search.
 
1322
     */
 
1323
    if(!THRD_INDX() && !ice->color_lookup_done){
 
1324
        COLOR_PAIR *linecolor;
 
1325
        SEARCHSET  *ss, *s;
 
1326
        ICE_S      *ic;
 
1327
        PAT_STATE  *pstate = NULL;
 
1328
 
 
1329
        if(pico_usingcolor()){
 
1330
            if(limit < 0L){
 
1331
                if(THREADING() && sp_viewing_a_thread(stream)){
 
1332
                    for(visible = 0L, n = top_msgno;
 
1333
                        visible < msgcount && n <= mn_get_total(msgmap);
 
1334
                        n++){
 
1335
 
 
1336
                        if(!get_lflag(stream, msgmap, n, MN_CHID2))
 
1337
                          break;
 
1338
                        
 
1339
                        if(!msgline_hidden(stream, msgmap, n, 0))
 
1340
                          visible++;
 
1341
                    }
 
1342
                    
 
1343
                }
 
1344
                else
 
1345
                  visible = mn_get_total(msgmap)
 
1346
                              - any_lflagged(msgmap, MN_HIDE|MN_CHID);
 
1347
 
 
1348
                limit = MIN(visible, msgcount);
 
1349
            }
 
1350
            /* clear sequence bits */
 
1351
            for(n = 1L; n <= stream->nmsgs; n++)
 
1352
              if((mc = mail_elt(stream, n)) != NULL)
 
1353
                mc->sequence = 0;
 
1354
 
 
1355
            cnt = i = 0;
 
1356
            n = top_msgno;
 
1357
            while(1){
 
1358
                if(n >= msgno
 
1359
                   && n <= mn_get_total(msgmap)
 
1360
                   && !fetch_ice(stream,(rawno = mn_m2raw(msgmap, n)))->color_lookup_done){
 
1361
 
 
1362
                    if(rawno >= 1L && rawno <= stream->nmsgs
 
1363
                       && (mc = mail_elt(stream, rawno))){
 
1364
                        mc->sequence = 1;
 
1365
                        cnt++;
 
1366
                    }
 
1367
                }
 
1368
 
 
1369
                if(++i >= limit)
 
1370
                  break;
 
1371
 
 
1372
                /* find next n which is visible */
 
1373
                while(++n <=  mn_get_total(msgmap)
 
1374
                      && msgline_hidden(stream, msgmap, n, 0))
 
1375
                  ;
 
1376
            }
 
1377
 
 
1378
            /*
 
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.
 
1388
             */
 
1389
            while(cnt > 0L){
 
1390
                ss = build_searchset(stream);
 
1391
                if(ss){
 
1392
                    int colormatch;
 
1393
 
 
1394
                    linecolor = NULL;
 
1395
                    colormatch = get_index_line_color(stream, ss, &pstate,
 
1396
                                                      &linecolor);
 
1397
 
 
1398
                    /*
 
1399
                     * Assign this color to all matched msgno's and
 
1400
                     * turn off the sequence bit so we won't check
 
1401
                     * for them again.
 
1402
                     */
 
1403
                    if(colormatch){
 
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))
 
1408
                               && mc->searched){
 
1409
                                cnt--;
 
1410
                                mc->sequence = 0;
 
1411
                                ic = fetch_ice(stream, n);
 
1412
                                ic->color_lookup_done = 1;
 
1413
                                if(linecolor)
 
1414
                                  ic->linecolor = new_color_pair(linecolor->fg,
 
1415
                                                                 linecolor->bg);
 
1416
                            }
 
1417
                          }
 
1418
                        }
 
1419
 
 
1420
                        if(linecolor)
 
1421
                          free_color_pair(&linecolor);
 
1422
                    }
 
1423
                    else{
 
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))
 
1429
                               && mc->sequence){
 
1430
                                cnt--;
 
1431
                                ic = fetch_ice(stream, n);
 
1432
                                ic->color_lookup_done = 1;
 
1433
                            }
 
1434
                          }
 
1435
                        }
 
1436
 
 
1437
                        /* just making sure */
 
1438
                        cnt = 0L;
 
1439
                    }
 
1440
 
 
1441
                    mail_free_searchset(&ss);
 
1442
                }
 
1443
                else
 
1444
                  cnt = 0L;
 
1445
            }
 
1446
 
 
1447
            ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
 
1448
        }
 
1449
        else
 
1450
          ice->color_lookup_done = 1;
 
1451
    }
 
1452
 
 
1453
    return(ice);                /* Return formatted index data */
 
1454
}
 
1455
 
 
1456
 
 
1457
int
 
1458
day_of_week(struct date *d)
 
1459
{
 
1460
    int m, y;
 
1461
 
 
1462
    m = d->month;
 
1463
    y = d->year;
 
1464
    if(m <= 2){
 
1465
        m += 9;
 
1466
        y--;
 
1467
    }
 
1468
    else
 
1469
      m -= 3;   /* March is month 0 */
 
1470
    
 
1471
    return((d->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100))%7);
 
1472
}
 
1473
 
 
1474
 
 
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}
 
1478
};
 
1479
 
 
1480
static char *day_name[] = {N_("Sunday"),N_("Monday"),N_("Tuesday"),N_("Wednesday"),
 
1481
                           N_("Thursday"),N_("Friday"),N_("Saturday")};
 
1482
 
 
1483
int
 
1484
day_of_year(struct date *d)
 
1485
{
 
1486
    int i, leap, doy;
 
1487
 
 
1488
    if(d->year <= 0 || d->month < 1 || d->month > 12)
 
1489
      return(-1);
 
1490
 
 
1491
    doy = d->day;
 
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];
 
1495
    
 
1496
    return(doy);
 
1497
}
 
1498
 
 
1499
 
 
1500
 
 
1501
/*----------------------------------------------------------------------
 
1502
   Format a string summarizing the message header for index on screen
 
1503
 
 
1504
   Args: buffer -- buffer to place formatted line
 
1505
         idata -- snot it takes to format the line
 
1506
 
 
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)
 
1510
 ----*/
 
1511
ICE_S *
 
1512
format_index_index_line(INDEXDATA_S *idata)
 
1513
{
 
1514
    char          str[BIGWIDTH+1], to_us, status, *field,
 
1515
                 *buffer, *s_tmp, *p, *newsgroups;
 
1516
    int           i, j, smallest, collapsed = 0,
 
1517
                  noff = 0;
 
1518
    long          l, score;
 
1519
    BODY         *body = NULL;
 
1520
    MESSAGECACHE *mc;
 
1521
    ADDRESS      *addr, *toaddr, *ccaddr, *last_to;
 
1522
    PINETHRD_S   *thrd = NULL;
 
1523
    INDEX_COL_S  *cdesc = NULL;
 
1524
    ICE_S        *ice;
 
1525
    IFIELD_S     *ifield;
 
1526
    IELEM_S      *ielem;
 
1527
    struct variable *vars = ps_global->vars;
 
1528
 
 
1529
    dprint((8, "=== format_index_line(%ld,%ld) ===\n",
 
1530
               idata ? idata->msgno : -1, idata ? idata->rawno : -1));
 
1531
 
 
1532
 
 
1533
    ice = fetch_ice(idata->stream, idata->rawno);
 
1534
    if(ice->charset)
 
1535
      fs_give((void **) &ice->charset);
 
1536
 
 
1537
    free_ifield(&ice->ifield);
 
1538
 
 
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);
 
1545
    }
 
1546
 
 
1547
    /* calculate contents of the required fields */
 
1548
    for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
 
1549
      if(cdesc->width){
 
1550
          memset(str, 0, sizeof(str));
 
1551
          ifield        = new_ifield(&ice->ifield);
 
1552
          ifield->ctype = cdesc->ctype;
 
1553
          ifield->width = cdesc->width;
 
1554
 
 
1555
          if(idata->bogus){
 
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 ]"));
 
1565
          }
 
1566
          else
 
1567
            switch(cdesc->ctype){
 
1568
              case iStatus:
 
1569
                to_us = status = ' ';
 
1570
                if(collapsed){
 
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,
 
1574
                                                      cdesc->ctype);
 
1575
                }
 
1576
                else{
 
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)){
 
1582
                              ice->to_us = 1;
 
1583
                              if(to_us == ' ')
 
1584
                                to_us = '+';
 
1585
 
 
1586
                              break;
 
1587
                          }
 
1588
 
 
1589
                        if(to_us != '+' && resent_to_us(idata)){
 
1590
                            ice->to_us = 1;
 
1591
                            if(to_us == ' ')
 
1592
                              to_us = '+';
 
1593
                        }
 
1594
 
 
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)){
 
1598
                                ice->cc_us = 1;
 
1599
                                to_us = '-';
 
1600
                                break;
 
1601
                            }
 
1602
                    }
 
1603
 
 
1604
                    status = (!idata->stream || !IS_NEWS(idata->stream)
 
1605
                              || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
 
1606
                               ? 'N' : ' ';
 
1607
 
 
1608
                     if(mc->seen)
 
1609
                       status = ' ';
 
1610
 
 
1611
                     if(mc->answered)
 
1612
                       status = 'A';
 
1613
 
 
1614
                     if(mc->deleted)
 
1615
                       status = 'D';
 
1616
                }
 
1617
 
 
1618
                snprintf(str, sizeof(str), "%c %c", to_us, status);
 
1619
 
 
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';
 
1627
                    ielem->datalen = 1;
 
1628
                    set_print_format(ielem, 1, ifield->leftadj);
 
1629
                }
 
1630
 
 
1631
                if(pico_usingcolor()){
 
1632
 
 
1633
                    if(str[0] == '*'){
 
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);
 
1638
                        }
 
1639
                    }
 
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);
 
1645
                        }
 
1646
                    }
 
1647
 
 
1648
                    if(str[2] == 'D'){
 
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);
 
1653
                        }
 
1654
                    }
 
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);
 
1660
                        }
 
1661
                    }
 
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);
 
1667
                        }
 
1668
                    }
 
1669
                }
 
1670
 
 
1671
                break;
 
1672
 
 
1673
              case iFStatus:
 
1674
              case iIStatus:
 
1675
              {
 
1676
                  char new, answered, deleted, flagged;
 
1677
 
 
1678
                  if(collapsed){
 
1679
                      thrd = fetch_thread(idata->stream, idata->rawno);
 
1680
                      to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
 
1681
                  }
 
1682
                  else{
 
1683
                      to_us = ' ';
 
1684
                      if(!IS_NEWS(idata->stream)){
 
1685
                        for(addr = fetch_to(idata); addr; addr = addr->next)
 
1686
                          if(address_is_us(addr, ps_global)){
 
1687
                              to_us = '+';
 
1688
                              break;
 
1689
                          }
 
1690
                      
 
1691
                        if(to_us == ' ' && resent_to_us(idata))
 
1692
                          to_us = '+';
 
1693
 
 
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)){
 
1697
                                to_us = '-';
 
1698
                                break;
 
1699
                            }
 
1700
                      }
 
1701
                  }
 
1702
 
 
1703
                  new = answered = deleted = flagged = ' ';
 
1704
 
 
1705
                  if(collapsed){
 
1706
                      unsigned long save_branch, cnt, tot_in_thrd;
 
1707
 
 
1708
                      /*
 
1709
                       * Branch is a sibling, not part of the thread, so
 
1710
                       * don't consider it when displaying this line.
 
1711
                       */
 
1712
                      save_branch = thrd->branch;
 
1713
                      thrd->branch = 0L;
 
1714
 
 
1715
                      tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
 
1716
                                                          F_NONE);
 
1717
 
 
1718
                      cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
 
1719
                      if(cnt)
 
1720
                        deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
 
1721
 
 
1722
                      cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
 
1723
                      if(cnt)
 
1724
                        answered = (cnt == tot_in_thrd) ? 'A' : 'a';
 
1725
 
 
1726
                      /* no lower case *, same thing for some or all */
 
1727
                      if(count_flags_in_thread(idata->stream, thrd, F_FLAG))
 
1728
                        flagged = '*';
 
1729
 
 
1730
                      new = status_symbol_for_thread(idata->stream, thrd,
 
1731
                                                     cdesc->ctype);
 
1732
 
 
1733
                      thrd->branch = save_branch;
 
1734
                  }
 
1735
                  else{
 
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){
 
1741
                              if(mc->recent)
 
1742
                                new = mc->seen ? 'R' : 'N';
 
1743
                              else if (!mc->seen)
 
1744
                                new = 'U';
 
1745
                          }
 
1746
                          else if(!mc->seen
 
1747
                                  && (!IS_NEWS(idata->stream)
 
1748
                                      || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
 
1749
                            new = 'N';
 
1750
 
 
1751
                          if(mc->answered)
 
1752
                            answered = 'A';
 
1753
 
 
1754
                          if(mc->deleted)
 
1755
                            deleted = 'D';
 
1756
 
 
1757
                          if(mc->flagged)
 
1758
                            flagged = '*';
 
1759
                      }
 
1760
                  }
 
1761
 
 
1762
                  
 
1763
                  snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
 
1764
                          answered, deleted);
 
1765
 
 
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';
 
1773
                    ielem->datalen = 1;
 
1774
                    set_print_format(ielem, 1, ifield->leftadj);
 
1775
                  }
 
1776
 
 
1777
                  if(pico_usingcolor()){
 
1778
 
 
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);
 
1785
                          }
 
1786
                      }
 
1787
 
 
1788
                      if(str[2] == '*'){
 
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);
 
1793
                          }
 
1794
                      }
 
1795
 
 
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);
 
1801
                          }
 
1802
                      }
 
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);
 
1808
                          }
 
1809
                      }
 
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);
 
1815
                          }
 
1816
                      }
 
1817
 
 
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);
 
1823
                          }
 
1824
                      }
 
1825
 
 
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);
 
1831
                          }
 
1832
                      }
 
1833
                  }
 
1834
              }
 
1835
 
 
1836
              break;
 
1837
 
 
1838
              case iMessNo:
 
1839
                /*
 
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.
 
1844
                 */
 
1845
                snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
 
1846
                break;
 
1847
 
 
1848
              case iArrow:
 
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);
 
1860
                }
 
1861
 
 
1862
                break;
 
1863
 
 
1864
              case iScore:
 
1865
                score = get_msg_score(idata->stream, idata->rawno);
 
1866
                if(score == SCORE_UNDEF){
 
1867
                    SEARCHSET *ss = NULL;
 
1868
 
 
1869
                    ss = mail_newsearchset();
 
1870
                    ss->first = ss->last = (unsigned long) idata->rawno;
 
1871
                    if(ss){
 
1872
                        /*
 
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
 
1884
                         * parameter.)
 
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.
 
1894
                         */
 
1895
                        idata->bogus =
 
1896
                            (calculate_some_scores(idata->stream,
 
1897
                                                   ss, idata->no_fetch) == 0)
 
1898
                                        ? 1 : 0;
 
1899
                        score = get_msg_score(idata->stream, idata->rawno);
 
1900
                        mail_free_searchset(&ss);
 
1901
                    }
 
1902
                }
 
1903
 
 
1904
                snprintf(str, sizeof(str), "%ld", score != SCORE_UNDEF ? score : 0L);
 
1905
                break;
 
1906
 
 
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:
 
1913
              case iSDateTime:
 
1914
              case iSDateTimeIsoS: case iSDateTimeIso:
 
1915
              case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
 
1916
              case iSDateTime24:
 
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));
 
1924
                break;
 
1925
 
 
1926
              case iFromTo:
 
1927
              case iFromToNotNews:
 
1928
              case iFrom:
 
1929
              case iAddress:
 
1930
              case iMailbox:
 
1931
                from_str(cdesc->ctype, idata, BIGWIDTH, str, ice);
 
1932
                break;
 
1933
 
 
1934
              case iTo:
 
1935
                if(((field = ((addr = fetch_to(idata))
 
1936
                              ? "To"
 
1937
                              : (addr = fetch_cc(idata))
 
1938
                              ? "Cc"
 
1939
                              : NULL))
 
1940
                    && !set_index_addr(idata, field, addr, NULL,
 
1941
                                       BIGWIDTH, str, &ice->charset))
 
1942
                   || !field)
 
1943
                  if(newsgroups = fetch_newsgroups(idata))
 
1944
                    snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
 
1945
 
 
1946
                break;
 
1947
 
 
1948
              case iCc:
 
1949
                set_index_addr(idata, "Cc", fetch_cc(idata),
 
1950
                               NULL, BIGWIDTH, str,
 
1951
                               &ice->charset);
 
1952
                break;
 
1953
 
 
1954
              case iRecips:
 
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)
 
1960
                  ;
 
1961
                 
 
1962
                /* point end of to list temporarily at cc list */
 
1963
                if(last_to)
 
1964
                  last_to->next = ccaddr;
 
1965
 
 
1966
                set_index_addr(idata, "To", toaddr, NULL,
 
1967
                               BIGWIDTH, str, &ice->charset);
 
1968
 
 
1969
                if(last_to)
 
1970
                  last_to->next = NULL;
 
1971
 
 
1972
                break;
 
1973
 
 
1974
              case iSender:
 
1975
                if(addr = fetch_sender(idata))
 
1976
                  set_index_addr(idata, "Sender", addr, NULL,
 
1977
                                 BIGWIDTH, str, &ice->charset);
 
1978
 
 
1979
                break;
 
1980
 
 
1981
              case iInit:
 
1982
                {ADDRESS *addr;
 
1983
 
 
1984
                 if((addr = fetch_from(idata)) && addr->personal){
 
1985
                    char *name, *initials = NULL, *dummy = NULL;
 
1986
 
 
1987
                        
 
1988
                    name = (char *) rfc1522_decode((unsigned char *)tmp_20k_buf,
 
1989
                                                   SIZEOF_20KBUF,
 
1990
                                                   addr->personal, &dummy);
 
1991
                    if(dummy)
 
1992
                      fs_give((void **)&dummy);
 
1993
 
 
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;
 
1998
                    }
 
1999
 
 
2000
                    if(name && *name){
 
2001
                        initials = reply_quote_initials(name);
 
2002
                        snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
 
2003
                    }
 
2004
                 }
 
2005
                }
 
2006
 
 
2007
                break;
 
2008
 
 
2009
              case iSize:
 
2010
                /* 0 ... 9999 */
 
2011
                if((l = fetch_size(idata)) < 10*1000L)
 
2012
                  snprintf(str, sizeof(str), "(%lu)", l);
 
2013
                /* 10K ... 999K */
 
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);
 
2017
                }
 
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)
 
2021
                                                                ? 1L : 0L);
 
2022
                    snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
 
2023
                }
 
2024
                /* 100M ... 2000M */
 
2025
                else if(l <= 2*1000L*1000L*1000L){
 
2026
                    l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
 
2027
                                                                ? 1L : 0L);
 
2028
                    snprintf(str, sizeof(str), "(%luM)", l);
 
2029
                }
 
2030
                else
 
2031
                  snprintf(str, sizeof(str), "(HUGE!)");
 
2032
 
 
2033
                break;
 
2034
 
 
2035
              case iSizeComma:
 
2036
                /* 0 ... 99,999 */
 
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));
 
2043
                }
 
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)
 
2047
                                                                ? 1L : 0L);
 
2048
                    snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
 
2049
                }
 
2050
                /* 1,000M ... 2,000M */
 
2051
                else if(l <= 2*1000L*1000L*1000L){
 
2052
                    l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
 
2053
                                                                ? 1L : 0L);
 
2054
                    snprintf(str, sizeof(str), "(%sM)", comatose(l));
 
2055
                }
 
2056
                else
 
2057
                  snprintf(str, sizeof(str), "(HUGE!)");
 
2058
 
 
2059
                break;
 
2060
 
 
2061
              case iSizeNarrow:
 
2062
                /* 0 ... 999 */
 
2063
                if((l = fetch_size(idata)) < 1000L)
 
2064
                  snprintf(str, sizeof(str), "(%lu)", l);
 
2065
                /* 1K ... 99K */
 
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);
 
2069
                }
 
2070
                /* .1M ... .9M */
 
2071
                else if(l < 1000L*1000L - 100L*1000L/2){
 
2072
                    l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
 
2073
                                                                ? 1L : 0L);
 
2074
                    snprintf(str, sizeof(str), "(.%luM)", l);
 
2075
                }
 
2076
                /* 1M ... 99M */
 
2077
                else if(l < 1000L*100L*1000L - 1000L*1000L/2){
 
2078
                    l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
 
2079
                                                                ? 1L : 0L);
 
2080
                    snprintf(str, sizeof(str), "(%luM)", l);
 
2081
                }
 
2082
                /* .1G ... .9G */
 
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);
 
2087
                }
 
2088
                /* 1G ... 2G */
 
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);
 
2093
                }
 
2094
                else
 
2095
                  snprintf(str, sizeof(str), "(HUGE!)");
 
2096
 
 
2097
                break;
 
2098
 
 
2099
              /* From Carl Jacobsen <carl@ucsd.edu> */
 
2100
              case iKSize:
 
2101
                l = fetch_size(idata);
 
2102
                l = (l / 1024L) + (l % 1024L != 0 ? 1 : 0);
 
2103
 
 
2104
                if(l < 1024L) {                         /* 0k .. 1023k */
 
2105
                  snprintf(str, sizeof(str), "(%luk)", l);
 
2106
 
 
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 & . */
 
2111
                  }
 
2112
                } else if (l <= 2L * 1024L * 1024L) {   /* 100M .. 2048 */
 
2113
                  snprintf(str, sizeof(str), "(%luM)", l / 1024L);
 
2114
                } else {
 
2115
                  snprintf(str, sizeof(str), "(HUGE!)");
 
2116
                }
 
2117
 
 
2118
                break;
 
2119
 
 
2120
              case iDescripSize:
 
2121
                if(body = fetch_body(idata))
 
2122
                  switch(body->type){
 
2123
                    case TYPETEXT:
 
2124
                    {
 
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   )");
 
2134
                        else
 
2135
                          snprintf(str, sizeof(str), "(huge   )");
 
2136
                    }
 
2137
 
 
2138
                    break;
 
2139
 
 
2140
                    case TYPEMULTIPART:
 
2141
                      if(strucmp(body->subtype, "MIXED") == 0){
 
2142
                          int x;
 
2143
 
 
2144
                          x = body->nested.part
 
2145
                            ? body->nested.part->body.type
 
2146
                            : TYPETEXT + 1000;
 
2147
                          switch(x){
 
2148
                            case TYPETEXT:
 
2149
                              if(body->nested.part->body.size.bytes < 6000)
 
2150
                                snprintf(str, sizeof(str), "(short+ )");
 
2151
                              else if(body->nested.part->body.size.bytes
 
2152
                                      < 25000)
 
2153
                                snprintf(str, sizeof(str), "(medium+)");
 
2154
                              else if(body->nested.part->body.size.bytes
 
2155
                                      < 100000)
 
2156
                                snprintf(str, sizeof(str), "(long+  )");
 
2157
                              else
 
2158
                                snprintf(str, sizeof(str), "(huge+  )");
 
2159
                              break;
 
2160
 
 
2161
                            default:
 
2162
                              snprintf(str, sizeof(str), "(multi  )");
 
2163
                              break;
 
2164
                          }
 
2165
                      }
 
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)");
 
2172
                      else
 
2173
                        snprintf(str, sizeof(str), "(multi  )");
 
2174
 
 
2175
                      break;
 
2176
 
 
2177
                    case TYPEMESSAGE:
 
2178
                      snprintf(str, sizeof(str), "(message)");
 
2179
                      break;
 
2180
 
 
2181
                    case TYPEAPPLICATION:
 
2182
                      snprintf(str, sizeof(str), "(applica)");
 
2183
                      break;
 
2184
 
 
2185
                    case TYPEAUDIO:
 
2186
                      snprintf(str, sizeof(str), "(audio  )");
 
2187
                      break;
 
2188
 
 
2189
                    case TYPEIMAGE:
 
2190
                      snprintf(str, sizeof(str), "(image  )");
 
2191
                      break;
 
2192
 
 
2193
                    case TYPEVIDEO:
 
2194
                      snprintf(str, sizeof(str), "(video  )");
 
2195
                      break;
 
2196
 
 
2197
                    default:
 
2198
                      snprintf(str, sizeof(str), "(other  )");
 
2199
                      break;
 
2200
                  }
 
2201
 
 
2202
                break;
 
2203
 
 
2204
              case iAtt:
 
2205
                str[0] = SPACE;
 
2206
                str[1] = '\0';
 
2207
                if((body = fetch_body(idata)) &&
 
2208
                   body->type == TYPEMULTIPART &&
 
2209
                   strucmp(body->subtype, "ALTERNATIVE") != 0){
 
2210
                    PART *part;
 
2211
                    int   atts = 0;
 
2212
 
 
2213
                    part = body->nested.part;  /* 1st part, don't count */
 
2214
                    while(part && part->next && atts < 10){
 
2215
                        atts++;
 
2216
                        part = part->next;
 
2217
                    }
 
2218
 
 
2219
                    if(atts > 9)
 
2220
                      str[0] = '*';
 
2221
                    else if(atts > 0)
 
2222
                      str[0] = '0' + atts;
 
2223
                }
 
2224
 
 
2225
                break;
 
2226
 
 
2227
              case iSubject:
 
2228
                subj_str(idata, BIGWIDTH, str, NoKW, 0, ice);
 
2229
                break;
 
2230
 
 
2231
              case iSubjectText:
 
2232
                subj_str(idata, BIGWIDTH, str, NoKW, 1, ice);
 
2233
                break;
 
2234
 
 
2235
              case iSubjKey:
 
2236
                subj_str(idata, BIGWIDTH, str, KW, 0, ice);
 
2237
                break;
 
2238
 
 
2239
              case iSubjKeyText:
 
2240
                subj_str(idata, BIGWIDTH, str, KW, 1, ice);
 
2241
                break;
 
2242
 
 
2243
              case iSubjKeyInit:
 
2244
                subj_str(idata, BIGWIDTH, str, KWInit, 0, ice);
 
2245
                break;
 
2246
 
 
2247
              case iSubjKeyInitText:
 
2248
                subj_str(idata, BIGWIDTH, str, KWInit, 1, ice);
 
2249
                break;
 
2250
 
 
2251
              case iKey:
 
2252
                key_str(idata, KW, ice);
 
2253
                break;
 
2254
 
 
2255
              case iKeyInit:
 
2256
                key_str(idata, KWInit, ice);
 
2257
                break;
 
2258
 
 
2259
              case iNews:
 
2260
                if(newsgroups = fetch_newsgroups(idata)){
 
2261
                    strncpy(str, newsgroups, BIGWIDTH);
 
2262
                    str[BIGWIDTH] = '\0';
 
2263
                }
 
2264
 
 
2265
                break;
 
2266
 
 
2267
              case iNewsAndTo:
 
2268
                if(newsgroups = fetch_newsgroups(idata))
 
2269
                  strncpy(str, newsgroups, sizeof(str));
 
2270
 
 
2271
                if((l = strlen(str)) < sizeof(str)){
 
2272
                    if(sizeof(str) - l < 6)
 
2273
                      strncpy(str+l, "...", sizeof(str)-l);
 
2274
                    else{
 
2275
                        if(l > 0){
 
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,
 
2279
                                           &ice->charset);
 
2280
                            if(!str[l+5])
 
2281
                              str[l] = '\0';
 
2282
                        }
 
2283
                        else
 
2284
                          set_index_addr(idata, "To", fetch_to(idata),
 
2285
                                         NULL, BIGWIDTH, str,
 
2286
                                         &ice->charset);
 
2287
                    }
 
2288
                }
 
2289
 
 
2290
                break;
 
2291
 
 
2292
              case iToAndNews:
 
2293
                set_index_addr(idata, "To", fetch_to(idata),
 
2294
                               NULL, BIGWIDTH, str,
 
2295
                               &ice->charset);
 
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);
 
2300
                    else{
 
2301
                        if(l > 0)
 
2302
                          strncpy(str+l, " and ", sizeof(str)-l);
 
2303
 
 
2304
                        if(l > 0)
 
2305
                          strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
 
2306
                        else
 
2307
                          strncpy(str, newsgroups, BIGWIDTH);
 
2308
                    }
 
2309
                }
 
2310
 
 
2311
                break;
 
2312
 
 
2313
              case iNewsAndRecips:
 
2314
                if(newsgroups = fetch_newsgroups(idata))
 
2315
                  strncpy(str, newsgroups, BIGWIDTH);
 
2316
 
 
2317
                if((l = strlen(str)) < BIGWIDTH){
 
2318
                    if(BIGWIDTH - l < 6)
 
2319
                      strncpy(str+l, "...", BIGWIDTH-l);
 
2320
                    else{
 
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)
 
2326
                          ;
 
2327
                         
 
2328
                        /* point end of to list temporarily at cc list */
 
2329
                        if(last_to)
 
2330
                          last_to->next = ccaddr;
 
2331
 
 
2332
                        if(l > 0){
 
2333
                            strncpy(str+l, " and ", sizeof(str)-l);
 
2334
                            set_index_addr(idata, "To", toaddr,
 
2335
                                           NULL, BIGWIDTH-l-5, str+l+5,
 
2336
                                           &ice->charset);
 
2337
                            if(!str[l+5])
 
2338
                              str[l] = '\0';
 
2339
                        }
 
2340
                        else
 
2341
                          set_index_addr(idata, "To", toaddr, NULL,
 
2342
                                         BIGWIDTH, str, &ice->charset);
 
2343
 
 
2344
                        if(last_to)
 
2345
                          last_to->next = NULL;
 
2346
                    }
 
2347
                }
 
2348
 
 
2349
                break;
 
2350
 
 
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)
 
2357
                  ;
 
2358
                 
 
2359
                /* point end of to list temporarily at cc list */
 
2360
                if(last_to)
 
2361
                  last_to->next = ccaddr;
 
2362
 
 
2363
                set_index_addr(idata, "To", toaddr, NULL,
 
2364
                               BIGWIDTH, str, &ice->charset);
 
2365
 
 
2366
                if(last_to)
 
2367
                  last_to->next = NULL;
 
2368
 
 
2369
                if((l = strlen(str)) < BIGWIDTH &&
 
2370
                   (newsgroups = fetch_newsgroups(idata))){
 
2371
                    if(BIGWIDTH - l < 6)
 
2372
                      strncpy(str+l, "...", BIGWIDTH-l);
 
2373
                    else{
 
2374
                        if(l > 0)
 
2375
                          strncpy(str+l, " and ", sizeof(str)-l);
 
2376
 
 
2377
                        if(l > 0)
 
2378
                          strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
 
2379
                        else
 
2380
                          strncpy(str, newsgroups, BIGWIDTH);
 
2381
                    }
 
2382
                }
 
2383
 
 
2384
                break;
 
2385
 
 
2386
            }
 
2387
 
 
2388
          /*
 
2389
           * If the element wasn't already filled in above, do it here.
 
2390
           */
 
2391
          if(!ifield->ielem){
 
2392
              ielem  = new_ielem(&ifield->ielem);
 
2393
 
 
2394
              ielem->freedata = 1;
 
2395
              ielem->data = cpystr(str);
 
2396
              ielem->datalen = strlen(str);
 
2397
 
 
2398
              ifield->leftadj = (cdesc->adjustment == Left) ? 1 : 0;
 
2399
              set_print_format(ielem, ifield->width, ifield->leftadj);
 
2400
          }
 
2401
      }
 
2402
 
 
2403
    ice->widths_done = 1;
 
2404
    ice->id = ice_hash(ice);
 
2405
 
 
2406
    return(ice);
 
2407
}
 
2408
 
 
2409
 
 
2410
ICE_S *
 
2411
format_thread_index_line(INDEXDATA_S *idata)
 
2412
{
 
2413
    char         *p, buffer[BIGWIDTH+1];
 
2414
    int           thdlen, space_left, i;
 
2415
    PINETHRD_S   *thrd = NULL;
 
2416
    ICE_S        *ice, *tice = NULL;
 
2417
    IFIELD_S     *ifield;
 
2418
    IELEM_S      *ielem;
 
2419
 
 
2420
    dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
 
2421
               idata ? idata->msgno : -1, idata ? idata->rawno : -1));
 
2422
 
 
2423
    space_left = ps_global->ttyo->screen_cols;
 
2424
 
 
2425
    if(ps_global->msgmap->max_thrdno < 1000)
 
2426
      thdlen = 3;
 
2427
    else if(ps_global->msgmap->max_thrdno < 10000)
 
2428
      thdlen = 4;
 
2429
    else if(ps_global->msgmap->max_thrdno < 100000)
 
2430
      thdlen = 5;
 
2431
    else
 
2432
      thdlen = 6;
 
2433
 
 
2434
    ice = fetch_ice(idata->stream, idata->rawno);
 
2435
    if(ice && ice->charset)
 
2436
      fs_give((void **) &ice->charset);
 
2437
 
 
2438
    thrd = fetch_thread(idata->stream, idata->rawno);
 
2439
 
 
2440
    if(!thrd || !ice)                   /* can't happen? */
 
2441
      return(ice);
 
2442
    
 
2443
    if(!ice->tice){
 
2444
        tice = (ICE_S *) fs_get(sizeof(*tice));
 
2445
        memset(tice, 0, sizeof(*tice));
 
2446
        ice->tice = tice;
 
2447
    }
 
2448
 
 
2449
    tice = ice->tice;
 
2450
 
 
2451
    if(!tice)
 
2452
      return(ice);
 
2453
 
 
2454
    if(tice->charset)
 
2455
      fs_give((void **) &tice->charset);
 
2456
 
 
2457
    free_ifield(&tice->ifield);
 
2458
 
 
2459
    if(space_left >= 3){
 
2460
        char to_us, status;
 
2461
 
 
2462
        p = buffer;
 
2463
        to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
 
2464
        status = status_symbol_for_thread(idata->stream, thrd, iStatus);
 
2465
 
 
2466
        if((p-buffer)+3 < sizeof(buffer)){
 
2467
            p[0] = to_us;
 
2468
            p[1] = ' ';
 
2469
            p[2] = status;
 
2470
            p[3] = '\0';;
 
2471
        }
 
2472
 
 
2473
        space_left -= 3;
 
2474
 
 
2475
        ifield = new_ifield(&tice->ifield);
 
2476
        ifield->ctype = iStatus;
 
2477
        ifield->width = 3;
 
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';
 
2485
            ielem->datalen = 1;
 
2486
            set_print_format(ielem, 1, ifield->leftadj);
 
2487
        }
 
2488
 
 
2489
        if(pico_usingcolor()){
 
2490
            struct variable *vars = ps_global->vars;
 
2491
 
 
2492
            if(to_us == '*'
 
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);
 
2501
            }
 
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);
 
2508
            }
 
2509
 
 
2510
            if(status == 'D'
 
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);
 
2516
            }
 
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);
 
2523
            }
 
2524
        }
 
2525
    }
 
2526
 
 
2527
    if(space_left >= thdlen+1){
 
2528
        p = buffer;
 
2529
        space_left--;
 
2530
 
 
2531
        snprintf(p, sizeof(buffer), "%*.*s", thdlen, thdlen, "");
 
2532
        space_left -= thdlen;
 
2533
 
 
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);
 
2543
    }
 
2544
 
 
2545
    if(space_left >= 7){
 
2546
 
 
2547
        p = buffer;
 
2548
        space_left--;
 
2549
 
 
2550
        date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer));
 
2551
        if(sizeof(buffer) > 6)
 
2552
          p[6] = '\0';
 
2553
 
 
2554
        if(strlen(p) < 6 && (sizeof(buffer)) > 6){
 
2555
            char *q;
 
2556
 
 
2557
            for(q = p + strlen(p); q < p + 6; q++)
 
2558
              *q = ' ';
 
2559
        }
 
2560
 
 
2561
        space_left -= 6;
 
2562
 
 
2563
        ifield = new_ifield(&tice->ifield);
 
2564
        ifield->ctype = iDate;
 
2565
        ifield->width = 6;
 
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);
 
2572
    }
 
2573
 
 
2574
 
 
2575
    if(space_left > 3){
 
2576
        int   from_width, subj_width, bigthread_adjust;
 
2577
        long  in_thread;
 
2578
        char *subj_start;
 
2579
        char  from[BIGWIDTH+1];
 
2580
        char  tcnt[50];
 
2581
 
 
2582
        space_left--;
 
2583
 
 
2584
        in_thread = count_lflags_in_thread(idata->stream, thrd,
 
2585
                                           ps_global->msgmap, MN_NONE);
 
2586
 
 
2587
        p = buffer;
 
2588
        snprintf(tcnt, sizeof(tcnt), "(%ld)", in_thread);
 
2589
        bigthread_adjust = MAX(0, strlen(tcnt) - 3);
 
2590
        
 
2591
        /* third of the rest */
 
2592
        from_width = MAX((space_left-1)/3 - bigthread_adjust, 1);
 
2593
 
 
2594
        /* the rest */
 
2595
        subj_width = space_left - from_width - 1;
 
2596
 
 
2597
        if(strlen(tcnt) > subj_width)
 
2598
          tcnt[subj_width] = '\0';
 
2599
 
 
2600
        from[0] = '\0';
 
2601
        from_str(iFromTo, idata, BIGWIDTH, from, tice);
 
2602
 
 
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;
 
2612
 
 
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 */
 
2622
 
 
2623
        subj_width -= strlen(tcnt);
 
2624
 
 
2625
        if(subj_width > 0)
 
2626
          subj_width--;
 
2627
 
 
2628
        if(subj_width > 0){
 
2629
            p = buffer;
 
2630
            if(idata->bogus){
 
2631
                if(idata->bogus < 2)
 
2632
                  snprintf(p, sizeof(buffer), "%-.*s", BIGWIDTH,
 
2633
                          _("[ No Message Text Available ]"));
 
2634
            }
 
2635
            else{
 
2636
                *p = '\0';
 
2637
                subj_str(idata, BIGWIDTH, p, NoKW, 0, NULL);
 
2638
            }
 
2639
 
 
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;
 
2649
        }
 
2650
    }
 
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;
 
2662
    }
 
2663
 
 
2664
    tice->widths_done = 1;
 
2665
    tice->id = ice_hash(tice);
 
2666
 
 
2667
    return(ice);
 
2668
}
 
2669
 
 
2670
 
 
2671
/*
 
2672
 * Print the fields of ice in buf with a single space between
 
2673
 * fields. Buf must have size at least n+1.
 
2674
 *
 
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
 
2680
 *
 
2681
 * Returns a pointer to buf.
 
2682
 */
 
2683
char *
 
2684
simple_index_line(char *buf, size_t buflen, int n, ICE_S *ice, long int msgno)
 
2685
{
 
2686
    char     *p, *q;
 
2687
    IFIELD_S *ifield;
 
2688
    IELEM_S  *ielem;
 
2689
 
 
2690
    if(n < 0 || n > 1000)
 
2691
      panic("unreasonable n in simple_index_line()");
 
2692
 
 
2693
    if(!buf)
 
2694
      panic("NULL buf in simple_index_line()");
 
2695
 
 
2696
    if(buflen > 0)
 
2697
      buf[0] = '\0';
 
2698
 
 
2699
    if(buflen > n)
 
2700
      buf[n] = '\0';
 
2701
 
 
2702
    p = buf;
 
2703
 
 
2704
    if(ice){
 
2705
      
 
2706
      for(ifield = ice->ifield; ifield && p-buf < n; ifield = ifield->next){
 
2707
 
 
2708
        /* space between fields */
 
2709
        if(ifield != ice->ifield && (p-buf) < buflen)
 
2710
          *p++ = ' ';
 
2711
 
 
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){
 
2716
            if(msgno > 0L)
 
2717
              snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
 
2718
            else
 
2719
              snprintf(ielem->data, ielem->datalen+1, "%*.*s", ifield->width, ifield->width, "");
 
2720
 
 
2721
            ielem->data[MIN(ifield->width,ielem->datalen)] = '\0';
 
2722
          }
 
2723
        }
 
2724
 
 
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';
 
2730
          p += strlen(p);
 
2731
        }
 
2732
      }
 
2733
    }
 
2734
 
 
2735
    buf[MIN(n,buflen-1)] = '\0';
 
2736
 
 
2737
    return(buf);
 
2738
}
 
2739
 
 
2740
 
 
2741
/*
 
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.
 
2746
 *
 
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.
 
2754
 *
 
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.
 
2758
 */
 
2759
int
 
2760
get_index_line_color(MAILSTREAM *stream, struct search_set *searchset,
 
2761
                     PAT_STATE **pstate, COLOR_PAIR **returned_color)
 
2762
{
 
2763
    PAT_S           *pat = NULL;
 
2764
    long             rflags = ROLE_INCOL;
 
2765
    COLOR_PAIR      *color = NULL;
 
2766
    int              match = 0;
 
2767
    static PAT_STATE localpstate;
 
2768
 
 
2769
    dprint((7, "get_index_line_color\n"));
 
2770
 
 
2771
    if(returned_color)
 
2772
      *returned_color = NULL;
 
2773
 
 
2774
    if(*pstate)
 
2775
      pat = next_pattern(*pstate);
 
2776
    else{
 
2777
        *pstate = &localpstate;
 
2778
        if(!nonempty_patterns(rflags, *pstate))
 
2779
          *pstate = NULL;
 
2780
 
 
2781
        if(*pstate)
 
2782
          pat = first_pattern(*pstate);
 
2783
    }
 
2784
 
 
2785
    if(*pstate){
 
2786
    
 
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)
 
2792
                  break;
 
2793
 
 
2794
                match++;
 
2795
                if(pat->action && pat->action->incol)
 
2796
                  color = new_color_pair(pat->action->incol->fg,
 
2797
                                         pat->action->incol->bg);
 
2798
            }
 
2799
            else
 
2800
              pat = next_pattern(*pstate);
 
2801
        }
 
2802
    }
 
2803
 
 
2804
    if(match && returned_color)
 
2805
      *returned_color = color;
 
2806
 
 
2807
    return(match);
 
2808
}
 
2809
 
 
2810
 
 
2811
/*
 
2812
 *
 
2813
 */
 
2814
int
 
2815
index_in_overview(MAILSTREAM *stream)
 
2816
{
 
2817
    INDEX_COL_S  *cdesc = NULL;
 
2818
 
 
2819
    if(!(stream->mailbox && IS_REMOTE(stream->mailbox)))
 
2820
      return(FALSE);                    /* no point! */
 
2821
 
 
2822
    if(stream->dtb && stream->dtb->name && !strcmp(stream->dtb->name, "nntp")){
 
2823
 
 
2824
      if(THRD_INDX())
 
2825
        return(TRUE);
 
2826
 
 
2827
      for(cdesc = ps_global->index_disp_format;
 
2828
          cdesc->ctype != iNothing;
 
2829
          cdesc++)
 
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            */
 
2834
          case iAtt:
 
2835
            return(FALSE);
 
2836
 
 
2837
          default :
 
2838
            break;
 
2839
        }
 
2840
    }
 
2841
 
 
2842
    return(TRUE);
 
2843
}
 
2844
 
 
2845
 
 
2846
 
 
2847
/*
 
2848
 * fetch_from - called to get a the index entry's "From:" field
 
2849
 */
 
2850
int
 
2851
resent_to_us(INDEXDATA_S *idata)
 
2852
{
 
2853
    if(!idata->valid_resent_to){
 
2854
        static char *fields[] = {"Resent-To", NULL};
 
2855
        char *h;
 
2856
 
 
2857
        if(idata->no_fetch){
 
2858
            idata->bogus = 1;   /* don't do this */
 
2859
            return(FALSE);
 
2860
        }
 
2861
 
 
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);
 
2865
        }
 
2866
 
 
2867
        idata->valid_resent_to = 1;
 
2868
    }
 
2869
 
 
2870
    return(idata->resent_to_us);
 
2871
}
 
2872
 
 
2873
 
 
2874
int
 
2875
parsed_resent_to_us(char *h)
 
2876
{
 
2877
    char    *p, *q;
 
2878
    ADDRESS *addr = NULL;
 
2879
    int      rv = FALSE;
 
2880
 
 
2881
    if(p = strindex(h, ':')){
 
2882
        for(q = ++p; q = strpbrk(q, "\015\012"); q++)
 
2883
          *q = ' ';             /* quash junk */
 
2884
 
 
2885
        rfc822_parse_adrlist(&addr, p, ps_global->maildomain);
 
2886
        if(addr){
 
2887
            rv = address_is_us(addr, ps_global);
 
2888
            mail_free_address(&addr);
 
2889
        }
 
2890
    }
 
2891
 
 
2892
    return(rv);
 
2893
}
 
2894
 
 
2895
 
 
2896
 
 
2897
/*
 
2898
 * fetch_from - called to get a the index entry's "From:" field
 
2899
 */
 
2900
ADDRESS *
 
2901
fetch_from(INDEXDATA_S *idata)
 
2902
{
 
2903
    if(idata->no_fetch)                 /* implies from is valid */
 
2904
      return(idata->from);
 
2905
    else if(idata->bogus)
 
2906
      idata->bogus = 2;
 
2907
    else{
 
2908
        ENVELOPE *env;
 
2909
 
 
2910
        /* c-client call's just cache access at this point */
 
2911
        if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
 
2912
          return(env->from);
 
2913
 
 
2914
        idata->bogus = 1;
 
2915
    }
 
2916
 
 
2917
    return(NULL);
 
2918
}
 
2919
 
 
2920
 
 
2921
/*
 
2922
 * fetch_to - called to get a the index entry's "To:" field
 
2923
 */
 
2924
ADDRESS *
 
2925
fetch_to(INDEXDATA_S *idata)
 
2926
{
 
2927
    if(idata->no_fetch){                /* check for specific validity */
 
2928
        if(idata->valid_to)
 
2929
          return(idata->to);
 
2930
        else
 
2931
          idata->bogus = 1;             /* can't give 'em what they want */
 
2932
    }
 
2933
    else if(idata->bogus){
 
2934
        idata->bogus = 2;               /* elevate bogosity */
 
2935
    }
 
2936
    else{
 
2937
        ENVELOPE *env;
 
2938
 
 
2939
        /* c-client call's just cache access at this point */
 
2940
        if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
 
2941
          return(env->to);
 
2942
 
 
2943
        idata->bogus = 1;
 
2944
    }
 
2945
 
 
2946
    return(NULL);
 
2947
}
 
2948
 
 
2949
 
 
2950
/*
 
2951
 * fetch_cc - called to get a the index entry's "Cc:" field
 
2952
 */
 
2953
ADDRESS *
 
2954
fetch_cc(INDEXDATA_S *idata)
 
2955
{
 
2956
    if(idata->no_fetch){                /* check for specific validity */
 
2957
        if(idata->valid_cc)
 
2958
          return(idata->cc);
 
2959
        else
 
2960
          idata->bogus = 1;             /* can't give 'em what they want */
 
2961
    }
 
2962
    else if(idata->bogus){
 
2963
        idata->bogus = 2;               /* elevate bogosity */
 
2964
    }
 
2965
    else{
 
2966
        ENVELOPE *env;
 
2967
 
 
2968
        /* c-client call's just cache access at this point */
 
2969
        if(env = pine_mail_fetchenvelope(idata->stream, idata->rawno))
 
2970
          return(env->cc);
 
2971
 
 
2972
        idata->bogus = 1;
 
2973
    }
 
2974
 
 
2975
    return(NULL);
 
2976
}
 
2977
 
 
2978
 
 
2979
 
 
2980
/*
 
2981
 * fetch_sender - called to get a the index entry's "Sender:" field
 
2982
 */
 
2983
ADDRESS *
 
2984
fetch_sender(INDEXDATA_S *idata)
 
2985
{
 
2986
    if(idata->no_fetch){                /* check for specific validity */
 
2987
        if(idata->valid_sender)
 
2988
          return(idata->sender);
 
2989
        else
 
2990
          idata->bogus = 1;             /* can't give 'em what they want */
 
2991
    }
 
2992
    else if(idata->bogus){
 
2993
        idata->bogus = 2;               /* elevate bogosity */
 
2994
    }
 
2995
    else{
 
2996
        ENVELOPE *env;
 
2997
 
 
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);
 
3001
 
 
3002
        idata->bogus = 1;
 
3003
    }
 
3004
 
 
3005
    return(NULL);
 
3006
}
 
3007
 
 
3008
 
 
3009
/*
 
3010
 * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
 
3011
 */
 
3012
char *
 
3013
fetch_newsgroups(INDEXDATA_S *idata)
 
3014
{
 
3015
    if(idata->no_fetch){                /* check for specific validity */
 
3016
        if(idata->valid_news)
 
3017
          return(idata->newsgroups);
 
3018
        else
 
3019
          idata->bogus = 1;             /* can't give 'em what they want */
 
3020
    }
 
3021
    else if(idata->bogus){
 
3022
        idata->bogus = 2;               /* elevate bogosity */
 
3023
    }
 
3024
    else{
 
3025
        ENVELOPE *env;
 
3026
 
 
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);
 
3030
 
 
3031
        idata->bogus = 1;
 
3032
    }
 
3033
 
 
3034
    return(NULL);
 
3035
}
 
3036
 
 
3037
 
 
3038
/*
 
3039
 * fetch_subject - called to get at the index entry's "Subject:" field
 
3040
 */
 
3041
char *
 
3042
fetch_subject(INDEXDATA_S *idata)
 
3043
{
 
3044
    if(idata->no_fetch)                 /* implies subject is valid */
 
3045
      return(idata->subject);
 
3046
    else if(idata->bogus)
 
3047
      idata->bogus = 2;
 
3048
    else{
 
3049
        ENVELOPE *env;
 
3050
 
 
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);
 
3054
 
 
3055
        idata->bogus = 1;
 
3056
    }
 
3057
 
 
3058
    return(NULL);
 
3059
}
 
3060
 
 
3061
 
 
3062
/*
 
3063
 * Return an allocated copy of the first few characters from the body
 
3064
 * of the message for possible use in the index screen.
 
3065
 */
 
3066
char *
 
3067
fetch_firsttext(INDEXDATA_S *idata)
 
3068
{
 
3069
    ENVELOPE *env;
 
3070
    BODY *body = NULL;
 
3071
    char *firsttext = NULL;
 
3072
    STORE_S *so;
 
3073
    gf_io_t pc;
 
3074
 
 
3075
    if(env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)){
 
3076
        if(body){
 
3077
            char *subtype = NULL;
 
3078
 
 
3079
            if((body->type == TYPETEXT
 
3080
                && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
 
3081
                    ||
 
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))){
 
3086
 
 
3087
                if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
 
3088
                    char buf[1025], *p;
 
3089
                    unsigned char c;
 
3090
                    int success;
 
3091
                    int was_space_for_eol = 0;
 
3092
                    long partial_fetch_len;
 
3093
 
 
3094
                    if(subtype && !strucmp(subtype, "html"))
 
3095
                      partial_fetch_len = 1024;
 
3096
                    else if(subtype && !strucmp(subtype, "plain"))
 
3097
                      partial_fetch_len = 128;
 
3098
                    else
 
3099
                      partial_fetch_len = 256;
 
3100
 
 
3101
                    gf_set_so_writec(&pc, so);
 
3102
                    success = get_body_part_text(idata->stream, body, idata->rawno,
 
3103
                                                 "1", partial_fetch_len,
 
3104
                                                 pc, NULL, NULL);
 
3105
                    gf_clear_so_writec(so);
 
3106
 
 
3107
                    if(success){
 
3108
                        so_seek(so, 0L, 0);
 
3109
                        p = buf;
 
3110
                        while(p-buf < sizeof(buf)-1 && so_readc(&c, so)){
 
3111
                            if(p == buf && isspace(c))
 
3112
                              ;
 
3113
                            else if(c == '\r' || c == '\n'){
 
3114
                                if(!was_space_for_eol){
 
3115
                                    *p++ = ' ';
 
3116
                                    was_space_for_eol++;
 
3117
                                }
 
3118
                            }
 
3119
                            else{
 
3120
                                was_space_for_eol = 0;
 
3121
                                *p++ = c;
 
3122
                            }
 
3123
                        }
 
3124
 
 
3125
                        *p = '\0';
 
3126
 
 
3127
                        if(p > buf)
 
3128
                          firsttext = cpystr(buf);
 
3129
                    }
 
3130
 
 
3131
                    so_give(&so);
 
3132
                }
 
3133
            }   
 
3134
        }
 
3135
    }
 
3136
 
 
3137
    return(firsttext);
 
3138
}
 
3139
 
 
3140
 
 
3141
/*
 
3142
 * fetch_date - called to get at the index entry's "Date:" field
 
3143
 */
 
3144
char *
 
3145
fetch_date(INDEXDATA_S *idata)
 
3146
{
 
3147
    if(idata->no_fetch)                 /* implies date is valid */
 
3148
      return(idata->date);
 
3149
    else if(idata->bogus)
 
3150
      idata->bogus = 2;
 
3151
    else{
 
3152
        ENVELOPE *env;
 
3153
 
 
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);
 
3157
 
 
3158
        idata->bogus = 1;
 
3159
    }
 
3160
 
 
3161
    return(NULL);
 
3162
}
 
3163
 
 
3164
 
 
3165
/*
 
3166
 * fetch_size - called to get at the index entry's "size" field
 
3167
 */
 
3168
long
 
3169
fetch_size(INDEXDATA_S *idata)
 
3170
{
 
3171
    if(idata->no_fetch)                 /* implies size is valid */
 
3172
      return(idata->size);
 
3173
    else if(idata->bogus)
 
3174
      idata->bogus = 2;
 
3175
    else{
 
3176
        MESSAGECACHE *mc;
 
3177
 
 
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);
 
3182
 
 
3183
        idata->bogus = 1;
 
3184
    }
 
3185
 
 
3186
    return(0L);
 
3187
}
 
3188
 
 
3189
 
 
3190
/*
 
3191
 * fetch_body - called to get a the index entry's body structure
 
3192
 */
 
3193
BODY *
 
3194
fetch_body(INDEXDATA_S *idata)
 
3195
{
 
3196
    BODY *body;
 
3197
    
 
3198
    if(idata->bogus || idata->no_fetch){
 
3199
        idata->bogus = 2;
 
3200
        return(NULL);
 
3201
    }
 
3202
 
 
3203
    if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
 
3204
      return(body);
 
3205
 
 
3206
    idata->bogus = 1;
 
3207
    return(NULL);
 
3208
}
 
3209
 
 
3210
 
 
3211
/*
 
3212
 * s is at least size width+1
 
3213
 */
 
3214
int
 
3215
set_index_addr(INDEXDATA_S *idata, char *field, struct mail_address *addr,
 
3216
               char *prefix, int width, char *s, char **cset)
 
3217
{
 
3218
    ADDRESS *atmp;
 
3219
    char    *p;
 
3220
    char    *save_personal = NULL;
 
3221
 
 
3222
    for(atmp = addr; idata->stream && atmp; atmp = atmp->next)
 
3223
      if(atmp->host && atmp->host[0] == '.'){
 
3224
          char *pref, *h, *fields[2];
 
3225
          
 
3226
          if(idata->no_fetch){
 
3227
              idata->bogus = 1;
 
3228
              break;
 
3229
          }
 
3230
 
 
3231
          fields[0] = field;
 
3232
          fields[1] = NULL;
 
3233
          if(h = pine_fetchheader_lines(idata->stream, idata->rawno,
 
3234
                                        NULL, fields)){
 
3235
              /* skip "field:" */
 
3236
              for(p = h + strlen(field) + 1;
 
3237
                  *p && isspace((unsigned char)*p); p++)
 
3238
                ;
 
3239
 
 
3240
              /* add prefix */
 
3241
              for(pref = prefix; pref && *pref; pref++)
 
3242
                if(width){
 
3243
                    *s++ = *pref;
 
3244
                    width--;
 
3245
                }
 
3246
                else
 
3247
                  break;
 
3248
 
 
3249
              while(width--)
 
3250
                if(*p == '\015' || *p == '\012')
 
3251
                  p++;                          /* skip CR LF */
 
3252
                else if(!*p)
 
3253
                  *s++ = ' ';
 
3254
                else
 
3255
                  *s++ = *p++;
 
3256
 
 
3257
              *s = '\0';                        /* tie off return string */
 
3258
 
 
3259
              fs_give((void **) &h);
 
3260
              return(TRUE);
 
3261
          }
 
3262
          /* else fall thru and display what c-client gave us */
 
3263
      }
 
3264
 
 
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];
 
3270
        int   l;
 
3271
 
 
3272
        if(l = prefix ? strlen(prefix) : 0)
 
3273
          strncpy(s, prefix, width+1);
 
3274
 
 
3275
        snprintf(buftmp, sizeof(buftmp), "%s", addr->personal);
 
3276
        p = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf,
 
3277
                                    SIZEOF_20KBUF, buftmp, &dummy);
 
3278
 
 
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);
 
3282
            if(free_this)
 
3283
              p = free_this;
 
3284
        }
 
3285
 
 
3286
        removing_leading_and_trailing_white_space(p);
 
3287
 
 
3288
        iutf8ncpy(s + l, p, width - l);
 
3289
 
 
3290
        if(free_this)
 
3291
          fs_give((void **) &free_this);
 
3292
 
 
3293
        if(dummy){
 
3294
            int dl = strlen(dummy);
 
3295
 
 
3296
            if(cset && dl > 0){
 
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));
 
3302
 
 
3303
                    strncpy(*cset, UNKNOWN_CHARSET, strlen(UNKNOWN_CHARSET)+1);
 
3304
                }
 
3305
                else if(!(*cset))
 
3306
                  *cset = cpystr(dummy);
 
3307
            }
 
3308
 
 
3309
            fs_give((void **)&dummy);
 
3310
        }
 
3311
 
 
3312
        s[width] = '\0';
 
3313
        
 
3314
        if(*(s+l))
 
3315
          return(TRUE);
 
3316
        else{
 
3317
            save_personal = addr->personal;
 
3318
            addr->personal = NULL;
 
3319
        }
 
3320
    }
 
3321
 
 
3322
    if(addr){
 
3323
        char *a_string;
 
3324
        int   l;
 
3325
 
 
3326
        a_string = addr_list_string(addr, NULL, 0);
 
3327
        if(save_personal)
 
3328
          addr->personal = save_personal;
 
3329
 
 
3330
        if(l = prefix ? strlen(prefix) : 0)
 
3331
          strncpy(s, prefix, width+1);
 
3332
 
 
3333
        iutf8ncpy(s + l, a_string, width - l);
 
3334
        s[width] = '\0';
 
3335
 
 
3336
        fs_give((void **)&a_string);
 
3337
 
 
3338
        return(TRUE);
 
3339
    }
 
3340
 
 
3341
    if(save_personal)
 
3342
      addr->personal = save_personal;
 
3343
 
 
3344
    return(FALSE);
 
3345
}
 
3346
 
 
3347
 
 
3348
void
 
3349
index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
 
3350
{
 
3351
    if(!env){
 
3352
        idata->bogus = 2;
 
3353
        return;
 
3354
    }
 
3355
 
 
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;
 
3363
 
 
3364
    idata->valid_to = 1;        /* signal that everythings here */
 
3365
    idata->valid_cc = 1;
 
3366
    idata->valid_sender = 1;
 
3367
    idata->valid_news = 1;
 
3368
}
 
3369
 
 
3370
 
 
3371
/*
 
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.
 
3376
 *
 
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.
 
3381
 */
 
3382
void
 
3383
date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len)
 
3384
{
 
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 +... */
 
3397
    int         hr12;
 
3398
    int         curtype, lastmonthtype, lastyeartype;
 
3399
    int         sdatetimetype, sdatetime24type;
 
3400
    struct      date d;
 
3401
#define TODAYSTR N_("Today")
 
3402
 
 
3403
    curtype =       (type == iCurDate ||
 
3404
                     type == iCurDateIso ||
 
3405
                     type == iCurDateIsoS ||
 
3406
                     type == iCurTime24 ||
 
3407
                     type == iCurTime12 ||
 
3408
                     type == iCurDay ||
 
3409
                     type == iCurDay2Digit ||
 
3410
                     type == iCurDayOfWeek ||
 
3411
                     type == iCurDayOfWeekAbb ||
 
3412
                     type == iCurMon ||
 
3413
                     type == iCurMon2Digit ||
 
3414
                     type == iCurMonLong ||
 
3415
                     type == iCurMonAbb ||
 
3416
                     type == iCurYear ||
 
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);
 
3447
    if(str_len > 0)
 
3448
      str[0] = '\0';
 
3449
 
 
3450
    if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
 
3451
      return;
 
3452
 
 
3453
    if(curtype || lastmonthtype || lastyeartype){
 
3454
        char dbuf[200];
 
3455
 
 
3456
        rfc822_date(dbuf);
 
3457
        parse_date(dbuf, &d);
 
3458
 
 
3459
        if(lastyeartype)
 
3460
          d.year--;
 
3461
        else if(lastmonthtype){
 
3462
            d.month--;
 
3463
            if(d.month <= 0){
 
3464
                d.month = 12;
 
3465
                d.year--;
 
3466
            }
 
3467
        }
 
3468
    }
 
3469
    else
 
3470
      parse_date(datesrc, &d);
 
3471
 
 
3472
    strncpy(monabb, (d.month > 0 && d.month < 13)
 
3473
                    ? month_abbrev(d.month) : "", sizeof(monabb));
 
3474
 
 
3475
    strncpy(mon, (d.month > 0 && d.month < 13)
 
3476
                    ? int2string(d.month) : "", sizeof(mon));
 
3477
 
 
3478
    strncpy(day, (d.day > 0 && d.day < 32)
 
3479
                    ? int2string(d.day) : "", sizeof(day));
 
3480
 
 
3481
    strncpy(dayord,
 
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));
 
3486
 
 
3487
    monabb[sizeof(monabb)-1] = '\0';
 
3488
    day[sizeof(day)-1] = '\0';
 
3489
    dayord[sizeof(dayord)-1] = '\0';
 
3490
 
 
3491
    strncpy(year4, (d.year >= 1000 && d.year < 10000)
 
3492
                    ? int2string(d.year) : "????", sizeof(year4));
 
3493
    year4[sizeof(year4)-1] = '\0';
 
3494
 
 
3495
    if(d.year >= 0){
 
3496
        if((d.year % 100) < 10){
 
3497
            yearzero[0] = '0';
 
3498
            strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
 
3499
        }
 
3500
        else
 
3501
          strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
 
3502
    }
 
3503
    else
 
3504
      strncpy(yearzero, "??", sizeof(yearzero));
 
3505
 
 
3506
    yearzero[sizeof(yearzero)-1] = '\0';
 
3507
 
 
3508
    if(d.month > 0 && d.month < 10){
 
3509
        monzero[0] = '0';
 
3510
        strncpy(monzero+1, int2string(d.month), sizeof(monzero)-1);
 
3511
    }
 
3512
    else if(d.month >= 10 && d.month <= 12)
 
3513
      strncpy(monzero, int2string(d.month), sizeof(monzero));
 
3514
    else
 
3515
      strncpy(monzero, "??", sizeof(monzero));
 
3516
 
 
3517
    monzero[sizeof(monzero)-1] = '\0';
 
3518
 
 
3519
    if(d.day > 0 && d.day < 10){
 
3520
        dayzero[0] = '0';
 
3521
        strncpy(dayzero+1, int2string(d.day), sizeof(dayzero)-1);
 
3522
    }
 
3523
    else if(d.day >= 10 && d.day <= 31)
 
3524
      strncpy(dayzero, int2string(d.day), sizeof(dayzero));
 
3525
    else
 
3526
      strncpy(dayzero, "??", sizeof(dayzero));
 
3527
 
 
3528
    dayzero[sizeof(dayzero)-1] = '\0';
 
3529
 
 
3530
    hr12 = (d.hour == 0) ? 12 :
 
3531
             (d.hour > 12) ? (d.hour - 12) : d.hour;
 
3532
    hour12[0] = '\0';
 
3533
    if(hr12 > 0 && hr12 <= 12)
 
3534
      strncpy(hour12, int2string(hr12), sizeof(hour12));
 
3535
 
 
3536
    hour12[sizeof(hour12)-1] = '\0';
 
3537
 
 
3538
    hour24[0] = '\0';
 
3539
    if(d.hour >= 0 && d.hour < 10){
 
3540
        hour24[0] = '0';
 
3541
        strncpy(hour24+1, int2string(d.hour), sizeof(hour24)-1);
 
3542
    }
 
3543
    else if(d.hour >= 10 && d.hour < 24)
 
3544
      strncpy(hour24, int2string(d.hour), sizeof(hour24));
 
3545
 
 
3546
    hour24[sizeof(hour24)-1] = '\0';
 
3547
 
 
3548
    minzero[0] = '\0';
 
3549
    if(d.minute >= 0 && d.minute < 10){
 
3550
        minzero[0] = '0';
 
3551
        strncpy(minzero+1, int2string(d.minute), sizeof(minzero)-1);
 
3552
    }
 
3553
    else if(d.minute >= 10 && d.minute <= 60)
 
3554
      strncpy(minzero, int2string(d.minute), sizeof(minzero));
 
3555
 
 
3556
    minzero[sizeof(minzero)-1] = '\0';
 
3557
 
 
3558
    if(sizeof(timezone) > 5){
 
3559
        if(d.hours_off_gmt <= 0){
 
3560
            timezone[0] = '-';
 
3561
            d.hours_off_gmt *= -1;
 
3562
            d.min_off_gmt *= -1;
 
3563
        }
 
3564
        else
 
3565
          timezone[0] = '+';
 
3566
 
 
3567
        timezone[1] = '\0';
 
3568
        if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
 
3569
            timezone[1] = '0';
 
3570
            strncpy(timezone+2, int2string(d.hours_off_gmt), sizeof(timezone)-2);
 
3571
        }
 
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);
 
3574
        else{
 
3575
            timezone[1] = '0';
 
3576
            timezone[2] = '0';
 
3577
        }
 
3578
 
 
3579
        timezone[3] = '\0';
 
3580
        if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
 
3581
            timezone[3] = '0';
 
3582
            strncpy(timezone+4, int2string(d.min_off_gmt), sizeof(timezone)-4);
 
3583
        }
 
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);
 
3586
        else{
 
3587
            timezone[3] = '0';
 
3588
            timezone[4] = '0';
 
3589
        }
 
3590
 
 
3591
        timezone[5] = '\0';
 
3592
        timezone[sizeof(timezone)-1] = '\0';
 
3593
    }
 
3594
 
 
3595
    switch(type){
 
3596
      case iRDate:
 
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);
 
3601
        break;
 
3602
      case iDayOfWeekAbb:
 
3603
      case iCurDayOfWeekAbb:
 
3604
        strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? week_abbrev(d.wkday) : "", str_len);
 
3605
        str[str_len-1] = '\0';
 
3606
        break;
 
3607
      case iDayOfWeek:
 
3608
      case iCurDayOfWeek:
 
3609
        strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? _(day_name[d.wkday]) : "", str_len);
 
3610
        str[str_len-1] = '\0';
 
3611
        break;
 
3612
      case iYear:
 
3613
      case iCurYear:
 
3614
      case iLstYear:
 
3615
      case iLstMonYear:
 
3616
        strncpy(str, year4, str_len);
 
3617
        break;
 
3618
      case iDay2Digit:
 
3619
      case iCurDay2Digit:
 
3620
        strncpy(str, dayzero, str_len);
 
3621
        break;
 
3622
      case iMon2Digit:
 
3623
      case iCurMon2Digit:
 
3624
      case iLstMon2Digit:
 
3625
        strncpy(str, monzero, str_len);
 
3626
        break;
 
3627
      case iYear2Digit:
 
3628
      case iCurYear2Digit:
 
3629
      case iLstYear2Digit:
 
3630
      case iLstMonYear2Digit:
 
3631
        strncpy(str, yearzero, str_len);
 
3632
        break;
 
3633
      case iTimezone:
 
3634
        strncpy(str, timezone, str_len);
 
3635
        break;
 
3636
      case iDay:
 
3637
      case iCurDay:
 
3638
        strncpy(str, day, str_len);
 
3639
        break;
 
3640
      case iDayOrdinal:
 
3641
        snprintf(str, str_len, "%s%s", day, dayord);
 
3642
        break;
 
3643
      case iMon:
 
3644
      case iCurMon:
 
3645
      case iLstMon:
 
3646
        if(d.month > 0 && d.month <= 12)
 
3647
          strncpy(str, int2string(d.month), str_len);
 
3648
 
 
3649
        break;
 
3650
      case iMonAbb:
 
3651
      case iCurMonAbb:
 
3652
      case iLstMonAbb:
 
3653
        strncpy(str, monabb, str_len);
 
3654
        break;
 
3655
      case iMonLong:
 
3656
      case iCurMonLong:
 
3657
      case iLstMonLong:
 
3658
        strncpy(str, (d.month > 0 && d.month < 13)
 
3659
                        ? month_name(d.month) : "", str_len);
 
3660
        break;
 
3661
      case iDate:
 
3662
      case iCurDate:
 
3663
        if(v)
 
3664
          snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
 
3665
        else
 
3666
          snprintf(str, str_len, "%3s %2s", monabb, day);
 
3667
 
 
3668
        break;
 
3669
      case iLDate:
 
3670
        if(v)
 
3671
          snprintf(str, str_len, "%s%s%s%s%s", monabb,
 
3672
                  (monabb[0] && day[0]) ? " " : "", day,
 
3673
                  ((monabb[0] || day[0]) && year4[0]) ? ", " : "",
 
3674
                  year4);
 
3675
        else
 
3676
          snprintf(str, str_len, "%3s %2s%c %4s", monabb, day,
 
3677
                  (monabb[0] && day[0] && year4[0]) ? ',' : ' ',
 
3678
                  year4);
 
3679
        break;
 
3680
      case iS1Date:
 
3681
      case iS2Date:
 
3682
      case iS3Date:
 
3683
      case iS4Date:
 
3684
      case iDateIso:
 
3685
      case iDateIsoS:
 
3686
      case iCurDateIso:
 
3687
      case iCurDateIsoS:
 
3688
        if(monzero[0] == '?' && dayzero[0] == '?' &&
 
3689
           yearzero[0] == '?')
 
3690
          snprintf(str, str_len, "%8s", "");
 
3691
        else{
 
3692
            switch(type){
 
3693
              case iS1Date:
 
3694
                snprintf(str, str_len, "%2s/%2s/%2s",
 
3695
                        monzero, dayzero, yearzero);
 
3696
                break;
 
3697
              case iS2Date:
 
3698
                snprintf(str, str_len, "%2s/%2s/%2s",
 
3699
                        dayzero, monzero, yearzero);
 
3700
                break;
 
3701
              case iS3Date:
 
3702
                snprintf(str, str_len, "%2s.%2s.%2s",
 
3703
                        dayzero, monzero, yearzero);
 
3704
                break;
 
3705
              case iS4Date:
 
3706
                snprintf(str, str_len, "%2s.%2s.%2s",
 
3707
                        yearzero, monzero, dayzero);
 
3708
                break;
 
3709
              case iDateIsoS:
 
3710
              case iCurDateIsoS:
 
3711
                snprintf(str, str_len, "%2s-%2s-%2s",
 
3712
                        yearzero, monzero, dayzero);
 
3713
                break;
 
3714
              case iDateIso:
 
3715
              case iCurDateIso:
 
3716
                snprintf(str, str_len, "%4s-%2s-%2s",
 
3717
                        year4, monzero, dayzero);
 
3718
                break;
 
3719
            }
 
3720
        }
 
3721
 
 
3722
        break;
 
3723
      case iTime24:
 
3724
      case iCurTime24:
 
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 : "");
 
3729
        break;
 
3730
      case iTime12:
 
3731
      case iCurTime12:
 
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" :
 
3738
                    "  ");
 
3739
        break;
 
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;
 
3747
          char        dbuf[200];
 
3748
          int         msg_day_of_year, now_day_of_year, today;
 
3749
          int         diff, ydiff, last_day_of_year;
 
3750
 
 
3751
          rfc822_date(dbuf);
 
3752
          parse_date(dbuf, &now);
 
3753
          today = day_of_week(&now) + 7;
 
3754
 
 
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;
 
3759
 
 
3760
              if(msg_day_of_year == -1)
 
3761
                diff = -100;
 
3762
              else if(ydiff == 0)
 
3763
                diff = now_day_of_year - msg_day_of_year;
 
3764
              else if(ydiff == 1){
 
3765
                  last_day = d;
 
3766
                  last_day.month = 12;
 
3767
                  last_day.day = 31;
 
3768
                  last_day_of_year = day_of_year(&last_day);
 
3769
 
 
3770
                  diff = now_day_of_year +
 
3771
                          (last_day_of_year - msg_day_of_year);
 
3772
              }
 
3773
              else if(ydiff == -1){
 
3774
                  last_day = now;
 
3775
                  last_day.month = 12;
 
3776
                  last_day.day = 31;
 
3777
                  last_day_of_year = day_of_year(&last_day);
 
3778
 
 
3779
                  diff = -1 * (msg_day_of_year +
 
3780
                          (last_day_of_year - now_day_of_year));
 
3781
              }
 
3782
              else if(ydiff > 1)
 
3783
                diff = 100;
 
3784
              else
 
3785
                diff = -100;
 
3786
 
 
3787
              if(diff == 0)
 
3788
                strncpy(str, _(TODAYSTR), str_len);
 
3789
              else if(diff == 1)
 
3790
                strncpy(str, _("Yesterday"), str_len);
 
3791
              else if(diff > 1 && diff < 7)
 
3792
                snprintf(str, str_len, "%s", _(day_name[(today - diff) % 7]));
 
3793
              else if(diff == -1)
 
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]));
 
3798
              else if(diff > 0
 
3799
                      && (ydiff == 0
 
3800
                          || (ydiff == 1 && 12 + now.month - d.month < 6))){
 
3801
                  if(v)
 
3802
                    snprintf(str, str_len, "%s%s%s", monabb,
 
3803
                             (monabb[0] && day[0]) ? " " : "", day);
 
3804
                  else
 
3805
                    snprintf(str, str_len, "%3s %2s", monabb, day);
 
3806
              }
 
3807
              else{
 
3808
                  if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
 
3809
                    type = iSDateTimeIsoS;
 
3810
 
 
3811
                  switch(type){
 
3812
                    case iSDate: case iSDateTime: case iSDateTime24:
 
3813
                      {
 
3814
                        struct tm tm;
 
3815
 
 
3816
                        memset(&tm, 0, sizeof(tm));
 
3817
                        tm.tm_year = MAX(d.year-1900, 0);
 
3818
                        tm.tm_mon = d.month-1;
 
3819
                        tm.tm_mday = d.day;
 
3820
                        strftime(str, str_len, "%x", &tm);
 
3821
                      }
 
3822
 
 
3823
                      break;
 
3824
                    case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
 
3825
                      if(v)
 
3826
                        snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
 
3827
                                 diff < 0 ? "!" : "");
 
3828
                      else
 
3829
                        snprintf(str, str_len, "%s%s/%s/%s%s",
 
3830
                                 (mon[0] && mon[1]) ? "" : " ",
 
3831
                                 mon, dayzero, yearzero,
 
3832
                                 diff < 0 ? "!" : "");
 
3833
                      break;
 
3834
                    case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
 
3835
                      if(v)
 
3836
                        snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
 
3837
                                 diff < 0 ? "!" : "");
 
3838
                      else
 
3839
                        snprintf(str, str_len, "%s%s/%s/%s%s",
 
3840
                                 (day[0] && day[1]) ? "" : " ",
 
3841
                                 day, monzero, yearzero,
 
3842
                                 diff < 0 ? "!" : "");
 
3843
                      break;
 
3844
                    case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
 
3845
                      if(v)
 
3846
                        snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
 
3847
                                 diff < 0 ? "!" : "");
 
3848
                      else
 
3849
                        snprintf(str, str_len, "%s%s.%s.%s%s",
 
3850
                                 (day[0] && day[1]) ? "" : " ",
 
3851
                                 day, monzero, yearzero,
 
3852
                                 diff < 0 ? "!" : "");
 
3853
                      break;
 
3854
                    case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
 
3855
                      if(v)
 
3856
                        snprintf(str, str_len, "%s.%s.%s%s",
 
3857
                                 yearzero, monzero, dayzero,
 
3858
                                 diff < 0 ? "!" : "");
 
3859
                      else
 
3860
                        snprintf(str, str_len, "%s.%s.%s%s",
 
3861
                                 yearzero, monzero, dayzero,
 
3862
                                 diff < 0 ? "!" : "");
 
3863
                      break;
 
3864
                    case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
 
3865
                      snprintf(str, str_len, "%2s-%2s-%2s",
 
3866
                            yearzero, monzero, dayzero);
 
3867
                      break;
 
3868
                    case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
 
3869
                      snprintf(str, str_len, "%4s-%2s-%2s",
 
3870
                            year4, monzero, dayzero);
 
3871
                      break;
 
3872
                  }
 
3873
              }
 
3874
 
 
3875
          }
 
3876
          else{
 
3877
              if(v)
 
3878
                snprintf(str, str_len, "%s%s%s", monabb,
 
3879
                        (monabb[0] && day[0]) ? " " : "", day);
 
3880
              else
 
3881
                snprintf(str, str_len, "%3s %2s", monabb, day);
 
3882
          }
 
3883
        }
 
3884
 
 
3885
        break;
 
3886
    }
 
3887
 
 
3888
    str[str_len-1] = '\0';
 
3889
    
 
3890
    if(type == iSTime ||
 
3891
       (sdatetimetype && !strcmp(str, _(TODAYSTR)))){
 
3892
        struct date now, last_day;
 
3893
        char        dbuf[200], *Ddd, *ampm;
 
3894
        int         daydiff;
 
3895
 
 
3896
        str[0] = '\0';
 
3897
        rfc822_date(dbuf);
 
3898
        parse_date(dbuf, &now);
 
3899
 
 
3900
        /* Figure out if message date lands in the past week */
 
3901
 
 
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)){
 
3905
 
 
3906
            daydiff = day_of_year(&now) - day_of_year(&d);
 
3907
 
 
3908
            /*
 
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)...
 
3912
             */
 
3913
            if(d.year == now.year - 1){
 
3914
                last_day = d;
 
3915
                last_day.month = 12;
 
3916
                last_day.day   = 31;
 
3917
 
 
3918
                daydiff += day_of_year(&last_day);
 
3919
            }
 
3920
        }
 
3921
        else
 
3922
          daydiff = -100;       /* comfortably out of range (of past week) */
 
3923
 
 
3924
        /* Build 2-digit hour and am/pm indicator, used below */
 
3925
 
 
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);
 
3930
        }
 
3931
        else{
 
3932
            strncpy(hour12, "??", sizeof(hour12));
 
3933
            hour12[sizeof(hour12)-1] = '\0';
 
3934
            ampm = "__";
 
3935
            strncpy(hour24, "??", sizeof(hour24));
 
3936
            hour24[sizeof(hour24)-1] = '\0';
 
3937
        }
 
3938
 
 
3939
        /* Build date/time in str, in format similar to that used by w(1) */
 
3940
 
 
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);
 
3944
            else{
 
3945
              strncpy(minzero, "??", sizeof(minzero));
 
3946
              minzero[sizeof(minzero)-1] = '\0';
 
3947
            }
 
3948
 
 
3949
            snprintf(str, str_len, "%s:%s%s", sdatetime24type ? hour24 : hour12,
 
3950
                     minzero, sdatetime24type ? "" : ampm);
 
3951
        }
 
3952
        else if(daydiff >= 1 && daydiff < 6){ /* If <1wk ago, "DddHHap" */
 
3953
 
 
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));
 
3957
            else
 
3958
              Ddd = "???";
 
3959
 
 
3960
            snprintf(str, str_len, "%s%s%s", Ddd, hour12, ampm);
 
3961
        }
 
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';
 
3966
 
 
3967
            if(d.day >= 1 && d.day <= 31)
 
3968
              snprintf(dayzero, sizeof(dayzero), "%02d", d.day);
 
3969
            else{
 
3970
              strncpy(dayzero, "??", sizeof(dayzero));
 
3971
              dayzero[sizeof(dayzero)-1] = '\0';
 
3972
            }
 
3973
 
 
3974
            if(d.year >= 0 && d.year <= 9999)
 
3975
              snprintf(yearzero, sizeof(yearzero), "%02d", d.year % 100);
 
3976
            else{
 
3977
              strncpy(yearzero, "??", sizeof(yearzero));
 
3978
              yearzero[sizeof(yearzero)-1] = '\0';
 
3979
            }
 
3980
 
 
3981
            snprintf(str, str_len, "%s%s%s", dayzero, monabb, yearzero);
 
3982
        }
 
3983
 
 
3984
        if(str[0] == '0'){      /* leading 0 (date|hour) elided or blanked */
 
3985
            if(v)
 
3986
              memmove(str, str + 1, strlen(str));
 
3987
            else
 
3988
              str[0] = ' ';
 
3989
        }
 
3990
    }
 
3991
}
 
3992
 
 
3993
 
 
3994
/*
 
3995
 * Format a string representing the keywords into ice.
 
3996
 *
 
3997
 * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
 
3998
 *
 
3999
 * Args  idata -- which message?
 
4000
 *      kwtype -- keywords or kw initials
 
4001
 *         ice -- index cache entry for message
 
4002
 */
 
4003
void
 
4004
key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
 
4005
{
 
4006
    int           firstone = 1;
 
4007
    KEYWORD_S    *kw;
 
4008
    char         *word;
 
4009
    COLOR_PAIR   *color = NULL;
 
4010
    SPEC_COLOR_S *sc = ps_global->kw_colors;
 
4011
    IELEM_S      *ielem = NULL;
 
4012
    IFIELD_S     *ourifield = NULL;
 
4013
    char         *p;
 
4014
    SIZEDTEXT     src, result;
 
4015
 
 
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)
 
4021
          ;
 
4022
    }
 
4023
 
 
4024
    if(!ourifield)
 
4025
      return;
 
4026
 
 
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 : "";
 
4032
 
 
4033
            /*
 
4034
             * Pick off the first initial. Since word is UTF-8 it may
 
4035
             * take more than one byte for the first initial.
 
4036
             */
 
4037
 
 
4038
            if(word && word[0]){
 
4039
                UCS ucs;
 
4040
                unsigned long remaining_octets;
 
4041
                unsigned char *inputp;
 
4042
 
 
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';
 
4053
 
 
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;
 
4060
                        color = NULL;
 
4061
                    }
 
4062
                }
 
4063
            }
 
4064
 
 
4065
            if(color)
 
4066
              free_color_pair(&color);
 
4067
        }
 
4068
      }
 
4069
    }
 
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)){
 
4073
 
 
4074
            if(!firstone){
 
4075
                ielem = new_ielem(&ourifield->ielem);
 
4076
                ielem->freedata = 1;
 
4077
                ielem->data = cpystr(" ");
 
4078
                ielem->datalen = 1;
 
4079
            }
 
4080
 
 
4081
            firstone = 0;
 
4082
 
 
4083
            word = (kw->nick && kw->nick[0]) ? kw->nick :
 
4084
                     (kw->kw && kw->kw[0])     ? kw->kw : "";
 
4085
 
 
4086
            if(word[0]){
 
4087
                ielem = new_ielem(&ourifield->ielem);
 
4088
                ielem->freedata = 1;
 
4089
                ielem->data = cpystr(word);
 
4090
                ielem->datalen = strlen(word);
 
4091
 
 
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;
 
4098
                    color = NULL;
 
4099
                }
 
4100
            }
 
4101
 
 
4102
            if(color)
 
4103
              free_color_pair(&color);
 
4104
        }
 
4105
      }
 
4106
    }
 
4107
 
 
4108
    /*
 
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.
 
4113
     */
 
4114
    if(pico_usingcolor()){
 
4115
        ielem = new_ielem(&ourifield->ielem);
 
4116
        ielem->freedata = 1;
 
4117
        ielem->data = cpystr(" ");
 
4118
        ielem->datalen = 1;
 
4119
    }
 
4120
 
 
4121
    ourifield->leftadj = 1;
 
4122
    set_ielem_widths_in_field(ourifield);
 
4123
}
 
4124
 
 
4125
 
 
4126
/*
 
4127
 * Put a string representing the subject into str. Idata tells us which
 
4128
 * message we are referring to.
 
4129
 *
 
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.
 
4132
 *
 
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
 
4139
 */
 
4140
void
 
4141
subj_str(INDEXDATA_S *idata, int width, char *str, SubjKW kwtype, int opening, ICE_S *ice)
 
4142
{
 
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;
 
4146
    size_t         len;
 
4147
    int            depth = 0, mult = 2, collapsed, i;
 
4148
    int            save;
 
4149
    int            do_subj = 0;
 
4150
    PINETHRD_S    *thd, *thdorig;
 
4151
    IELEM_S       *ielem = NULL, *anotherielem, *subjielem = NULL;
 
4152
    IFIELD_S      *ourifield = NULL;
 
4153
 
 
4154
    /*
 
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.
 
4157
     */
 
4158
    if(opening && idata->no_fetch){
 
4159
        idata->bogus = 1;
 
4160
        return;
 
4161
    }
 
4162
 
 
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)
 
4168
          ;
 
4169
    }
 
4170
 
 
4171
    memset(str, 0, (width+1) * sizeof(*str));
 
4172
    origstr = str;
 
4173
    rawsubj = fetch_subject(idata);
 
4174
    if(!rawsubj)
 
4175
      rawsubj = "";
 
4176
 
 
4177
    /*
 
4178
     * Before we do anything else, decode the character set in the subject and
 
4179
     * work with the result.
 
4180
     */
 
4181
    sp = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf,
 
4182
                                 SIZEOF_20KBUF, rawsubj, &cset);
 
4183
 
 
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);
 
4187
        if(free_this)
 
4188
          sp = free_this;
 
4189
    }
 
4190
 
 
4191
    len = strlen(sp);
 
4192
    len += 100;                 /* for possible charset, escaped characters */
 
4193
    origsubj = fs_get((len+1) * sizeof(unsigned char));
 
4194
    origsubj[0] = '\0';
 
4195
 
 
4196
    iutf8ncpy(origsubj, sp, len);
 
4197
 
 
4198
    origsubj[len] = '\0';
 
4199
 
 
4200
    if(cset){
 
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));
 
4206
 
 
4207
            strncpy(ice->charset, UNKNOWN_CHARSET, strlen(UNKNOWN_CHARSET)+1);
 
4208
        }
 
4209
        else if(ice && !ice->charset)
 
4210
          ice->charset = cpystr(cset);
 
4211
 
 
4212
        fs_give((void **) &cset);
 
4213
    }
 
4214
 
 
4215
 
 
4216
    /*
 
4217
     * origsubj is the original subject but it has been decoded. We need
 
4218
     * to free it at the end of this routine.
 
4219
     */
 
4220
 
 
4221
 
 
4222
    /*
 
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.
 
4231
     */
 
4232
    if(kwtype == KW || kwtype == KWInit){
 
4233
        subject = prepend_keyword_subject(idata->stream, idata->rawno,
 
4234
                                          origsubj, kwtype,
 
4235
                                          ourifield ? &subjielem : NULL,
 
4236
                                          ps_global->VAR_KW_BRACES);
 
4237
        free_subj = subject;
 
4238
    }
 
4239
    else{
 
4240
        subject = origsubj;
 
4241
        if(ourifield){
 
4242
            subjielem = new_ielem(&subjielem);
 
4243
            subjielem->freedata = 1;
 
4244
            subjielem->data = cpystr(subject);
 
4245
            subjielem->datalen = strlen(subject);
 
4246
        }
 
4247
    }
 
4248
 
 
4249
    if(!subject)
 
4250
      subject = "";
 
4251
 
 
4252
    if(THREADING()
 
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,
 
4261
                                                  thd && thd->next
 
4262
                                                  && get_lflag(idata->stream,
 
4263
                                                               NULL,idata->rawno,
 
4264
                                                               MN_COLL));
 
4265
 
 
4266
        sptr = str;
 
4267
 
 
4268
        if(thd)
 
4269
          while(thd->parent &&
 
4270
                (thd = fetch_thread(idata->stream, thd->parent)))
 
4271
            depth++;
 
4272
 
 
4273
        if(depth > 0){
 
4274
            if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
 
4275
              mult = 1;
 
4276
 
 
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);
 
4284
                        q = str + width-4;
 
4285
                    }
 
4286
                    else if(width >= 5 && depth < 1000){
 
4287
                        snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
 
4288
                        q = str + width-5;
 
4289
                    }
 
4290
                    else{
 
4291
                        snprintf(str, width+1, "%s", repeat_char(width, '.'), width);
 
4292
                        q = str;
 
4293
                    }
 
4294
 
 
4295
                    border = q;
 
4296
                    sptr = NULL;
 
4297
                }
 
4298
 
 
4299
                if(p < border){
 
4300
                    p[0] = ' ';
 
4301
                    if(p + 1 < border)
 
4302
                      p[1] = ' ';
 
4303
 
 
4304
                    if(ps_global->thread_disp_style == THREAD_STRUCT
 
4305
                       || ps_global->thread_disp_style == THREAD_MUTTLIKE){
 
4306
    /*
 
4307
     * WARNING!
 
4308
     * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
 
4309
     * is ascii.
 
4310
     */
 
4311
                        if(thd == thdorig && !thd->branch)
 
4312
                          p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
 
4313
                        else if(thd == thdorig || thd->branch)
 
4314
                          p[0] = '|';
 
4315
 
 
4316
                        if(p + 1 < border && thd == thdorig)
 
4317
                          p[1] = '-';
 
4318
                    }
 
4319
                }
 
4320
            }
 
4321
        }
 
4322
 
 
4323
        if(sptr){
 
4324
            /*
 
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.
 
4328
             *
 
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.
 
4337
             */
 
4338
            if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
 
4339
                if(depth == 0)
 
4340
                  do_subj++;
 
4341
                else{
 
4342
                    if(thdorig->parent &&
 
4343
                       (thd = fetch_thread(idata->stream, thdorig->parent))
 
4344
                       && thd->rawno){
 
4345
                        char       *this_orig = NULL,
 
4346
                                   *prev_orig = NULL,
 
4347
                                   *free_prev_orig = NULL,
 
4348
                                   *this_prep = NULL,  /* includes prepend */
 
4349
                                   *prev_prep = NULL;
 
4350
                        ENVELOPE   *env;
 
4351
                        char       *prevsubj = NULL;
 
4352
                        mailcache_t mc;
 
4353
                        SORTCACHE  *sc = NULL;
 
4354
 
 
4355
                        /* get the stripped subject of previous message */
 
4356
                        mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
 
4357
                        if(mc)
 
4358
                          sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
 
4359
                        
 
4360
                        if(sc && sc->subject)
 
4361
                          prev_orig = sc->subject;
 
4362
                        else{
 
4363
                            char *stripthis;
 
4364
 
 
4365
                            env = pine_mail_fetchenvelope(idata->stream,
 
4366
                                                          thd->rawno);
 
4367
                            stripthis = (env && env->subject)
 
4368
                                                    ? env->subject : "";
 
4369
 
 
4370
                            mail_strip_subject(stripthis, &prev_orig);
 
4371
                            
 
4372
                            free_prev_orig = prev_orig;
 
4373
                        }
 
4374
 
 
4375
                        mail_strip_subject(rawsubj, &this_orig);
 
4376
 
 
4377
                        if(kwtype == KW || kwtype == KWInit){
 
4378
                            prev_prep = prepend_keyword_subject(idata->stream,
 
4379
                                                                thd->rawno,
 
4380
                                                                prev_orig,
 
4381
                                                                kwtype, NULL,
 
4382
                                                    ps_global->VAR_KW_BRACES);
 
4383
 
 
4384
                            this_prep = prepend_keyword_subject(idata->stream,
 
4385
                                                                idata->rawno,
 
4386
                                                                this_orig,
 
4387
                                                                kwtype, NULL,
 
4388
                                                    ps_global->VAR_KW_BRACES);
 
4389
 
 
4390
                            if((this_prep || prev_prep)
 
4391
                               && (this_prep && !prev_prep
 
4392
                                   || prev_prep && !this_prep
 
4393
                                   || strucmp(this_prep, prev_prep)))
 
4394
                              do_subj++;
 
4395
                        }
 
4396
                        else{
 
4397
                            if((this_orig || prev_orig)
 
4398
                               && (this_orig && !prev_orig
 
4399
                                   || prev_orig && !this_orig
 
4400
                                   || strucmp(this_orig, prev_orig)))
 
4401
                              do_subj++;
 
4402
                        }
 
4403
 
 
4404
                        /*
 
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.
 
4411
                         *
 
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!
 
4422
                         *
 
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.
 
4428
                         */
 
4429
                        if(!do_subj){
 
4430
                            while(thd){
 
4431
                                if(!msgline_hidden(idata->stream,
 
4432
                                             sp_msgmap(idata->stream),
 
4433
                                             mn_raw2m(sp_msgmap(idata->stream),
 
4434
                                                      (long) thd->rawno),
 
4435
                                             0)){
 
4436
                                    break;      /* found a visible parent */
 
4437
                                }
 
4438
 
 
4439
                                if(thd && thd->parent)
 
4440
                                  thd = fetch_thread(idata->stream,thd->parent);
 
4441
                                else
 
4442
                                  thd = NULL;
 
4443
                            }
 
4444
 
 
4445
                            if(!thd)            /* none were visible */
 
4446
                              do_subj++;
 
4447
                        }
 
4448
 
 
4449
                        if(this_orig)
 
4450
                          fs_give((void **) &this_orig);
 
4451
 
 
4452
                        if(this_prep)
 
4453
                          fs_give((void **) &this_prep);
 
4454
 
 
4455
                        if(free_prev_orig)
 
4456
                          fs_give((void **) &free_prev_orig);
 
4457
 
 
4458
                        if(prev_prep)
 
4459
                          fs_give((void **) &prev_prep);
 
4460
                    }
 
4461
                    else
 
4462
                      do_subj++;
 
4463
                }
 
4464
            }
 
4465
            else
 
4466
              do_subj++;
 
4467
 
 
4468
            if(do_subj){
 
4469
                width -= (sptr - str);
 
4470
 
 
4471
                strncpy(sptr, subject, width);
 
4472
                sptr[width] = '\0';
 
4473
            }
 
4474
            else if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
 
4475
                sptr[0] = '>';
 
4476
                sptr++;
 
4477
                /*
 
4478
                 * We decided we don't need the subject so we'd better
 
4479
                 * eliminate subjielem.
 
4480
                 */
 
4481
                free_ielem(&subjielem);
 
4482
            }
 
4483
        }
 
4484
 
 
4485
        if(ourifield && sptr && sptr > origstr){
 
4486
            ielem = new_ielem(&ourifield->ielem);
 
4487
            ielem->type = eThreadInfo;
 
4488
            ielem->freedata = 1;
 
4489
            save = *sptr;
 
4490
            *sptr = '\0';
 
4491
            ielem->data = cpystr(origstr);
 
4492
            ielem->datalen = strlen(origstr);
 
4493
            *sptr = save;
 
4494
        }
 
4495
    }
 
4496
    else{
 
4497
        /*
 
4498
         * Not much to do for the non-threading case. Just copy the
 
4499
         * subject we have so far into str and truncate it.
 
4500
         */
 
4501
        strncpy(str, subject, width);
 
4502
        str[width] = '\0';
 
4503
    }
 
4504
 
 
4505
    if(ourifield){
 
4506
        /*
 
4507
         * We need to add subjielem to the end of the ourifield->ielem list.
 
4508
         */
 
4509
        if(subjielem){
 
4510
            if(ourifield->ielem){
 
4511
                for(ielem = ourifield->ielem;
 
4512
                    ielem && ielem->next; ielem = ielem->next)
 
4513
                  ;
 
4514
                
 
4515
                ielem->next = subjielem;
 
4516
            }
 
4517
            else
 
4518
                ourifield->ielem = subjielem;
 
4519
        }
 
4520
 
 
4521
        ourifield->leftadj = 1;
 
4522
    }
 
4523
 
 
4524
    if(opening && ourifield){
 
4525
        IELEM_S *ftielem = NULL;
 
4526
        size_t len;
 
4527
        char *first_text;
 
4528
 
 
4529
        first_text = fetch_firsttext(idata);
 
4530
 
 
4531
        if(first_text){
 
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);
 
4541
            if(first_text)
 
4542
              fs_give((void **) &first_text);
 
4543
 
 
4544
            if(ftielem){
 
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);
 
4550
 
 
4551
                    ielem = new_ielem(&ftielem);
 
4552
                    ielem->freedata = 1;
 
4553
                    ielem->data = cpystr(" ");
 
4554
                    ielem->datalen = 1;
 
4555
                }
 
4556
 
 
4557
                if(ourifield->ielem){
 
4558
                    for(ielem = ourifield->ielem;
 
4559
                        ielem && ielem->next; ielem = ielem->next)
 
4560
                      ;
 
4561
                    
 
4562
                    ielem->next = ftielem;
 
4563
                }
 
4564
                else
 
4565
                    ourifield->ielem = ftielem;
 
4566
            }
 
4567
 
 
4568
            ourifield->leftadj = 1;
 
4569
        }
 
4570
    }
 
4571
 
 
4572
    if(ourifield)
 
4573
      set_ielem_widths_in_field(ourifield);
 
4574
 
 
4575
    if(origsubj)
 
4576
      fs_give((void **) &origsubj);
 
4577
 
 
4578
    if(free_subj)
 
4579
      fs_give((void **) &free_subj);
 
4580
 
 
4581
    if(free_this)
 
4582
      fs_give((void **) &free_this);
 
4583
}
 
4584
 
 
4585
 
 
4586
/*
 
4587
 * Returns an allocated string which is the passed in subject with a
 
4588
 * list of keywords prepended.
 
4589
 *
 
4590
 * If kwtype == KW you will end up with
 
4591
 *
 
4592
 *     {keyword1 keyword2} subject
 
4593
 *
 
4594
 * (actually, keyword nicknames will be used instead of the actual keywords
 
4595
 *  in the case that the user defined nicknames)
 
4596
 *
 
4597
 * If kwtype == KWInit you get
 
4598
 *
 
4599
 *     {AB} subject
 
4600
 *
 
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
 
4603
 * than two.
 
4604
 *
 
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
 
4607
 * be set.
 
4608
 */
 
4609
char *
 
4610
prepend_keyword_subject(MAILSTREAM *stream, long int rawno, char *subject,
 
4611
                        SubjKW kwtype, IELEM_S **ielemp, char *braces)
 
4612
{
 
4613
    char        **t;
 
4614
    char         *p, *next_piece, *retsubj = NULL, *str;
 
4615
    char         *left_brace = NULL, *right_brace = NULL;
 
4616
    size_t        len;
 
4617
    int           some_set = 0, save;
 
4618
    IELEM_S      *ielem;
 
4619
    KEYWORD_S    *kw;
 
4620
    COLOR_PAIR   *color = NULL;
 
4621
    SPEC_COLOR_S *sc = ps_global->kw_colors;
 
4622
    char         *tmp = NULL;
 
4623
 
 
4624
    if(!subject)
 
4625
      subject = "";
 
4626
 
 
4627
    if(braces && *braces)
 
4628
      get_pair(braces, &left_brace, &right_brace, 1, 0);
 
4629
 
 
4630
    len = (left_brace ? strlen(left_brace) : 0) +
 
4631
            (right_brace ? strlen(right_brace) : 0);
 
4632
 
 
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)){
 
4636
              if(kwtype == KW){
 
4637
                  if(some_set)
 
4638
                    len++;              /* space between keywords */
 
4639
 
 
4640
                  str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
 
4641
                  len += strlen(str);
 
4642
              }
 
4643
              else if(kwtype == KWInit){
 
4644
                  str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
 
4645
                  /* interested in only the first UTF-8 initial */
 
4646
                  if(str && str[0]){
 
4647
                    UCS ucs;
 
4648
                    unsigned long remaining_octets;
 
4649
                    unsigned char *inputp;
 
4650
 
 
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);
 
4656
                    }
 
4657
                  }
 
4658
              }
 
4659
 
 
4660
              some_set++;
 
4661
          }
 
4662
    }
 
4663
 
 
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;
 
4669
 
 
4670
        for(kw = ps_global->keywords; kw; kw = kw->next){
 
4671
            if(user_flag_is_set(stream, rawno, kw->kw)){
 
4672
                if(p == retsubj){
 
4673
                    if(left_brace && len > 0)
 
4674
                      sstrncpy(&p, left_brace, len);
 
4675
                }
 
4676
                else if(kwtype == KW)
 
4677
                  *p++ = ' ';
 
4678
                
 
4679
                if(ielemp && p > next_piece){
 
4680
                    save = *p;
 
4681
                    *p = '\0';
 
4682
                    ielem = new_ielem(ielemp);
 
4683
                    ielem->freedata = 1;
 
4684
                    ielem->data = cpystr(next_piece);
 
4685
                    ielem->datalen = strlen(next_piece);
 
4686
                    *p = save;
 
4687
                    next_piece = p;
 
4688
                }
 
4689
 
 
4690
                str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
 
4691
 
 
4692
                if(kwtype == KWInit){
 
4693
                  if(str && str[0]){
 
4694
                    UCS ucs;
 
4695
                    unsigned long remaining_octets;
 
4696
                    unsigned char *inputp;
 
4697
 
 
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;
 
4711
                                save = *p;
 
4712
                                *p = '\0';
 
4713
                                ielem->data = cpystr(next_piece);
 
4714
                                ielem->datalen = strlen(next_piece);
 
4715
                                ielem->color = color;
 
4716
                                color = NULL;
 
4717
                                *p = save;
 
4718
                                next_piece = p;
 
4719
                            }
 
4720
                        }
 
4721
                    }
 
4722
 
 
4723
                    if(color)
 
4724
                      free_color_pair(&color);
 
4725
                  }
 
4726
                }
 
4727
                else{
 
4728
                    if(len-(p-retsubj) > 0)
 
4729
                      sstrncpy(&p, str, len-(p-retsubj));
 
4730
 
 
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;
 
4738
                        save = *p;
 
4739
                        *p = '\0';
 
4740
                        ielem->data = cpystr(next_piece);
 
4741
                        ielem->datalen = strlen(next_piece);
 
4742
                        ielem->color = color;
 
4743
                        color = NULL;
 
4744
                        *p = save;
 
4745
                        next_piece = p;
 
4746
                    }
 
4747
 
 
4748
                    if(color)
 
4749
                      free_color_pair(&color);
 
4750
                }
 
4751
            }
 
4752
        }
 
4753
 
 
4754
        if(len-(p-retsubj) > 0 && right_brace)
 
4755
          sstrncpy(&p, right_brace, len-(p-retsubj));
 
4756
 
 
4757
        if(len-(p-retsubj) > 0 && subject)
 
4758
          sstrncpy(&p, subject, len-(p-retsubj));
 
4759
 
 
4760
        if(ielemp && p > next_piece){
 
4761
            save = *p;
 
4762
            *p = '\0';
 
4763
            ielem = new_ielem(ielemp);
 
4764
            ielem->freedata = 1;
 
4765
            ielem->data = cpystr(next_piece);
 
4766
            ielem->datalen = strlen(next_piece);
 
4767
            *p = save;
 
4768
            next_piece = p;
 
4769
        }
 
4770
 
 
4771
        retsubj[len] = '\0';            /* just making sure */
 
4772
    }
 
4773
    else{
 
4774
        if(ielemp){
 
4775
            ielem = new_ielem(ielemp);
 
4776
            ielem->freedata = 1;
 
4777
            ielem->data = cpystr(subject);
 
4778
            ielem->datalen = strlen(subject);
 
4779
        }
 
4780
 
 
4781
        retsubj = cpystr(subject);
 
4782
    }
 
4783
 
 
4784
    if(braces){
 
4785
        if(left_brace)
 
4786
          fs_give((void **) &left_brace);
 
4787
 
 
4788
        if(right_brace)
 
4789
          fs_give((void **) &right_brace);
 
4790
    }
 
4791
 
 
4792
    return(retsubj);
 
4793
}
 
4794
 
 
4795
 
 
4796
/*
 
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.
 
4799
 */
 
4800
void
 
4801
from_str(IndexColType ctype, INDEXDATA_S *idata, int width, char *str, ICE_S *ice)
 
4802
{
 
4803
    char       *field, *newsgroups, *border, *p, *fptr = NULL, *q = NULL;
 
4804
    ADDRESS    *addr;
 
4805
    int         depth = 0, mult = 2;
 
4806
    PINETHRD_S *thd, *thdorig;
 
4807
 
 
4808
    if(THREADING()
 
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,
 
4816
                                                  thd && thd->next
 
4817
                                                  && get_lflag(idata->stream,
 
4818
                                                               NULL,idata->rawno,
 
4819
                                                               MN_COLL));
 
4820
 
 
4821
        fptr = str;
 
4822
 
 
4823
        if(thd)
 
4824
          while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
 
4825
            depth++;
 
4826
 
 
4827
        if(depth > 0){
 
4828
            if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
 
4829
              mult = 1;
 
4830
 
 
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);
 
4838
                        q = str + width-4;
 
4839
                    }
 
4840
                    else if(width >= 5 && depth < 1000){
 
4841
                        snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
 
4842
                        q = str + width-5;
 
4843
                    }
 
4844
                    else{
 
4845
                        snprintf(str, width+1, "%s", repeat_char(width, '.'), width);
 
4846
                        q = str;
 
4847
                    }
 
4848
 
 
4849
                    border = q;
 
4850
                    fptr = NULL;
 
4851
                }
 
4852
 
 
4853
                if(p + 1 < border){
 
4854
                    p[0] = p[1] = ' ';
 
4855
                    if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
 
4856
    /*
 
4857
     * WARNING!
 
4858
     * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
 
4859
     * is ascii.
 
4860
     */
 
4861
                        if(thd == thdorig && !thd->branch)
 
4862
                          p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
 
4863
                        else if(thd == thdorig || thd->branch)
 
4864
                          p[0] = '|';
 
4865
 
 
4866
                        if(thd == thdorig)
 
4867
                          p[1] = '-';
 
4868
                    }
 
4869
                }
 
4870
                else if(p < border){
 
4871
                    p[0] = ' ';
 
4872
                    if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
 
4873
    /*
 
4874
     * WARNING!
 
4875
     * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
 
4876
     * is ascii.
 
4877
     */
 
4878
                        if(thd == thdorig && !thd->branch)
 
4879
                          p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
 
4880
                        else if(thd == thdorig || thd->branch)
 
4881
                          p[0] = '|';
 
4882
                    }
 
4883
                }
 
4884
            }
 
4885
        }
 
4886
    }
 
4887
    else
 
4888
      fptr = str;
 
4889
 
 
4890
    if(fptr){
 
4891
        width = (str + width) - fptr;
 
4892
        switch(ctype){
 
4893
          case iFromTo:
 
4894
          case iFromToNotNews:
 
4895
            if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
 
4896
                if(width <= 4){
 
4897
                    strncpy(fptr, "To: ", width);
 
4898
                    fptr[width] = '\0';
 
4899
                    break;
 
4900
                }
 
4901
                else{
 
4902
                    if((field = ((addr = fetch_to(idata))
 
4903
                                 ? "To"
 
4904
                                 : (addr = fetch_cc(idata))
 
4905
                                 ? "Cc"
 
4906
                                 : NULL))
 
4907
                       && set_index_addr(idata, field, addr, "To: ",
 
4908
                                         width, fptr,
 
4909
                                         ice ? &ice->charset : NULL))
 
4910
                      break;
 
4911
 
 
4912
                    if(ctype == iFromTo &&
 
4913
                       (newsgroups = fetch_newsgroups(idata)) &&
 
4914
                       *newsgroups){
 
4915
                        snprintf(fptr, width, "To: %-*.*s", width-4, width-4,
 
4916
                                newsgroups);
 
4917
                        break;
 
4918
                    }
 
4919
 
 
4920
                    /* else fall thru to From: */
 
4921
                }
 
4922
            }
 
4923
            /* else fall thru to From: */
 
4924
 
 
4925
            if(idata->bogus)
 
4926
              break;
 
4927
 
 
4928
          case iFrom:
 
4929
            set_index_addr(idata, "From", fetch_from(idata),
 
4930
                           NULL, width, fptr, ice ? &ice->charset : NULL);
 
4931
            break;
 
4932
 
 
4933
          case iAddress:
 
4934
          case iMailbox:
 
4935
            if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
 
4936
                char *mb = NULL, *hst = NULL, *at = NULL;
 
4937
                size_t len;
 
4938
 
 
4939
                mb = addr->mailbox;
 
4940
                if(ctype == iAddress && addr->host && addr->host[0]
 
4941
                   && addr->host[0] != '.'){
 
4942
                    at = "@";
 
4943
                    hst = addr->host;
 
4944
                }
 
4945
 
 
4946
                len = strlen(mb);
 
4947
                if(!at || width <= len)
 
4948
                  snprintf(fptr, width+1, "%-*.*s", width, width, mb);
 
4949
                else
 
4950
                  snprintf(fptr, width+1, "%s@%-*.*s", mb, width-len-1, width-len-1, hst);
 
4951
            }
 
4952
 
 
4953
            break;
 
4954
        }
 
4955
    }
 
4956
}
 
4957
 
 
4958
 
 
4959
/*
 
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.
 
4962
 */
 
4963
void
 
4964
set_ielem_widths_in_field(IFIELD_S *ifield)
 
4965
{
 
4966
    IELEM_S *ielem = NULL;
 
4967
    int      datawidth, fmtwidth;
 
4968
 
 
4969
    if(!ifield)
 
4970
      return;
 
4971
 
 
4972
    fmtwidth = ifield->width;
 
4973
 
 
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));
 
4977
            assert(0);
 
4978
        }
 
4979
 
 
4980
        datawidth = (int) utf8_width(ielem->data);
 
4981
        if(datawidth >= fmtwidth || !ielem->next){
 
4982
            set_print_format(ielem, fmtwidth, ifield->leftadj);
 
4983
            fmtwidth = 0;
 
4984
        }
 
4985
        else{
 
4986
            set_print_format(ielem, datawidth, ifield->leftadj);
 
4987
            fmtwidth -= datawidth;
 
4988
        }
 
4989
    }
 
4990
}
 
4991
 
 
4992
 
 
4993
/*
 
4994
 * Simple hash function from K&R 2nd edition, p. 144.
 
4995
 *
 
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.
 
4999
 */
 
5000
unsigned long
 
5001
line_hash(char *s)
 
5002
{
 
5003
    unsigned long hashval;
 
5004
 
 
5005
    for(hashval = 0; *s != '\0'; s++)
 
5006
      hashval = *s + 31 * hashval;
 
5007
 
 
5008
    hashval = hashval % LINE_HASH_N;
 
5009
 
 
5010
    if(!hashval)
 
5011
      hashval++;
 
5012
 
 
5013
    return(hashval);
 
5014
}
 
5015
 
 
5016
 
 
5017
/*
 
5018
 * Returns nonzero if considered hidden, 0 if not considered hidden.
 
5019
 */
 
5020
int
 
5021
msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
 
5022
{
 
5023
    int ret;
 
5024
 
 
5025
    if(flags & MH_ANYTHD){
 
5026
        ret = ((any_lflagged(msgmap, MN_HIDE) > 0)
 
5027
               && get_lflag(stream, msgmap, msgno, MN_HIDE));
 
5028
    }
 
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));
 
5032
    }
 
5033
    else{
 
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));
 
5038
        }
 
5039
        else if(THRD_INDX()){
 
5040
            /*
 
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.
 
5045
             */
 
5046
            if(get_lflag(stream, msgmap, msgno, MN_CHID))       /* not top */
 
5047
              ret = 1;
 
5048
            else{
 
5049
                unsigned long rawno;
 
5050
                PINETHRD_S   *thrd = NULL;
 
5051
 
 
5052
                rawno = mn_m2raw(msgmap, msgno);
 
5053
                if(rawno)
 
5054
                  thrd = fetch_thread(stream, rawno);
 
5055
 
 
5056
                ret = !thread_has_some_visible(stream, thrd);
 
5057
            }
 
5058
        }
 
5059
        else{
 
5060
            ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
 
5061
                   && get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
 
5062
        }
 
5063
    }
 
5064
    
 
5065
    dprint((10,
 
5066
               "msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
 
5067
 
 
5068
    return(ret);
 
5069
}
 
5070
 
 
5071
 
 
5072
void
 
5073
adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
 
5074
{
 
5075
    long n, cur;
 
5076
    int  dir;
 
5077
 
 
5078
    cur = mn_get_cur(msgmap);
 
5079
 
 
5080
    /* if current is hidden, adjust */
 
5081
    if(cur >= 1L && cur <= mn_get_total(msgmap)
 
5082
       && msgline_hidden(stream, msgmap, cur, 0)){
 
5083
 
 
5084
        dir = mn_get_revsort(msgmap) ? -1 : 1;
 
5085
 
 
5086
        for(n = cur;
 
5087
            ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
 
5088
            && msgline_hidden(stream, msgmap, n, 0);
 
5089
            n -= dir)
 
5090
          ;
 
5091
        
 
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 */
 
5095
            for(n = cur;
 
5096
                ((dir == 1 && n >= 1L)
 
5097
                 || (dir == -1 && n <= mn_get_total(msgmap)))
 
5098
                && msgline_hidden(stream, msgmap, n, 0);
 
5099
                n += dir)
 
5100
              ;
 
5101
 
 
5102
            if((dir == -1 && n >= 1L)
 
5103
               || (dir == 1 && n <= mn_get_total(msgmap)))
 
5104
              mn_reset_cur(msgmap, n);
 
5105
            /* else trouble! */
 
5106
        }
 
5107
    }
 
5108
}
 
5109
 
 
5110
 
 
5111
void
 
5112
setup_for_index_index_screen(void)
 
5113
{
 
5114
    format_index_line = format_index_index_line;
 
5115
    setup_header_widths = setup_index_header_widths;
 
5116
}
 
5117
 
 
5118
 
 
5119
void
 
5120
setup_for_thread_index_screen(void)
 
5121
{
 
5122
    format_index_line = format_thread_index_line;
 
5123
    setup_header_widths = setup_thread_header_widths;
 
5124
}
 
5125
 
 
5126
 
 
5127
unsigned long
 
5128
ice_hash(ICE_S *ice)
 
5129
{
 
5130
    char buf[MAX_SCREEN_COLS+1];
 
5131
 
 
5132
    buf[0] = '\0';
 
5133
 
 
5134
    if(ice)
 
5135
      simple_index_line(buf, sizeof(buf), ps_global->ttyo->screen_cols, ice, 0L);
 
5136
 
 
5137
    buf[sizeof(buf) - 1] = '\0';
 
5138
 
 
5139
    return(line_hash(buf));
 
5140
}
 
5141
 
 
5142
 
 
5143
char *
 
5144
left_adjust(int width)
 
5145
{
 
5146
    return(format_str(width, 1));
 
5147
}
 
5148
 
 
5149
 
 
5150
char *
 
5151
right_adjust(int width)
 
5152
{
 
5153
    return(format_str(width, 0));
 
5154
}
 
5155
 
 
5156
 
 
5157
/*
 
5158
 * Returns allocated and filled in format string.
 
5159
 */
 
5160
char *
 
5161
format_str(int width, int left)
 
5162
{
 
5163
    char  *format;
 
5164
    size_t len;
 
5165
 
 
5166
    len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
 
5167
    format = (char *) fs_get(len + 1);
 
5168
    copy_format_str(width, left, format, len);
 
5169
    format[len] = '\0';
 
5170
 
 
5171
    return(format);
 
5172
}
 
5173
 
 
5174
 
 
5175
/*
 
5176
 * Put the left or right adjusted format string of width width into
 
5177
 * dest. Dest is of size n+1.
 
5178
 */
 
5179
char *
 
5180
copy_format_str(int width, int left, char *dest, int n)
 
5181
{
 
5182
    char  *p;
 
5183
    size_t len;
 
5184
 
 
5185
    p = int2string(width);
 
5186
 
 
5187
    snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
 
5188
 
 
5189
    dest[n] = '\0';
 
5190
 
 
5191
    return(dest);
 
5192
}
 
5193
 
 
5194
 
 
5195
/*
 
5196
 * Sets up the print_format string to be width wide with left or right
 
5197
 * adjust. Takes care of memory freeing and allocation.
 
5198
 */
 
5199
void
 
5200
set_print_format(IELEM_S *ielem, int width, int leftadj)
 
5201
{
 
5202
    if(ielem){
 
5203
        ielem->wid = width;
 
5204
 
 
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);
 
5211
            }
 
5212
 
 
5213
            copy_format_str(width, leftadj, ielem->print_format,
 
5214
                            PRINT_FORMAT_LEN(width,leftadj));
 
5215
        }
 
5216
        else{
 
5217
            ielem->print_format = leftadj ? left_adjust(width)
 
5218
                                          : right_adjust(width);
 
5219
            ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
 
5220
        }
 
5221
    }
 
5222
}