summaryrefslogtreecommitdiff
path: root/pith/mailindx.c
diff options
context:
space:
mode:
Diffstat (limited to 'pith/mailindx.c')
-rw-r--r--pith/mailindx.c6434
1 files changed, 6434 insertions, 0 deletions
diff --git a/pith/mailindx.c b/pith/mailindx.c
new file mode 100644
index 00000000..f3b68bfb
--- /dev/null
+++ b/pith/mailindx.c
@@ -0,0 +1,6434 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mailindx.h"
+#include "../pith/mailview.h"
+#include "../pith/flag.h"
+#include "../pith/icache.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/strlst.h"
+#include "../pith/status.h"
+#include "../pith/mailcmd.h"
+#include "../pith/search.h"
+#include "../pith/charset.h"
+#include "../pith/reply.h"
+#include "../pith/bldaddr.h"
+#include "../pith/addrstring.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/pattern.h"
+#include "../pith/sequence.h"
+#include "../pith/color.h"
+#include "../pith/stream.h"
+#include "../pith/string.h"
+#include "../pith/send.h"
+#include "../pith/options.h"
+#include "../pith/ablookup.h"
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+/*
+ * pointers to formatting functions
+ */
+ICE_S *(*format_index_line)(INDEXDATA_S *);
+void (*setup_header_widths)(MAILSTREAM *);
+
+/*
+ * pointer to optional load_overview functionality
+ */
+void (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
+
+/*
+ * pointer to hook for saving index format state
+ */
+void (*pith_opt_save_index_state)(int);
+
+/*
+ * hook to allow caller to insert cue that indicates a condensed
+ * thread relationship cue
+ */
+int (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, size_t *, int, int);
+int (*pith_opt_truncate_sfstr)(void);
+
+
+/*
+ * Internal prototypes
+ */
+void setup_for_thread_index_screen(void);
+ICE_S *format_index_index_line(INDEXDATA_S *);
+ICE_S *format_thread_index_line(INDEXDATA_S *);
+int set_index_addr(INDEXDATA_S *, char *, ADDRESS *, char *, int, char *);
+int ctype_is_fixed_length(IndexColType);
+void setup_index_header_widths(MAILSTREAM *);
+void setup_thread_header_widths(MAILSTREAM *);
+int parse_index_format(char *, INDEX_COL_S **);
+int index_in_overview(MAILSTREAM *);
+ADDRESS *fetch_from(INDEXDATA_S *);
+ADDRESS *fetch_sender(INDEXDATA_S *);
+char *fetch_newsgroups(INDEXDATA_S *);
+char *fetch_subject(INDEXDATA_S *);
+char *fetch_date(INDEXDATA_S *);
+long fetch_size(INDEXDATA_S *);
+BODY *fetch_body(INDEXDATA_S *);
+char *fetch_firsttext(INDEXDATA_S *idata, int);
+char *fetch_header(INDEXDATA_S *idata, char *hdrname);
+void subj_str(INDEXDATA_S *, char *, size_t, SubjKW, int, ICE_S *);
+void key_str(INDEXDATA_S *, SubjKW, ICE_S *);
+void header_str(INDEXDATA_S *, HEADER_TOK_S *, ICE_S *);
+void prio_str(INDEXDATA_S *, IndexColType, ICE_S *);
+void from_str(IndexColType, INDEXDATA_S *, char *, size_t, ICE_S *);
+int day_of_week(struct date *);
+int day_of_year(struct date *);
+unsigned long ice_hash(ICE_S *);
+char *left_adjust(int);
+char *right_adjust(int);
+char *format_str(int, int);
+char *copy_format_str(int, int, char *, int);
+void set_print_format(IELEM_S *, int, int);
+void set_ielem_widths_in_field(IFIELD_S *);
+
+
+#define BIGWIDTH 2047
+
+
+/*----------------------------------------------------------------------
+ Initialize the index_disp_format array in ps_global from this
+ format string.
+
+ Args: format -- the string containing the format tokens
+ answer -- put the answer here, free first if there was a previous
+ value here
+ ----*/
+void
+init_index_format(char *format, INDEX_COL_S **answer)
+{
+ char *p;
+ int i, w, monabb_width = 0, column = 0;
+
+ /*
+ * Record the fact that SCORE appears in some index format. This
+ * is a heavy-handed approach. It will stick at 1 if any format ever
+ * contains score during this session. This is ok since it will just
+ * cause recalculation if wrong and these things rarely change much.
+ */
+ if(!ps_global->a_format_contains_score && format
+ && strstr(format, "SCORE")){
+ ps_global->a_format_contains_score = 1;
+ /* recalculate need for scores */
+ scores_are_used(SCOREUSE_INVALID);
+ }
+
+ set_need_format_setup(ps_global->mail_stream);
+ /* if custom format is specified, try it, else go with default */
+ if(!(format && *format && parse_index_format(format, answer))){
+ static INDEX_COL_S answer_default[] = {
+ {iStatus, Fixed, 3},
+ {iMessNo, WeCalculate},
+ {iSDateTime24, WeCalculate},
+ {iFromTo, Percent, 33}, /* percent of rest */
+ {iSizeNarrow, WeCalculate},
+ {iSubjKey, Percent, 67},
+ {iNothing}
+ };
+
+ if(*answer)
+ free_index_format(answer);
+
+ *answer = (INDEX_COL_S *)fs_get(sizeof(answer_default));
+ memcpy(*answer, answer_default, sizeof(answer_default));
+ }
+
+ /*
+ * Test to see how long the month abbreviations are.
+ */
+ for(i = 1; i <= 12; i++){
+ p = month_abbrev_locale(i);
+ monabb_width = MAX(utf8_width(p), monabb_width);
+ }
+
+ monabb_width = MIN(MAX(2, monabb_width), 5);
+
+ /*
+ * Fill in req_width's for WeCalculate items.
+ */
+ for(column = 0; (*answer)[column].ctype != iNothing; column++){
+
+ /* don't use strftime if we're not trying to use the LC_TIME stuff */
+ if(F_ON(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ switch((*answer)[column].ctype){
+ case iSDate:
+ (*answer)[column].ctype = iS1Date;
+ break;
+ case iSDateTime:
+ (*answer)[column].ctype = iSDateTimeS1;
+ break;
+ case iSDateTime24:
+ (*answer)[column].ctype = iSDateTimeS124;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if((*answer)[column].wtype == WeCalculate){
+ switch((*answer)[column].ctype){
+ case iPrio:
+ case iPrioBang:
+ case iAtt:
+ (*answer)[column].req_width = 1;
+ break;
+ case iYear2Digit:
+ case iDay:
+ case iMon:
+ case iDay2Digit:
+ case iMon2Digit:
+ case iArrow:
+ case iKeyInit:
+ (*answer)[column].req_width = 2;
+ break;
+ case iStatus:
+ case iMessNo:
+ case iInit:
+ (*answer)[column].req_width = 3;
+ break;
+ case iYear:
+ case iDayOrdinal:
+ case iSIStatus:
+ (*answer)[column].req_width = 4;
+ break;
+ case iTime24:
+ case iTimezone:
+ case iSizeNarrow:
+ case iKey:
+ (*answer)[column].req_width = 5;
+ break;
+ case iFStatus:
+ case iIStatus:
+ case iScore:
+ (*answer)[column].req_width = 6;
+ break;
+ case iTime12:
+ case iSTime:
+ case iKSize:
+ case iSize:
+ case iPrioAlpha:
+ (*answer)[column].req_width = 7;
+ break;
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIsoS:
+ case iSizeComma:
+ (*answer)[column].req_width = 8;
+ break;
+ case iMonAbb:
+ (*answer)[column].req_width = monabb_width;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iDayOfWeekAbb:
+ {
+ w = 0;
+
+ /*
+ * Test to see how long it is.
+ */
+ for(i = 0; i < 7; i++){
+ p = day_abbrev_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(2, w), 5);
+ }
+ break;
+ case iDate:
+ (*answer)[column].req_width = monabb_width + 3;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iMonLong:
+ {
+ w = 0;
+
+ /*
+ * Test to see how long it is.
+ */
+ for(i = 1; i <= 12; i++){
+ p = month_name_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(3, w), 12);
+ }
+ break;
+ case iDayOfWeek:
+ {
+ w = 0;
+
+ for(i = 0; i < 7; i++){
+ p = day_name_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(3, w), 12);
+ }
+ break;
+ case iSDate:
+ case iSDateTime:
+ case iSDateTime24:
+ case iPrefDate:
+ case iPrefTime:
+ case iPrefDateTime:
+ {
+ /*
+ * Format a date to see how long it is.
+ * Make it as least as long as "Yesterday".
+ * We should really use the width of the longest
+ * of the translated yesterdays and friends but...
+ */
+ struct tm tm;
+ int len = 20;
+ char ss[100];
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = 106;
+ tm.tm_mon = 11;
+ tm.tm_mday = 31;
+ tm.tm_hour = 3;
+ tm.tm_min = 23;
+ tm.tm_wday = 3;
+ switch((*answer)[column].ctype){
+ case iPrefTime:
+ our_strftime(ss, sizeof(ss), "%X", &tm);
+ break;
+ case iPrefDateTime:
+ our_strftime(ss, sizeof(ss), "%c", &tm);
+ len = 32;
+ break;
+ default:
+ our_strftime(ss, sizeof(ss), "%x", &tm);
+ break;
+ }
+ (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), len);
+ }
+
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+
+ case iDescripSize:
+ case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ /*
+ * These SDates are 8 wide but they need to be 9 for "Yesterday".
+ */
+ (*answer)[column].req_width = 9;
+ break;
+ case iDateIso:
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ (*answer)[column].req_width = 10;
+ break;
+ case iLDate:
+ (*answer)[column].req_width = 12;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iRDate:
+ (*answer)[column].req_width = 16;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ calc_extra_hdrs();
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
+ (void *) get_extra_hdrs());
+}
+
+
+void
+reset_index_format(void)
+{
+ long rflags = ROLE_DO_OTHER;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ int we_set_it = 0;
+
+ if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
+ if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL,
+ NULL, NULL, SE_NOSERVER|SE_NOPREFETCH))
+ break;
+ }
+
+ if(pat && pat->action && !pat->action->bogus
+ && pat->action->index_format){
+ we_set_it++;
+ init_index_format(pat->action->index_format,
+ &ps_global->index_disp_format);
+ }
+ }
+
+ if(!we_set_it)
+ init_index_format(ps_global->VAR_INDEX_FORMAT,
+ &ps_global->index_disp_format);
+}
+
+
+void
+free_index_format(INDEX_COL_S **disp_format)
+{
+ INDEX_COL_S *cdesc = NULL;
+
+ if(disp_format && *disp_format){
+ for(cdesc = (*disp_format); cdesc->ctype != iNothing; cdesc++)
+ if(cdesc->hdrtok)
+ free_hdrtok(&cdesc->hdrtok);
+
+ fs_give((void **) disp_format);
+ }
+}
+
+
+HEADER_TOK_S *
+new_hdrtok(char *hdrname)
+{
+ HEADER_TOK_S *hdrtok;
+
+ hdrtok = (HEADER_TOK_S *) fs_get(sizeof(HEADER_TOK_S));
+ memset(hdrtok, 0, sizeof(HEADER_TOK_S));
+ hdrtok->hdrname = hdrname ? cpystr(hdrname) : NULL;
+ hdrtok->fieldnum = 0;
+ hdrtok->adjustment = Left;
+ hdrtok->fieldsepcnt = 1;
+ hdrtok->fieldseps = cpystr(" ");
+
+ return(hdrtok);
+}
+
+
+void
+free_hdrtok(HEADER_TOK_S **hdrtok)
+{
+ if(hdrtok && *hdrtok){
+ if((*hdrtok)->hdrname)
+ fs_give((void **) &(*hdrtok)->hdrname);
+
+ if((*hdrtok)->fieldseps)
+ fs_give((void **) &(*hdrtok)->fieldseps);
+
+ fs_give((void **) hdrtok);
+ }
+}
+
+
+/* popular ones first to make it slightly faster */
+static INDEX_PARSE_T itokens[] = {
+ {"STATUS", iStatus, FOR_INDEX},
+ {"MSGNO", iMessNo, FOR_INDEX},
+ {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FROMORTO", iFromTo, FOR_INDEX},
+ {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX},
+ {"SIZE", iSize, FOR_INDEX},
+ {"SIZECOMMA", iSizeComma, FOR_INDEX},
+ {"SIZENARROW", iSizeNarrow, FOR_INDEX},
+ {"KSIZE", iKSize, FOR_INDEX},
+ {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FULLSTATUS", iFStatus, FOR_INDEX},
+ {"IMAPSTATUS", iIStatus, FOR_INDEX},
+ {"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX},
+ {"SUBJKEY", iSubjKey, FOR_INDEX},
+ {"SUBJKEYINIT", iSubjKeyInit, FOR_INDEX},
+ {"SUBJECTTEXT", iSubjectText, FOR_INDEX},
+ {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX},
+ {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX},
+ {"OPENINGTEXT", iOpeningText, FOR_INDEX},
+ {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX},
+ {"KEY", iKey, FOR_INDEX},
+ {"KEYINIT", iKeyInit, FOR_INDEX},
+ {"DESCRIPSIZE", iDescripSize, FOR_INDEX},
+ {"ATT", iAtt, FOR_INDEX},
+ {"SCORE", iScore, FOR_INDEX},
+ {"PRIORITY", iPrio, FOR_INDEX},
+ {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX},
+ {"PRIORITY!", iPrioBang, FOR_INDEX},
+ {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWSANDTO", iNewsAndTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"RECIPSANDNEWS", iRecipsAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFDATETIME", iCurPrefDateTime,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"HEADER", iHeader, FOR_INDEX},
+ {"TEXT", iText, FOR_INDEX},
+ {"ARROW", iArrow, FOR_INDEX},
+ {"NEWLINE", iNewLine, FOR_REPLY_INTRO},
+ {"CURSORPOS", iCursorPos, FOR_TEMPLATE},
+ {NULL, iNothing, FOR_NOTHING}
+};
+
+INDEX_PARSE_T *
+itoken(int i)
+{
+ return((i < sizeof(itokens) && itokens[i].name) ? &itokens[i] : NULL);
+}
+
+
+/*
+ * Args txt -- The token being checked begins at the beginning
+ * of txt. The end of the token is delimited by a null, or
+ * white space, or an underscore if DELIM_USCORE is set,
+ * or a left paren if DELIM_PAREN is set.
+ * flags -- Flags contains the what_for value, and DELIM_ values.
+ *
+ * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
+ */
+INDEX_PARSE_T *
+itoktype(char *txt, int flags)
+{
+ INDEX_PARSE_T *pt;
+ char token[100 + 1];
+ char *v, *w;
+
+ /*
+ * Separate a copy of the possible token out of txt.
+ */
+ v = txt;
+ w = token;
+ while(w < token+sizeof(token)-1 &&
+ *v &&
+ !(!(*v & 0x80) && isspace((unsigned char)*v)) &&
+ !(flags & DELIM_USCORE && *v == '_') &&
+ !(flags & DELIM_PAREN && *v == '(') &&
+ !(flags & DELIM_COLON && *v == ':'))
+ *w++ = *v++;
+
+ *w = '\0';
+
+ for(pt = itokens; pt->name; pt++)
+ if(pt->what_for & flags && !strucmp(pt->name, token))
+ return(pt);
+
+ return(NULL);
+}
+
+
+int
+parse_index_format(char *format_str, INDEX_COL_S **answer)
+{
+ int i, column = 0;
+ char *p, *q;
+ INDEX_PARSE_T *pt;
+ INDEX_COL_S cdesc[200]; /* plenty of temp storage for answer */
+
+ memset((void *)cdesc, 0, sizeof(cdesc));
+
+ p = format_str;
+ while(p && *p && column < 200-1){
+ /* skip leading white space for next word */
+ p = skip_white_space(p);
+ pt = itoktype(p, FOR_INDEX | DELIM_PAREN | DELIM_COLON);
+
+ /* ignore unrecognized word */
+ if(!pt){
+ for(q = p; *p && !isspace((unsigned char)*p); p++)
+ ;
+
+ if(*p)
+ *p++ = '\0';
+
+ dprint((1,
+ "parse_index_format: unrecognized token: %s\n",
+ q ? q : "?"));
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Unrecognized word in index-format: %s"), q);
+ continue;
+ }
+
+ cdesc[column].ctype = pt->ctype;
+
+ if(pt->ctype == iHeader || pt->ctype == iText){
+ /*
+ * iHeader field has special syntax.
+ *
+ * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
+ *
+ * where width is the regular width or percentage width or
+ * left out for default width, fieldnum defaults to 0 for
+ * whole thing, 1 for first field, ...
+ * and field_separators is a list of characters which separate
+ * the fields. The whole parenthesized part is optional. If used
+ * the arguments can be dropped from the right, so
+ *
+ * HEADER:hdrname or
+ * HEADER:hdrname(10) or
+ * HEADER:hdrname(10%) or
+ * HEADER:hdrname(10,2) or
+ * HEADER:hdrname(,2) or
+ * HEADER:hdrname(10,2, ) or
+ * HEADER:hdrname(10,2,\,:) or
+ * HEADER:hdrname(10,2,\,:,R)
+ *
+ * iText field uses the hdrtok field for convenience. It has syntax
+ *
+ * TEXT:text or
+ * TEXT:text(10) or
+ * TEXT:text(10%)
+ *
+ * and the literal text goes into the index line. It is also special
+ * because there is no 1 column space after this field.
+ */
+
+ /* skip over name */
+ p += strlen(pt->name);
+
+ /* look for header name */
+ if(*p == ':'){
+ char *w, hdrname[200];
+
+ hdrname[0] = '\0';
+ w = hdrname;
+ p++;
+ if(*p == '\"'){ /* quoted name */
+ p++;
+ while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
+ if(*p == '\\')
+ p++;
+
+ *w++ = *p++;
+ }
+
+ *w = '\0';
+ if(*p == '\"')
+ p++;
+ }
+ else{
+ while(w < hdrname + sizeof(hdrname)-1 &&
+ !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
+ *p != '(')
+ *w++ = *p++;
+
+ *w = '\0';
+ }
+
+ if(hdrname[0]){
+ cdesc[column].hdrtok = new_hdrtok(hdrname);
+ }
+ else{
+ if(pt->ctype == iHeader){
+ dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
+ }
+ else{
+ dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
+ }
+ }
+ }
+ else{
+ if(pt->ctype == iHeader){
+ dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
+ }
+ else{
+ dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
+ }
+
+ /* skip over rest of bogus config */
+ while(!(!(*p & 0x80) && isspace((unsigned char)*p)) && *p != '(')
+ p++;
+ }
+ }
+ else{
+ /* skip over name and look for parens */
+ p += strlen(pt->name);
+ }
+
+ if(*p == '('){
+ p++;
+ q = p;
+ while(p && *p && isdigit((unsigned char) *p))
+ p++;
+
+ if(pt->ctype == iHeader){
+ /* first argument is width or width percentage, like for others */
+ if(p && *p && (*p == ')' || *p == ',')){
+ if(p > q){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = atoi(q);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+ else if(p && *p && *p == '%' && p > q){
+ cdesc[column].wtype = Percent;
+ cdesc[column].req_width = atoi(q);
+ p++;
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+
+ /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
+ if(p && *p && *p == ','){
+ p++;
+ /* no space allowed between arguments */
+ if(*p && isdigit((unsigned char) *p)){
+ q = p;
+ while(*p && isdigit((unsigned char) *p))
+ p++;
+
+ cdesc[column].hdrtok->fieldnum = atoi(q);
+
+ /*
+ * Optional 3rd argument is field separators.
+ * Comma is \, and backslash is \\.
+ */
+ if(*p == ','){
+ int j;
+
+ p++;
+ /* don't use default */
+ if(*p && *p != ')' && *p != ',' && cdesc[column].hdrtok->fieldseps)
+ cdesc[column].hdrtok->fieldseps[0] = '\0';
+
+ j = 0;
+ if(*p == '\"' && strchr(p+1, '\"')){
+ p++;
+ while(*p && *p != ')' && *p != '\"' && *p != ','){
+ if(cdesc[column].hdrtok->fieldseps)
+ fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(cdesc[column].hdrtok->fieldseps){
+ cdesc[column].hdrtok->fieldseps[j++] = *p++;
+ cdesc[column].hdrtok->fieldseps[j] = '\0';
+ cdesc[column].hdrtok->fieldsepcnt = j;
+ }
+ }
+
+ if(*p == '\"')
+ p++;
+ }
+ else{
+ while(*p && *p != ')' && *p != ','){
+ if(cdesc[column].hdrtok->fieldseps)
+ fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(cdesc[column].hdrtok->fieldseps){
+ cdesc[column].hdrtok->fieldseps[j++] = *p++;
+ cdesc[column].hdrtok->fieldseps[j] = '\0';
+ cdesc[column].hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+
+ /* optional 4th argument, left or right adjust */
+ if(*p == ','){
+ p++;
+ if(*p == 'L' || *p == 'l')
+ cdesc[column].hdrtok->adjustment = Left;
+ else if(*p == 'R' || *p == 'r')
+ cdesc[column].hdrtok->adjustment = Right;
+ else{
+ dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p ? p : "<null>"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 4th argument should be L or R");
+ }
+ }
+ }
+ }
+ else{
+ dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p ? p : "<null>"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
+ }
+ }
+ }
+ else{
+ if(p && *p && *p == ')' && p > q){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = atoi(q);
+ }
+ else if(p && *p && *p == '%' && p > q){
+ cdesc[column].wtype = Percent;
+ cdesc[column].req_width = atoi(q);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+ }
+ else{
+ /* if they left out width for iText we can figure it out */
+ if(pt->ctype == iText && cdesc[column].hdrtok && cdesc[column].hdrtok->hdrname){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = utf8_width(cdesc[column].hdrtok->hdrname);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+
+ column++;
+ /* skip text at end of word */
+ while(p && *p && !isspace((unsigned char)*p))
+ p++;
+ }
+
+ /* if, after all that, we didn't find anything recognizable, bitch */
+ if(!column){
+ dprint((1, "Completely unrecognizable index-format\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Configured \"index-format\" unrecognizable. Using default."));
+ return(0);
+ }
+
+ /* Finish with Nothing column */
+ cdesc[column].ctype = iNothing;
+
+ /* free up old answer */
+ if(*answer)
+ free_index_format(answer);
+
+ /* allocate space for new answer */
+ *answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
+ memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
+ /* copy answer to real place */
+ for(i = 0; i <= column; i++)
+ (*answer)[i] = cdesc[i];
+
+ return(1);
+}
+
+
+/*
+ * These types are basically fixed in width.
+ * The order is slightly significant. The ones towards the front of the
+ * list get space allocated sooner than the ones at the end of the list.
+ */
+static IndexColType fixed_ctypes[] = {
+ iMessNo, iStatus, iFStatus, iIStatus, iSIStatus,
+ iDate, iSDate, iSDateTime, iSDateTime24,
+ iSTime, iLDate,
+ iS1Date, iS2Date, iS3Date, iS4Date, iDateIso, iDateIsoS,
+ iSDateIso, iSDateIsoS,
+ iSDateS1, iSDateS2, iSDateS3, iSDateS4,
+ iSDateTimeIso, iSDateTimeIsoS,
+ iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
+ iSDateTimeIso24, iSDateTimeIsoS24,
+ iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
+ iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize,
+ iPrio, iPrioBang, iPrioAlpha, iInit,
+ iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit,
+ iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek
+};
+
+
+int
+ctype_is_fixed_length(IndexColType ctype)
+{
+ int j;
+
+ for(j = 0; ; j++){
+ if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
+ break;
+
+ if(ctype == fixed_ctypes[j])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+ Setup the widths of the various columns in the index display
+ ----*/
+void
+setup_index_header_widths(MAILSTREAM *stream)
+{
+ int colspace; /* for reserving space between columns */
+ int j, some_to_calculate;
+ int space_left, screen_width, fix;
+ int keep_going, tot_pct, was_sl;
+ long max_msgno;
+ WidthType wtype;
+ INDEX_COL_S *cdesc;
+
+ max_msgno = mn_get_total(ps_global->msgmap);
+
+ dprint((8, "=== setup_index_header_widths() ===\n"));
+
+ clear_icache_flags(stream);
+ screen_width = ps_global->ttyo->screen_cols;
+ space_left = screen_width;
+ some_to_calculate = 0;
+ colspace = -1;
+
+ /*
+ * Calculate how many fields there are so we know how many spaces
+ * between columns to reserve. Fill in Fixed widths now. Reserve
+ * special case WeCalculate with non-zero req_widths before doing
+ * Percent cases below.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+
+ if(cdesc->wtype == Fixed){
+ cdesc->width = cdesc->req_width;
+ if(cdesc->width > 0)
+ colspace++;
+ }
+ else if(cdesc->wtype == Percent){
+ cdesc->width = 0; /* calculated later */
+ colspace++;
+ }
+ else{ /* WeCalculate */
+ cdesc->width = cdesc->req_width; /* reserve this for now */
+ some_to_calculate++;
+ colspace++;
+ }
+
+ /* no space after iText */
+ if(cdesc->ctype == iText)
+ colspace--;
+
+ space_left -= cdesc->width;
+ }
+
+ colspace = MAX(colspace, 0);
+
+ space_left -= colspace; /* space between columns */
+
+ ps_global->display_keywords_in_subject = 0;
+ ps_global->display_keywordinits_in_subject = 0;
+
+ /*
+ * Set the actual lengths for the fixed width fields and set up
+ * the left or right adjustment for everything.
+ * There should be a case setting actual_length for all of the types
+ * in fixed_ctypes.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+
+ wtype = cdesc->wtype;
+
+ if(cdesc->ctype == iSubjKey || cdesc->ctype == iSubjKeyText)
+ ps_global->display_keywords_in_subject = 1;
+ else if(cdesc->ctype == iSubjKeyInit || cdesc->ctype == iSubjKeyInitText)
+ ps_global->display_keywordinits_in_subject = 1;
+
+ if(wtype == WeCalculate || wtype == Percent || cdesc->width != 0){
+
+ switch(cdesc->ctype){
+ case iSDate: case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iSTime:
+ set_format_includes_smartdate(stream);
+ break;
+
+ default:
+ break;
+ }
+
+ if(ctype_is_fixed_length(cdesc->ctype)){
+ switch(cdesc->ctype){
+ case iPrio:
+ case iPrioBang:
+ case iAtt:
+ cdesc->actual_length = 1;
+ cdesc->adjustment = Left;
+ break;
+
+ case iYear2Digit:
+ case iDay2Digit:
+ case iMon2Digit:
+ cdesc->actual_length = 2;
+ cdesc->adjustment = Left;
+ break;
+
+ case iArrow:
+ cdesc->actual_length = 2;
+ cdesc->adjustment = Right;
+ break;
+
+ case iStatus:
+ case iInit:
+ cdesc->actual_length = 3;
+ cdesc->adjustment = Left;
+ break;
+
+ case iMessNo:
+ set_format_includes_msgno(stream);
+ if(max_msgno < 1000)
+ cdesc->actual_length = 3;
+ else if(max_msgno < 10000)
+ cdesc->actual_length = 4;
+ else if(max_msgno < 100000)
+ cdesc->actual_length = 5;
+ else
+ cdesc->actual_length = 6;
+
+ cdesc->adjustment = Right;
+ break;
+
+ case iYear:
+ case iSIStatus:
+ cdesc->actual_length = 4;
+ cdesc->adjustment = Left;
+ break;
+
+ case iTime24:
+ case iTimezone:
+ cdesc->actual_length = 5;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSizeNarrow:
+ cdesc->actual_length = 5;
+ cdesc->adjustment = Right;
+ break;
+
+ case iFStatus:
+ case iIStatus:
+ cdesc->actual_length = 6;
+ cdesc->adjustment = Left;
+ break;
+
+ case iScore:
+ cdesc->actual_length = 6;
+ cdesc->adjustment = Right;
+ break;
+
+ case iTime12:
+ case iSize:
+ case iKSize:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Right;
+ break;
+
+ case iSTime:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Left;
+ break;
+
+ case iPrioAlpha:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Left;
+ break;
+
+
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIsoS:
+ cdesc->actual_length = 8;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSizeComma:
+ cdesc->actual_length = 8;
+ cdesc->adjustment = Right;
+ break;
+
+ case iSDate:
+ case iSDateTime:
+ case iSDateTime24:
+ case iMonAbb:
+ case iDayOfWeekAbb:
+ case iDayOfWeek:
+ case iDate:
+ case iMonLong:
+ cdesc->actual_length = cdesc->req_width;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTimeIsoS:
+ case iSDateTimeIsoS24:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ if(cdesc->ctype == iSDateIso
+ || cdesc->ctype == iSDateTimeIso
+ || cdesc->ctype == iSDateTimeIso24)
+ cdesc->actual_length = 10;
+ else
+ cdesc->actual_length = 9;
+
+ cdesc->adjustment = Left;
+ break;
+
+ case iDescripSize:
+ cdesc->actual_length = 9;
+ cdesc->adjustment = Right;
+ break;
+
+ case iDateIso:
+ cdesc->actual_length = 10;
+ cdesc->adjustment = Left;
+ break;
+
+ case iLDate:
+ cdesc->actual_length = 12;
+ cdesc->adjustment = Left;
+ break;
+
+ default:
+ panic("Unhandled fixed case in setup_index_header");
+ break;
+ }
+ }
+ else if(cdesc->ctype == iHeader)
+ cdesc->adjustment = cdesc->hdrtok ? cdesc->hdrtok->adjustment : Left;
+ else
+ cdesc->adjustment = Left;
+ }
+ }
+
+ if(ps_global->display_keywords_in_subject)
+ ps_global->display_keywordinits_in_subject = 0;
+
+ /* if have reserved unneeded space for size, give it back */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ if(cdesc->ctype == iSize || cdesc->ctype == iKSize ||
+ cdesc->ctype == iSizeNarrow ||
+ cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){
+ if(cdesc->actual_length == 0){
+ if((fix=cdesc->width) > 0){ /* had this reserved */
+ cdesc->width = 0;
+ space_left += fix;
+ }
+
+ space_left++; /* +1 for space between columns */
+ }
+ }
+
+ /*
+ * Calculate the field widths that are basically fixed in width.
+ * Do them in this order in case we don't have enough space to go around.
+ * The set of fixed_ctypes here is the same as the set where we
+ * set the actual_lengths above.
+ */
+ for(j = 0; space_left > 0 && some_to_calculate; j++){
+
+ if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
+ break;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
+ cdesc++)
+ if(cdesc->ctype == fixed_ctypes[j] && cdesc->wtype == WeCalculate){
+ some_to_calculate--;
+ fix = MIN(cdesc->actual_length - cdesc->width, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+
+ /*
+ * Fill in widths for Percent cases. If there are no more to calculate,
+ * use the percentages as relative numbers and use the rest of the space,
+ * else treat them as absolute percentages of the original avail screen.
+ */
+ if(space_left > 0){
+ if(some_to_calculate){
+ int tot_requested = 0;
+
+ /*
+ * Requests are treated as percent of screen width. See if they
+ * will all fit. If not, trim them back proportionately.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ tot_requested += fix;
+ }
+ }
+
+ if(tot_requested > space_left){
+ int multiplier = (100 * space_left) / tot_requested;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ fix = (2 * fix * multiplier + 100) / 200;
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ else{
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ }
+ else{
+ tot_pct = 0;
+ was_sl = space_left;
+ /* add up total percentages requested */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ if(cdesc->wtype == Percent)
+ tot_pct += cdesc->req_width;
+
+ /* give relative weight to requests */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0 && tot_pct > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ fix = ((2*cdesc->req_width*was_sl)+tot_pct) / (2*tot_pct);
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ }
+
+ /* split up rest, give twice as much to Subject */
+ keep_going = 1;
+ while(space_left > 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
+ keep_going++;
+ cdesc->width++;
+ space_left--;
+ if(space_left > 0 && (cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText)){
+ cdesc->width++;
+ space_left--;
+ }
+ }
+ }
+ }
+
+ /* if still more, pad out percent's */
+ keep_going = 1;
+ while(space_left > 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
+ keep_going++;
+ cdesc->width++;
+ space_left--;
+ }
+ }
+ }
+
+ /* if user made Fixed fields too big, give back space */
+ keep_going = 1;
+ while(space_left < 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left < 0;
+ cdesc++){
+ if(cdesc->wtype == Fixed && cdesc->width > 0){
+ keep_going++;
+ cdesc->width--;
+ space_left++;
+ }
+ }
+ }
+
+ if(pith_opt_save_index_state)
+ (*pith_opt_save_index_state)(FALSE);
+}
+
+
+void
+setup_thread_header_widths(MAILSTREAM *stream)
+{
+ clear_icache_flags(stream);
+ if(pith_opt_save_index_state)
+ (*pith_opt_save_index_state)(TRUE);
+}
+
+
+/*
+ * load_overview - c-client call back to gather overview data
+ *
+ * Note: if we never get called, UID represents a hole
+ * if we're passed a zero UID, totally bogus overview data
+ * if we're passed a zero obuf, mostly bogus overview data
+ */
+void
+load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
+{
+ if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
+ INDEXDATA_S idata;
+ ICE_S *ice;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.no_fetch = 1;
+
+ /*
+ * Only really load the thing if we've got an NNTP stream
+ * otherwise we're just using mail_fetch_overview to load the
+ * IMAP envelope cache with the specific set of messages
+ * in a single RTT.
+ */
+ idata.stream = stream;
+ idata.rawno = rawno;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
+ idata.size = obuf->optional.octets;
+ idata.from = obuf->from;
+ idata.date = obuf->date;
+ idata.subject = obuf->subject;
+
+ ice = (*format_index_line)(&idata);
+ if(idata.bogus && ice){
+ if(THRD_INDX()){
+ if(ice->tice)
+ clear_ice(&ice->tice);
+ }
+ else
+ clear_ice(&ice);
+ }
+ else if(F_OFF(F_QUELL_NEWS_ENV_CB, ps_global)
+ && (!THRD_INDX() || (ice && ice->tice))
+ && !msgline_hidden(stream, sp_msgmap(stream), idata.msgno, 0)
+ && pith_opt_paint_index_hline){
+ (*pith_opt_paint_index_hline)(stream, idata.msgno, ice);
+ }
+ }
+}
+
+
+ICE_S *
+build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ long int msgno, long int top_msgno, int msgcount, int *fetched)
+{
+ ICE_S *ice, *ic;
+ MESSAGECACHE *mc;
+ long n, i, cnt, rawno, visible, limit = -1L;
+
+ rawno = mn_m2raw(msgmap, msgno);
+
+ /* cache hit? */
+ if(THRD_INDX()){
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+
+ if(ice->tice && ice->tice->ifield
+ && ice->tice->color_lookup_done && ice->tice->widths_done){
+#ifdef DEBUG
+ char buf[MAX_SCREEN_COLS+1];
+ simple_index_line(buf, sizeof(buf), ice->tice, msgno);
+#endif
+ dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
+ ice->tice,
+ buf[0] ? buf : "?",
+ buf[0] ? strlen(buf) : 0));
+ return(ice);
+ }
+ }
+ else{
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+
+ if(ice->ifield && ice->color_lookup_done && ice->widths_done){
+#ifdef DEBUG
+ char buf[MAX_SCREEN_COLS+1];
+ simple_index_line(buf, sizeof(buf), ice, msgno);
+#endif
+ dprint((9, "Hit: Returning %p -> <%s (%d)\n",
+ ice,
+ buf[0] ? buf : "?",
+ buf[0] ? strlen(buf) : 0));
+ return(ice);
+ }
+ }
+
+ /*
+ * If we are in THRD_INDX() and the width changed we don't currently
+ * have a method of fixing just the widths and print_format strings.
+ * Instead, we clear the index cache entry and start over.
+ */
+ if(THRD_INDX() && ice && ice->tice && ice->tice->ifield
+ && !ice->tice->widths_done){
+ clear_ice(&ice->tice);
+ }
+
+ /*
+ * Fetch everything we need to start filling in the index line
+ * explicitly via mail_fetch_overview. On an nntp stream
+ * this has the effect of building the index lines in the
+ * load_overview callback. Under IMAP we're either getting
+ * the envelope data via the imap_envelope callback or
+ * preloading the cache. Either way, we're getting exactly
+ * what we want rather than relying on linear lookahead sort
+ * of prefetch...
+ */
+ if(!(fetched && *fetched) && index_in_overview(stream)
+ && ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
+ || (!THRD_INDX() && !ice->ifield))){
+ char *seq;
+ int count;
+ MESSAGECACHE *mc;
+ PINETHRD_S *thrd;
+
+ if(fetched)
+ (*fetched)++;
+
+ /* clear sequence bits */
+ for(n = 1L; n <= stream->nmsgs; n++)
+ if((mc = mail_elt(stream, n)) != NULL)
+ mc->sequence = 0;
+
+ /*
+ * Light interesting bits
+ * NOTE: not set above because m2raw's cheaper
+ * than raw2m for every message
+ */
+
+ /*
+ * Unfortunately, it is expensive to calculate visible pages
+ * in thread index if we are zoomed, so we don't try.
+ */
+ if(THRD_INDX() && any_lflagged(msgmap, MN_HIDE))
+ visible = msgmap->visible_threads;
+ else if(THREADING() && sp_viewing_a_thread(stream)){
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ for(visible = 0L, n = top_msgno;
+ visible < msgcount && n <= mn_get_total(msgmap);
+ n++){
+
+ if(!get_lflag(stream, msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(stream, msgmap, n, 0))
+ visible++;
+ }
+
+ }
+ else
+ visible = mn_get_total(msgmap)
+ - any_lflagged(msgmap, MN_HIDE|MN_CHID);
+
+ limit = MIN(visible, msgcount);
+
+ if(THRD_INDX()){
+ count = i = 0;
+
+ /*
+ * First add the msgno we're asking for in case it
+ * isn't visible.
+ */
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, msgno));
+ if(msgno <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, top_msgno));
+
+ /*
+ * Loop through visible threads, marking them for fetching.
+ * Stop at end of screen or sooner if we run out of visible
+ * threads.
+ */
+ while(thrd){
+ n = mn_raw2m(msgmap, thrd->rawno);
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next thread which is visible */
+ do{
+ if(mn_get_revsort(msgmap) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else if(!mn_get_revsort(msgmap) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ } while(thrd
+ && msgline_hidden(stream, msgmap,
+ mn_raw2m(msgmap, thrd->rawno), 0));
+ }
+ }
+ else{
+ count = i = 0;
+
+ /*
+ * First add the msgno we're asking for in case it
+ * isn't visible.
+ */
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,msgno)))) || !ic->ifield)){
+ if((thrd = fetch_thread(stream, rawno)) != NULL){
+ /*
+ * If we're doing a MUTTLIKE display the index line
+ * may depend on the thread parent, and grandparent,
+ * and further back. So just fetch the whole thread
+ * in that case.
+ */
+ if(THREADING()
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE
+ && thrd->top)
+ thrd = fetch_thread(stream, thrd->top);
+
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+ else if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+ }
+
+ n = top_msgno;
+ while(1){
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,n)))) || !ic->ifield)){
+ if((thrd = fetch_thread(stream, rawno)) != NULL){
+ /*
+ * If we're doing a MUTTLIKE display the index line
+ * may depend on the thread parent, and grandparent,
+ * and further back. So just fetch the whole thread
+ * in that case.
+ */
+ if(THREADING()
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE
+ && thrd->top)
+ thrd = fetch_thread(stream, thrd->top);
+
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+ else if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next n which is visible */
+ while(++n <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, n, 0))
+ ;
+ }
+ }
+
+ if(count){
+ seq = build_sequence(stream, NULL, NULL);
+ if(seq){
+ ps_global->dont_count_flagchanges = 1;
+ mail_fetch_overview_sequence(stream, seq,
+ (stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap"))
+ ? NULL : load_overview);
+ ps_global->dont_count_flagchanges = 0;
+ fs_give((void **) &seq);
+ }
+ }
+
+ /*
+ * reassign ice from the cache as it may've been built
+ * within the overview callback or it may have become stale
+ * in the prior sequence bit setting loop ...
+ */
+ rawno = mn_m2raw(msgmap, msgno);
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+ }
+
+ if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
+ || (!THRD_INDX() && !ice->ifield)){
+ INDEXDATA_S idata;
+
+ /*
+ * With pre-fetching/callback-formatting done and no success,
+ * fall into formatting the requested line...
+ */
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.stream = stream;
+ idata.msgno = msgno;
+ idata.rawno = mn_m2raw(msgmap, msgno);
+ if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, idata.rawno))){
+ idata.size = mc->rfc822_size;
+ index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
+ }
+ else
+ idata.bogus = 2;
+
+ ice = (*format_index_line)(&idata);
+ if(!ice)
+ return(NULL);
+ }
+
+ /*
+ * If needed, reset the print_format strings so that they add up to
+ * the right total width. The reset width functionality isn't implemented
+ * for THRD_INDX() so we are just doing a complete rebuild in that
+ * case. This is driven by the clear_ice() call in clear_index_cache_ent()
+ * so it should never be the case that THRD_INDX() is true and only
+ * widths_done needs to be fixed.
+ */
+ if((!THRD_INDX() && ice->ifield && !ice->widths_done)){
+ ICE_S *working_ice;
+ IFIELD_S *ifield;
+ INDEX_COL_S *cdesc;
+
+ if(need_format_setup(stream))
+ setup_header_widths(stream);
+
+ if(THRD_INDX())
+ working_ice = ice ? ice->tice : NULL;
+ else
+ working_ice = ice;
+
+ if(working_ice){
+ /*
+ * First fix the ifield widths. The cdescs with nonzero widths
+ * should correspond to the ifields that are defined.
+ */
+ ifield = working_ice->ifield;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && ifield; cdesc++){
+ if(cdesc->width){
+ if(cdesc->ctype != ifield->ctype){
+ dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno, (int) cdesc->ctype, (int) ifield->ctype));
+ assert(0);
+ }
+
+ ifield->width = cdesc->width;
+ ifield = ifield->next;
+ }
+ }
+
+ /* fix the print_format strings and widths */
+ for(ifield = working_ice->ifield; ifield; ifield = ifield->next)
+ set_ielem_widths_in_field(ifield);
+
+ working_ice->widths_done = 1;
+ }
+ }
+
+ if(THRD_INDX() && ice->tice)
+ ice->tice->color_lookup_done = 1;
+
+ /*
+ * Look for a color for this line (and other lines in the current
+ * view). This does a SEARCH for each role which has a color until
+ * it finds a match. This will be satisfied by the c-client
+ * cache created by the mail_fetch_overview above if it is a header
+ * search.
+ */
+ if(!THRD_INDX() && !ice->color_lookup_done){
+ COLOR_PAIR *linecolor;
+ SEARCHSET *ss, *s;
+ ICE_S *ic;
+ PAT_STATE *pstate = NULL;
+
+ if(pico_usingcolor()){
+ if(limit < 0L){
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ for(visible = 0L, n = top_msgno;
+ visible < msgcount && n <= mn_get_total(msgmap);
+ n++){
+
+ if(!get_lflag(stream, msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(stream, msgmap, n, 0))
+ visible++;
+ }
+
+ }
+ else
+ visible = mn_get_total(msgmap)
+ - any_lflagged(msgmap, MN_HIDE|MN_CHID);
+
+ limit = MIN(visible, msgcount);
+ }
+ /* clear sequence bits */
+ for(n = 1L; n <= stream->nmsgs; n++)
+ if((mc = mail_elt(stream, n)) != NULL)
+ mc->sequence = 0;
+
+ cnt = i = 0;
+ n = top_msgno;
+ while(1){
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,(rawno = mn_m2raw(msgmap, n)))) || !ic->color_lookup_done)){
+
+ if(rawno >= 1L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))){
+ mc->sequence = 1;
+ cnt++;
+ }
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next n which is visible */
+ while(++n <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, n, 0))
+ ;
+ }
+
+ /*
+ * Why is there a loop here? The first call to get_index_line_color
+ * will return a set of messages which match one of the roles.
+ * Then, we eliminate those messages from the search set and try
+ * again. This time we'd get past that role and into a different
+ * role. Because of that, we hang onto the state and don't reset
+ * to the first_pattern on the second and subsequent times
+ * through the loop, avoiding fruitless match_pattern calls in
+ * get_index_line_color.
+ * Before the first call, pstate should be set to NULL.
+ */
+ while(cnt > 0L){
+ ss = build_searchset(stream);
+ if(ss){
+ int colormatch;
+
+ linecolor = NULL;
+ colormatch = get_index_line_color(stream, ss, &pstate,
+ &linecolor);
+
+ /*
+ * Assign this color to all matched msgno's and
+ * turn off the sequence bit so we won't check
+ * for them again.
+ */
+ if(colormatch){
+ for(s = ss; s; s = s->next){
+ for(n = s->first; n <= s->last; n++){
+ if(n >= 1L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n))
+ && mc->searched){
+ cnt--;
+ mc->sequence = 0;
+ ic = fetch_ice(stream, n);
+ if(ic){
+ ic->color_lookup_done = 1;
+ if(linecolor)
+ ic->linecolor = new_color_pair(linecolor->fg,
+ linecolor->bg);
+ }
+ }
+ }
+ }
+
+ if(linecolor)
+ free_color_pair(&linecolor);
+ }
+ else{
+ /* have to mark the rest of the lookups done */
+ for(s = ss; s && cnt > 0; s = s->next){
+ for(n = s->first; n <= s->last && cnt > 0; n++){
+ if(n >= 1L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n))
+ && mc->sequence){
+ cnt--;
+ ic = fetch_ice(stream, n);
+ if(ic)
+ ic->color_lookup_done = 1;
+ }
+ }
+ }
+
+ /* just making sure */
+ cnt = 0L;
+ }
+
+ mail_free_searchset(&ss);
+ }
+ else
+ cnt = 0L;
+ }
+
+ ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
+ }
+ else
+ ice->color_lookup_done = 1;
+ }
+
+ return(ice); /* Return formatted index data */
+}
+
+
+int
+day_of_week(struct date *d)
+{
+ int m, y;
+
+ m = d->month;
+ y = d->year;
+ if(m <= 2){
+ m += 9;
+ y--;
+ }
+ else
+ m -= 3; /* March is month 0 */
+
+ return((d->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100))%7);
+}
+
+
+static int daytab[2][13] = {
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+int
+day_of_year(struct date *d)
+{
+ int i, leap, doy;
+
+ if(d->year <= 0 || d->month < 1 || d->month > 12)
+ return(-1);
+
+ doy = d->day;
+ leap = (d->year%4 == 0 && d->year%100 != 0) || d->year%400 == 0;
+ for(i = 1; i < d->month; i++)
+ doy += daytab[leap][i];
+
+ return(doy);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format a string summarizing the message header for index on screen
+
+ Args: buffer -- buffer to place formatted line
+ idata -- snot it takes to format the line
+
+ Result: returns pointer given buffer IF entry formatted
+ else NULL if there was a problem (but the buffer is
+ still suitable for display)
+ ----*/
+ICE_S *
+format_index_index_line(INDEXDATA_S *idata)
+{
+ char str[BIGWIDTH+1], to_us, status, *field,
+ *p, *newsgroups;
+ int i, collapsed = 0, start, fromfield;
+ long l, score;
+ BODY *body = NULL;
+ MESSAGECACHE *mc;
+ ADDRESS *addr, *toaddr, *ccaddr, *last_to;
+ PINETHRD_S *thrd = NULL;
+ INDEX_COL_S *cdesc = NULL;
+ ICE_S *ice, **icep;
+ IFIELD_S *ifield;
+ IELEM_S *ielem;
+ struct variable *vars = ps_global->vars;
+
+ dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
+ idata ? idata->msgno : -1, idata ? idata->rawno : -1));
+
+
+ ice = fetch_ice(idata->stream, idata->rawno);
+ if(!ice)
+ return(NULL);
+
+ free_ifield(&ice->ifield);
+
+ /*
+ * Operate on a temporary copy of ice. The reason for this
+ * is that we may end up causing a pine_mail_fetchenvelope() call
+ * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
+ * and mm_flags may do a clear_ice(), freeing the ice we are working
+ * on out from under us. We try to fetch everything we need in
+ * build_header_work() but c-client will short-circuit our request
+ * if we already got the raw header for some reason. One possible
+ * reason is a categorizer command in a filter. In that case
+ * we still need a fetch fast to get the rest of the envelope data.
+ */
+ ice = copy_ice(ice);
+
+ /* is this a collapsed thread index line? */
+ if(!idata->bogus && THREADING()){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ collapsed = thrd && thrd->next
+ && get_lflag(idata->stream, NULL,
+ idata->rawno, MN_COLL);
+ }
+
+ /* calculate contents of the required fields */
+ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
+ if(cdesc->width){
+ memset(str, 0, sizeof(str));
+ ifield = new_ifield(&ice->ifield);
+ ifield->ctype = cdesc->ctype;
+ ifield->width = cdesc->width;
+ fromfield = 0;
+
+ if(idata->bogus){
+ if(cdesc->ctype == iMessNo)
+ snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
+ else if(idata->bogus < 2 && (cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText))
+ snprintf(str, sizeof(str), "%s", _("[ No Message Text Available ]"));
+ }
+ else
+ switch(cdesc->ctype){
+ case iStatus:
+ to_us = status = ' ';
+ if(collapsed){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
+ status = status_symbol_for_thread(idata->stream, thrd,
+ cdesc->ctype);
+ }
+ else{
+ if((mc=mail_elt(idata->stream,idata->rawno)) && mc->flagged)
+ to_us = '*'; /* simple */
+ else if(!IS_NEWS(idata->stream)){
+ for(addr = fetch_to(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ ice->to_us = 1;
+ if(to_us == ' ')
+ to_us = '+';
+
+ break;
+ }
+
+ if(to_us != '+' && resent_to_us(idata)){
+ ice->to_us = 1;
+ if(to_us == ' ')
+ to_us = '+';
+ }
+
+ if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
+ for(addr = fetch_cc(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ ice->cc_us = 1;
+ to_us = '-';
+ break;
+ }
+ }
+
+ status = (!idata->stream || !IS_NEWS(idata->stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
+ ? 'N' : ' ';
+
+ if(mc->seen)
+ status = ' ';
+
+ if(user_flag_is_set(idata->stream, idata->rawno, FORWARDED_FLAG))
+ status = 'F';
+
+ if(mc->answered)
+ status = 'A';
+
+ if(mc->deleted)
+ status = 'D';
+ }
+
+ snprintf(str, sizeof(str), "%c %c", to_us, status);
+
+ ifield->leftadj = 1;
+ for(i = 0; i < 3; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = str[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+
+ if(str[0] == '*'){
+ if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
+ }
+ }
+ else if(str[0] == '+' || str[0] == '-'){
+ if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
+ }
+ }
+
+ if(str[2] == 'D'){
+ if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'A'){
+ if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'F'){
+ if(VAR_IND_FWD_FORE_COLOR && VAR_IND_FWD_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_FWD_FORE_COLOR, VAR_IND_FWD_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'N'){
+ if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ }
+
+ break;
+
+ case iFStatus:
+ case iIStatus:
+ case iSIStatus:
+ {
+ char new, answered, deleted, flagged;
+
+ if(collapsed){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
+ }
+ else{
+ to_us = ' ';
+ if(!IS_NEWS(idata->stream)){
+ for(addr = fetch_to(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '+';
+ break;
+ }
+
+ if(to_us == ' ' && resent_to_us(idata))
+ to_us = '+';
+
+ if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
+ for(addr = fetch_cc(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '-';
+ break;
+ }
+ }
+ }
+
+ new = answered = deleted = flagged = ' ';
+
+ if(collapsed){
+ unsigned long save_branch, cnt, tot_in_thrd;
+
+ /*
+ * Branch is a sibling, not part of the thread, so
+ * don't consider it when displaying this line.
+ */
+ save_branch = thrd->branch;
+ thrd->branch = 0L;
+
+ tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
+ F_NONE);
+
+ cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
+ if(cnt)
+ deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
+
+ cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
+ if(cnt)
+ answered = (cnt == tot_in_thrd) ? 'A' : 'a';
+
+ /* no lower case *, same thing for some or all */
+ if(count_flags_in_thread(idata->stream, thrd, F_FLAG))
+ flagged = '*';
+
+ new = status_symbol_for_thread(idata->stream, thrd,
+ cdesc->ctype);
+
+ thrd->branch = save_branch;
+ }
+ else{
+ mc = (idata->rawno > 0L && idata->stream
+ && idata->rawno <= idata->stream->nmsgs)
+ ? mail_elt(idata->stream, idata->rawno) : NULL;
+ if(mc && mc->valid){
+ if(cdesc->ctype == iIStatus || cdesc->ctype == iSIStatus){
+ if(mc->recent)
+ new = mc->seen ? 'R' : 'N';
+ else if (!mc->seen)
+ new = 'U';
+ }
+ else if(!mc->seen
+ && (!IS_NEWS(idata->stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
+ new = 'N';
+
+ if(mc->answered)
+ answered = 'A';
+
+ if(mc->deleted)
+ deleted = 'D';
+
+ if(mc->flagged)
+ flagged = '*';
+ }
+ }
+
+ snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
+ answered, deleted);
+
+ if(cdesc->ctype == iSIStatus)
+ start = 2;
+ else
+ start = 0;
+
+ ifield->leftadj = 1;
+ for(i = start; i < 6; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = str[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+
+ if(str[0] == '+' || str[0] == '-'){
+ if(start == 0
+ && VAR_IND_PLUS_FORE_COLOR
+ && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
+ }
+ }
+
+ if(str[2] == '*'){
+ if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem;
+ else
+ ielem = ifield->ielem->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
+ }
+ }
+
+ if(str[3] == 'N' || str[3] == 'n'){
+ if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ else if(str[3] == 'R' || str[3] == 'r'){
+ if(VAR_IND_REC_FORE_COLOR && VAR_IND_REC_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_REC_FORE_COLOR, VAR_IND_REC_BACK_COLOR);
+ }
+ }
+ else if(str[3] == 'U' || str[3] == 'u'){
+ if(VAR_IND_UNS_FORE_COLOR && VAR_IND_UNS_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_UNS_FORE_COLOR, VAR_IND_UNS_BACK_COLOR);
+ }
+ }
+
+ if(str[4] == 'A' || str[4] == 'a'){
+ if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next->next;
+ else
+ ielem = ifield->ielem->next->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
+ }
+ }
+
+ if(str[5] == 'D' || str[5] == 'd'){
+ if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next->next->next;
+ else
+ ielem = ifield->ielem->next->next->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
+ }
+ }
+ }
+ }
+
+ break;
+
+ case iMessNo:
+ /*
+ * This is a special case. The message number is
+ * generated on the fly in the painting routine.
+ * But the data array is allocated here in case it
+ * is useful for the paint routine.
+ */
+ snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
+ break;
+
+ case iArrow:
+ snprintf(str, sizeof(str), "%-*.*s", ifield->width, ifield->width, " ");
+ if(VAR_IND_ARR_FORE_COLOR && VAR_IND_ARR_BACK_COLOR){
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(str);
+ ielem->datalen = strlen(str);
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ARR_FORE_COLOR,
+ VAR_IND_ARR_BACK_COLOR);
+ }
+
+ break;
+
+ case iScore:
+ score = get_msg_score(idata->stream, idata->rawno);
+ if(score == SCORE_UNDEF){
+ SEARCHSET *ss = NULL;
+
+ ss = mail_newsearchset();
+ ss->first = ss->last = (unsigned long) idata->rawno;
+ if(ss){
+ /*
+ * This looks like it might be expensive to get the
+ * score for each message when needed but it shouldn't
+ * be too bad because we know we have the envelope
+ * data cached. We can't calculate all of the scores
+ * we need for the visible messages right here in
+ * one fell swoop because we don't have the other
+ * envelopes yet. And we can't get the other
+ * envelopes at this point because we may be in
+ * the middle of a c-client callback (pine_imap_env).
+ * (Actually we could, because we know whether or
+ * not we're in the callback because of the no_fetch
+ * parameter.)
+ * We have another problem if the score rules depend
+ * on something other than envelope data. I guess they
+ * only do that if they have an alltext (search the
+ * text of the message) definition. So, we're going
+ * to pass no_fetch to calculate_scores so that it
+ * can return an error if we need the text data but
+ * can't get it because of no_fetch. Setting bogus
+ * will cause us to do the scores calculation later
+ * when we are no longer in the callback.
+ */
+ idata->bogus =
+ (calculate_some_scores(idata->stream,
+ ss, idata->no_fetch) == 0)
+ ? 1 : 0;
+ score = get_msg_score(idata->stream, idata->rawno);
+ mail_free_searchset(&ss);
+ }
+ }
+
+ snprintf(str, sizeof(str), "%ld", score != SCORE_UNDEF ? score : 0L);
+ break;
+
+ case iDate: case iMonAbb: case iLDate:
+ case iSDate: case iSTime:
+ case iS1Date: case iS2Date: case iS3Date: case iS4Date:
+ case iDateIso: case iDateIsoS: case iTime24: case iTime12:
+ case iSDateIsoS: case iSDateIso:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime:
+ case iSDateTimeIsoS: case iSDateTimeIso:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24:
+ case iSDateTimeIsoS24: case iSDateTimeIso24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iTimezone: case iYear: case iYear2Digit:
+ case iRDate: case iDay: case iDay2Digit: case iMon2Digit:
+ case iDayOrdinal: case iMon: case iMonLong:
+ case iDayOfWeekAbb: case iDayOfWeek:
+ case iPrefDate: case iPrefTime: case iPrefDateTime:
+ date_str(fetch_date(idata), cdesc->ctype, 0, str, sizeof(str), cdesc->monabb_width);
+ break;
+
+ case iFromTo:
+ case iFromToNotNews:
+ case iFrom:
+ case iAddress:
+ case iMailbox:
+ fromfield++;
+ from_str(cdesc->ctype, idata, str, sizeof(str), ice);
+ break;
+
+ case iTo:
+ if(((field = ((addr = fetch_to(idata))
+ ? "To"
+ : (addr = fetch_cc(idata))
+ ? "Cc"
+ : NULL))
+ && !set_index_addr(idata, field, addr, NULL, BIGWIDTH, str))
+ || !field)
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
+
+ break;
+
+ case iCc:
+ set_index_addr(idata, "Cc", fetch_cc(idata), NULL, BIGWIDTH, str);
+ break;
+
+ case iRecips:
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+
+ break;
+
+ case iSender:
+ fromfield++;
+ if((addr = fetch_sender(idata)) != NULL)
+ set_index_addr(idata, "Sender", addr, NULL, BIGWIDTH, str);
+
+ break;
+
+ case iInit:
+ {ADDRESS *addr;
+
+ if((addr = fetch_from(idata)) && addr->personal){
+ char *name, *initials = NULL;
+
+ name = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->personal);
+ if(name == addr->personal){
+ strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
+ tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
+ name = (char *) tmp_20k_buf;
+ }
+
+ if(name && *name){
+ initials = reply_quote_initials(name);
+ snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
+ }
+ }
+ }
+
+ break;
+
+ case iSize:
+ /* 0 ... 9999 */
+ if((l = fetch_size(idata)) < 10*1000L)
+ snprintf(str, sizeof(str), "(%lu)", l);
+ /* 10K ... 999K */
+ else if(l < 1000L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luK)", l);
+ }
+ /* 1.0M ... 99.9M */
+ else if(l < 1000L*100L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
+ }
+ /* 100M ... 2000M */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luM)", l);
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ case iSizeComma:
+ /* 0 ... 99,999 */
+ if((l = fetch_size(idata)) < 100*1000L)
+ snprintf(str, sizeof(str), "(%s)", comatose(l));
+ /* 100K ... 9,999K */
+ else if(l < 10L*1000L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%sK)", comatose(l));
+ }
+ /* 10.0M ... 999.9M */
+ else if(l < 1000L*1000L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
+ }
+ /* 1,000M ... 2,000M */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%sM)", comatose(l));
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ case iSizeNarrow:
+ /* 0 ... 999 */
+ if((l = fetch_size(idata)) < 1000L)
+ snprintf(str, sizeof(str), "(%lu)", l);
+ /* 1K ... 99K */
+ else if(l < 100L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luK)", l);
+ }
+ /* .1M ... .9M */
+ else if(l < 1000L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(.%luM)", l);
+ }
+ /* 1M ... 99M */
+ else if(l < 1000L*100L*1000L - 1000L*1000L/2){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luM)", l);
+ }
+ /* .1G ... .9G */
+ else if(l < 1000L*1000L*1000L - 100L*1000L*1000L/2){
+ l = l/(100L*1000L*1000L) + (l%(100L*1000L*1000L) >=
+ (100L*1000L*1000L/2) ? 1L : 0L);
+ snprintf(str, sizeof(str), "(.%luG)", l);
+ }
+ /* 1G ... 2G */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L*1000L) + (l%(1000L*1000L*1000L) >=
+ (1000L*1000L*1000L/2) ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luG)", l);
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ /* From Carl Jacobsen <carl@ucsd.edu> */
+ case iKSize:
+ l = fetch_size(idata);
+ l = (l / 1024L) + (l % 1024L != 0 ? 1 : 0);
+
+ if(l < 1024L) { /* 0k .. 1023k */
+ snprintf(str, sizeof(str), "(%luk)", l);
+
+ } else if (l < 100L * 1024L){ /* 1.0M .. 99.9M */
+ snprintf(str, sizeof(str), "(%lu.M)", (l * 10L) / 1024L);
+ if ((p = strchr(str, '.')) != NULL) {
+ p--; p[1] = p[0]; p[0] = '.'; /* swap last digit & . */
+ }
+ } else if (l <= 2L * 1024L * 1024L) { /* 100M .. 2048 */
+ snprintf(str, sizeof(str), "(%luM)", l / 1024L);
+ } else {
+ snprintf(str, sizeof(str), "(HUGE!)");
+ }
+
+ break;
+
+ case iDescripSize:
+ if((body = fetch_body(idata)) != NULL)
+ switch(body->type){
+ case TYPETEXT:
+ {
+ mc = (idata->rawno > 0L && idata->stream
+ && idata->rawno <= idata->stream->nmsgs)
+ ? mail_elt(idata->stream, idata->rawno) : NULL;
+ if(mc && mc->rfc822_size < 6000)
+ snprintf(str, sizeof(str), "(short )");
+ else if(mc && mc->rfc822_size < 25000)
+ snprintf(str, sizeof(str), "(medium )");
+ else if(mc && mc->rfc822_size < 100000)
+ snprintf(str, sizeof(str), "(long )");
+ else
+ snprintf(str, sizeof(str), "(huge )");
+ }
+
+ break;
+
+ case TYPEMULTIPART:
+ if(strucmp(body->subtype, "MIXED") == 0){
+ int x;
+
+ x = body->nested.part
+ ? body->nested.part->body.type
+ : TYPETEXT + 1000;
+ switch(x){
+ case TYPETEXT:
+ if(body->nested.part->body.size.bytes < 6000)
+ snprintf(str, sizeof(str), "(short+ )");
+ else if(body->nested.part->body.size.bytes
+ < 25000)
+ snprintf(str, sizeof(str), "(medium+)");
+ else if(body->nested.part->body.size.bytes
+ < 100000)
+ snprintf(str, sizeof(str), "(long+ )");
+ else
+ snprintf(str, sizeof(str), "(huge+ )");
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "(multi )");
+ break;
+ }
+ }
+ else if(strucmp(body->subtype, "DIGEST") == 0)
+ snprintf(str, sizeof(str), "(digest )");
+ else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
+ snprintf(str, sizeof(str), "(mul/alt)");
+ else if(strucmp(body->subtype, "PARALLEL") == 0)
+ snprintf(str, sizeof(str), "(mul/par)");
+ else
+ snprintf(str, sizeof(str), "(multi )");
+
+ break;
+
+ case TYPEMESSAGE:
+ snprintf(str, sizeof(str), "(message)");
+ break;
+
+ case TYPEAPPLICATION:
+ snprintf(str, sizeof(str), "(applica)");
+ break;
+
+ case TYPEAUDIO:
+ snprintf(str, sizeof(str), "(audio )");
+ break;
+
+ case TYPEIMAGE:
+ snprintf(str, sizeof(str), "(image )");
+ break;
+
+ case TYPEVIDEO:
+ snprintf(str, sizeof(str), "(video )");
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "(other )");
+ break;
+ }
+
+ break;
+
+ case iAtt:
+ str[0] = SPACE;
+ str[1] = '\0';
+ if((body = fetch_body(idata)) &&
+ body->type == TYPEMULTIPART &&
+ strucmp(body->subtype, "ALTERNATIVE") != 0){
+ PART *part;
+ int atts = 0;
+
+ part = body->nested.part; /* 1st part, don't count */
+ while(part && part->next && atts < 10){
+ atts++;
+ part = part->next;
+ }
+
+ if(atts > 9)
+ str[0] = '*';
+ else if(atts > 0)
+ str[0] = '0' + atts;
+ }
+
+ break;
+
+ case iSubject:
+ subj_str(idata, str, sizeof(str), NoKW, 0, ice);
+ break;
+
+ case iSubjectText:
+ subj_str(idata, str, sizeof(str), NoKW, 1, ice);
+ break;
+
+ case iSubjKey:
+ subj_str(idata, str, sizeof(str), KW, 0, ice);
+ break;
+
+ case iSubjKeyText:
+ subj_str(idata, str, sizeof(str), KW, 1, ice);
+ break;
+
+ case iSubjKeyInit:
+ subj_str(idata, str, sizeof(str), KWInit, 0, ice);
+ break;
+
+ case iSubjKeyInitText:
+ subj_str(idata, str, sizeof(str), KWInit, 1, ice);
+ break;
+
+ case iOpeningText:
+ case iOpeningTextNQ:
+ if(idata->no_fetch)
+ idata->bogus = 1;
+ else{
+ char *first_text;
+
+ first_text = fetch_firsttext(idata, cdesc->ctype == iOpeningTextNQ);
+
+ if(first_text){
+ strncpy(str, first_text, BIGWIDTH);
+ str[BIGWIDTH] = '\0';
+ fs_give((void **) &first_text);
+ }
+ }
+
+ break;
+
+ case iKey:
+ key_str(idata, KW, ice);
+ break;
+
+ case iKeyInit:
+ key_str(idata, KWInit, ice);
+ break;
+
+ case iNews:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL){
+ strncpy(str, newsgroups, BIGWIDTH);
+ str[BIGWIDTH] = '\0';
+ }
+
+ break;
+
+ case iNewsAndTo:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ strncpy(str, newsgroups, sizeof(str));
+
+ if((l = strlen(str)) < sizeof(str)){
+ if(sizeof(str) - l < 6)
+ strncpy(str+l, "...", sizeof(str)-l);
+ else{
+ if(l > 0){
+ strncpy(str+l, " and ", sizeof(str)-l);
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH-l-5, str+l+5);
+ if(!str[l+5])
+ str[l] = '\0';
+ }
+ else
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH, str);
+ }
+ }
+
+ break;
+
+ case iToAndNews:
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH, str);
+ if((l = strlen(str)) < sizeof(str) &&
+ (newsgroups = fetch_newsgroups(idata))){
+ if(sizeof(str) - l < 6)
+ strncpy(str+l, "...", sizeof(str)-l);
+ else{
+ if(l > 0)
+ strncpy(str+l, " and ", sizeof(str)-l);
+
+ if(l > 0)
+ strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
+ else
+ strncpy(str, newsgroups, BIGWIDTH);
+ }
+ }
+
+ break;
+
+ case iNewsAndRecips:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ strncpy(str, newsgroups, BIGWIDTH);
+
+ if((l = strlen(str)) < BIGWIDTH){
+ if(BIGWIDTH - l < 6)
+ strncpy(str+l, "...", BIGWIDTH-l);
+ else{
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ if(l > 0){
+ strncpy(str+l, " and ", sizeof(str)-l);
+ set_index_addr(idata, "To", toaddr,
+ NULL, BIGWIDTH-l-5, str+l+5);
+ if(!str[l+5])
+ str[l] = '\0';
+ }
+ else
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+ }
+ }
+
+ break;
+
+ case iRecipsAndNews:
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+
+ if((l = strlen(str)) < BIGWIDTH &&
+ (newsgroups = fetch_newsgroups(idata))){
+ if(BIGWIDTH - l < 6)
+ strncpy(str+l, "...", BIGWIDTH-l);
+ else{
+ if(l > 0)
+ strncpy(str+l, " and ", sizeof(str)-l);
+
+ if(l > 0)
+ strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
+ else
+ strncpy(str, newsgroups, BIGWIDTH);
+ }
+ }
+
+ break;
+
+ case iPrio:
+ case iPrioAlpha:
+ case iPrioBang:
+ prio_str(idata, cdesc->ctype, ice);
+ break;
+
+ case iHeader:
+ header_str(idata, cdesc->hdrtok, ice);
+ break;
+
+ case iText:
+ strncpy(str, (cdesc->hdrtok && cdesc->hdrtok->hdrname) ? cdesc->hdrtok->hdrname : "", sizeof(str));
+ str[sizeof(str)-1] = '\0';
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * If the element wasn't already filled in above, do it here.
+ */
+ if(!ifield->ielem){
+ ielem = new_ielem(&ifield->ielem);
+
+ ielem->freedata = 1;
+ ielem->data = cpystr(str);
+ ielem->datalen = strlen(str);
+
+ if(fromfield && pico_usingcolor()
+ && ps_global->VAR_IND_FROM_FORE_COLOR
+ && ps_global->VAR_IND_FROM_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_FROM_FORE_COLOR,
+ ps_global->VAR_IND_FROM_BACK_COLOR);
+ /*
+ * This space is here so that if the text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+ else if((cdesc->ctype == iOpeningText || cdesc->ctype == iOpeningTextNQ)
+ && pico_usingcolor()
+ && ps_global->VAR_IND_OP_FORE_COLOR
+ && ps_global->VAR_IND_OP_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR,
+ ps_global->VAR_IND_OP_BACK_COLOR);
+ /*
+ * This space is here so that if the text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ ifield->leftadj = (cdesc->adjustment == Left) ? 1 : 0;
+ set_ielem_widths_in_field(ifield);
+ }
+ }
+
+ ice->widths_done = 1;
+ ice->id = ice_hash(ice);
+
+ /*
+ * Now we have to put the temporary copy of ice back as the
+ * real thing.
+ */
+ icep = fetch_ice_ptr(idata->stream, idata->rawno);
+ if(icep){
+ free_ice(icep); /* free what is already there */
+ *icep = ice;
+ }
+
+ return(ice);
+}
+
+
+ICE_S *
+format_thread_index_line(INDEXDATA_S *idata)
+{
+ char *p, buffer[BIGWIDTH+1];
+ int thdlen, space_left, i;
+ PINETHRD_S *thrd = NULL;
+ ICE_S *ice, *tice = NULL, **ticep = NULL;
+ IFIELD_S *ifield;
+ IELEM_S *ielem;
+ int (*save_sfstr_func)(void);
+ struct variable *vars = ps_global->vars;
+
+ dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
+ idata ? idata->msgno : -1, idata ? idata->rawno : -1));
+
+ space_left = ps_global->ttyo->screen_cols;
+
+ if(ps_global->msgmap->max_thrdno < 1000)
+ thdlen = 3;
+ else if(ps_global->msgmap->max_thrdno < 10000)
+ thdlen = 4;
+ else if(ps_global->msgmap->max_thrdno < 100000)
+ thdlen = 5;
+ else
+ thdlen = 6;
+
+ ice = fetch_ice(idata->stream, idata->rawno);
+
+ thrd = fetch_thread(idata->stream, idata->rawno);
+
+ if(!thrd || !ice) /* can't happen? */
+ return(ice);
+
+ if(!ice->tice){
+ tice = (ICE_S *) fs_get(sizeof(*tice));
+ memset(tice, 0, sizeof(*tice));
+ ice->tice = tice;
+ }
+
+ tice = ice->tice;
+
+ if(!tice)
+ return(ice);
+
+ free_ifield(&tice->ifield);
+
+ ticep = &ice->tice;
+ tice = copy_ice(tice);
+
+ if(space_left >= 3){
+ char to_us, status;
+
+ p = buffer;
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
+ status = status_symbol_for_thread(idata->stream, thrd, iStatus);
+
+ if((p-buffer)+3 < sizeof(buffer)){
+ p[0] = to_us;
+ p[1] = ' ';
+ p[2] = status;
+ p[3] = '\0';;
+ }
+
+ space_left -= 3;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iStatus;
+ ifield->width = 3;
+ ifield->leftadj = 1;
+ for(i = 0; i < 3; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = p[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+ if(to_us == '*'
+ && VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR,
+ VAR_IND_IMP_BACK_COLOR);
+ if(F_ON(F_COLOR_LINE_IMPORTANT, ps_global))
+ tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR,
+ VAR_IND_IMP_BACK_COLOR);
+ }
+ else if((to_us == '+' || to_us == '-')
+ && VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR,
+ VAR_IND_PLUS_BACK_COLOR);
+ }
+
+ if(status == 'D'
+ && VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR,
+ VAR_IND_DEL_BACK_COLOR);
+ }
+ else if(status == 'N'
+ && VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR,
+ VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ }
+
+ if(space_left >= thdlen+1){
+ p = buffer;
+ space_left--;
+
+ snprintf(p, sizeof(buffer), "%*.*s", thdlen, thdlen, "");
+ space_left -= thdlen;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iMessNo;
+ ifield->width = thdlen;
+ ifield->leftadj = 0;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = strlen(p);
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ }
+
+ if(space_left >= 7){
+
+ p = buffer;
+ space_left--;
+
+ date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer), 0);
+ if(sizeof(buffer) > 6)
+ p[6] = '\0';
+
+ if(strlen(p) < 6 && (sizeof(buffer)) > 6){
+ char *q;
+
+ for(q = p + strlen(p); q < p + 6; q++)
+ *q = ' ';
+ }
+
+ space_left -= 6;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iDate;
+ ifield->width = 6;
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = ifield->width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ }
+
+
+ if(space_left > 3){
+ int from_width, subj_width, bigthread_adjust;
+ long in_thread;
+ char from[BIGWIDTH+1];
+ char tcnt[50];
+
+ space_left--;
+
+ in_thread = count_lflags_in_thread(idata->stream, thrd,
+ ps_global->msgmap, MN_NONE);
+
+ p = buffer;
+ if(in_thread == 1 && THRD_AUTO_VIEW())
+ snprintf(tcnt, sizeof(tcnt), " ");
+ else
+ snprintf(tcnt, sizeof(tcnt), "(%ld)", in_thread);
+
+ bigthread_adjust = MAX(0, strlen(tcnt) - 3);
+
+ /* third of the rest */
+ from_width = MAX((space_left-1)/3 - bigthread_adjust, 1);
+
+ /* the rest */
+ subj_width = space_left - from_width - 1;
+
+ if(strlen(tcnt) > subj_width)
+ tcnt[subj_width] = '\0';
+
+ from[0] = '\0';
+ save_sfstr_func = pith_opt_truncate_sfstr;
+ pith_opt_truncate_sfstr = NULL;
+ from_str(iFromTo, idata, from, sizeof(from), tice);
+ pith_opt_truncate_sfstr = save_sfstr_func;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->type = eTypeCol;
+ ielem->data = cpystr(from);
+ ielem->datalen = strlen(from);
+ ifield->width = from_width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iFrom;
+ if(from_width > 0 && pico_usingcolor()
+ && VAR_IND_FROM_FORE_COLOR && VAR_IND_FROM_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_FROM_FORE_COLOR,
+ VAR_IND_FROM_BACK_COLOR);
+ }
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 0;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(tcnt);
+ ielem->datalen = strlen(tcnt);
+ ifield->width = ielem->datalen;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iAtt; /* not used, except that it isn't special */
+
+ subj_width -= strlen(tcnt);
+
+ if(subj_width > 0)
+ subj_width--;
+
+ if(subj_width > 0){
+ if(idata->bogus){
+ if(idata->bogus < 2)
+ snprintf(buffer, sizeof(buffer), "%-.*s", BIGWIDTH,
+ _("[ No Message Text Available ]"));
+ }
+ else{
+ buffer[0] = '\0';
+ save_sfstr_func = pith_opt_truncate_sfstr;
+ pith_opt_truncate_sfstr = NULL;
+ subj_str(idata, buffer, sizeof(buffer), NoKW, 0, NULL);
+ pith_opt_truncate_sfstr = save_sfstr_func;
+ }
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->type = eTypeCol;
+ ielem->data = cpystr(buffer);
+ ielem->datalen = strlen(buffer);
+ ifield->width = subj_width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iSubject;
+ if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR && VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_SUBJ_FORE_COLOR,
+ VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+ }
+ else if(space_left > 1){
+ snprintf(p, sizeof(buffer)-(p-buffer), "%-.*s", space_left-1, " ");
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = strlen(p);
+ ifield->width = space_left-1;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iSubject;
+ }
+
+ tice->widths_done = 1;
+ tice->id = ice_hash(tice);
+
+ if(ticep){
+ free_ice(ticep); /* free what is already there */
+ *ticep = tice;
+ }
+
+ return(ice);
+}
+
+
+/*
+ * Print the fields of ice in buf with a single space between fields.
+ *
+ * Args buf -- place to put the line
+ * ice -- the data for the line
+ * msgno -- this is the msgno to be used, blanks if <= 0
+ *
+ * Returns a pointer to buf.
+ */
+char *
+simple_index_line(char *buf, size_t buflen, ICE_S *ice, long int msgno)
+{
+ char *p;
+ IFIELD_S *ifield, *previfield = NULL;
+ IELEM_S *ielem;
+
+ if(!buf)
+ panic("NULL buf in simple_index_line()");
+
+ if(buflen > 0)
+ buf[0] = '\0';
+
+ p = buf;
+
+ if(ice){
+
+ for(ifield = ice->ifield; ifield && p-buf < buflen; ifield = ifield->next){
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
+ *p++ = ' ';
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ if(msgno > 0L)
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ else
+ snprintf(ielem->data, ielem->datalen+1, "%*.*s", ifield->width, ifield->width, "");
+ }
+ }
+
+ for(ielem = ifield->ielem;
+ ielem && ielem->print_format && p-buf < buflen;
+ ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(p, src,
+ buflen-(p-buf) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ p += bytes_added;
+ }
+
+ previfield = ifield;
+ }
+
+ if(p-buf < buflen)
+ *p = '\0';
+ }
+
+ buf[buflen-1] = '\0';
+
+ return(buf);
+}
+
+
+/*
+ * Look in current mail_stream for matches for messages in the searchset
+ * which match a color rule pattern. Return the color.
+ * The searched bit will be set for all of the messages which match the
+ * first pattern which has a match.
+ *
+ * Args stream -- the mail stream
+ * searchset -- restrict attention to this set of messages
+ * pstate -- The pattern state. On the first call it will be Null.
+ * Null means start over with a new first_pattern.
+ * After that it will be pointing to our local PAT_STATE
+ * so that next_pattern goes to the next one after the
+ * ones we've already checked.
+ *
+ * Returns 0 if no match, 1 if a match.
+ * The color that goes with the matched rule in returned_color.
+ * It may be NULL, which indicates default.
+ */
+int
+get_index_line_color(MAILSTREAM *stream, SEARCHSET *searchset,
+ PAT_STATE **pstate, COLOR_PAIR **returned_color)
+{
+ PAT_S *pat = NULL;
+ long rflags = ROLE_INCOL;
+ COLOR_PAIR *color = NULL;
+ int match = 0;
+ static PAT_STATE localpstate;
+
+ dprint((7, "get_index_line_color\n"));
+
+ if(returned_color)
+ *returned_color = NULL;
+
+ if(*pstate)
+ pat = next_pattern(*pstate);
+ else{
+ *pstate = &localpstate;
+ if(!nonempty_patterns(rflags, *pstate))
+ *pstate = NULL;
+
+ if(*pstate)
+ pat = first_pattern(*pstate);
+ }
+
+ if(*pstate){
+
+ /* Go through the possible roles one at a time until we get a match. */
+ while(!match && pat){
+ if(match_pattern(pat->patgrp, stream, searchset, NULL,
+ get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){
+ if(!pat->action || pat->action->bogus)
+ break;
+
+ match++;
+ if(pat->action && pat->action->incol)
+ color = new_color_pair(pat->action->incol->fg,
+ pat->action->incol->bg);
+ }
+ else
+ pat = next_pattern(*pstate);
+ }
+ }
+
+ if(match && returned_color)
+ *returned_color = color;
+
+ return(match);
+}
+
+
+/*
+ *
+ */
+int
+index_in_overview(MAILSTREAM *stream)
+{
+ INDEX_COL_S *cdesc = NULL;
+
+ if(!(stream->mailbox && IS_REMOTE(stream->mailbox)))
+ return(FALSE); /* no point! */
+
+ if(stream->dtb && stream->dtb->name && !strcmp(stream->dtb->name, "nntp")){
+
+ if(THRD_INDX())
+ return(TRUE);
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ switch(cdesc->ctype){
+ case iTo: /* can't be satisfied by XOVER */
+ case iSender: /* ... or specifically handled */
+ case iDescripSize: /* ... in news case */
+ case iAtt:
+ return(FALSE);
+
+ default :
+ break;
+ }
+ }
+
+ return(TRUE);
+}
+
+
+
+/*
+ * fetch_from - called to get a the index entry's "From:" field
+ */
+int
+resent_to_us(INDEXDATA_S *idata)
+{
+ if(!idata->valid_resent_to){
+ static char *fields[] = {"Resent-To", NULL};
+ char *h;
+
+ if(idata->no_fetch){
+ idata->bogus = 1; /* don't do this */
+ return(FALSE);
+ }
+
+ if((h = pine_fetchheader_lines(idata->stream,idata->rawno,NULL,fields)) != NULL){
+ idata->resent_to_us = parsed_resent_to_us(h);
+ fs_give((void **) &h);
+ }
+
+ idata->valid_resent_to = 1;
+ }
+
+ return(idata->resent_to_us);
+}
+
+
+int
+parsed_resent_to_us(char *h)
+{
+ char *p, *q;
+ ADDRESS *addr = NULL;
+ int rv = FALSE;
+
+ if((p = strindex(h, ':')) != NULL){
+ for(q = ++p; (q = strpbrk(q, "\015\012")) != NULL; q++)
+ *q = ' '; /* quash junk */
+
+ rfc822_parse_adrlist(&addr, p, ps_global->maildomain);
+ if(addr){
+ rv = address_is_us(addr, ps_global);
+ mail_free_address(&addr);
+ }
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * fetch_from - called to get a the index entry's "From:" field
+ */
+ADDRESS *
+fetch_from(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies from is valid */
+ return(idata->from);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->from);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_to - called to get a the index entry's "To:" field
+ */
+ADDRESS *
+fetch_to(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_to)
+ return(idata->to);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->to);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_cc - called to get a the index entry's "Cc:" field
+ */
+ADDRESS *
+fetch_cc(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_cc)
+ return(idata->cc);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->cc);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+
+/*
+ * fetch_sender - called to get a the index entry's "Sender:" field
+ */
+ADDRESS *
+fetch_sender(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_sender)
+ return(idata->sender);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->sender);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
+ */
+char *
+fetch_newsgroups(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_news)
+ return(idata->newsgroups);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->newsgroups);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_subject - called to get at the index entry's "Subject:" field
+ */
+char *
+fetch_subject(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies subject is valid */
+ return(idata->subject);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->subject);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Return an allocated copy of the first few characters from the body
+ * of the message for possible use in the index screen.
+ *
+ * Maybe we could figure out some way to do aggregate calls to get
+ * this info for all the lines in view instead of all the one at a
+ * time calls we're doing now.
+ */
+char *
+fetch_firsttext(INDEXDATA_S *idata, int delete_quotes)
+{
+ ENVELOPE *env;
+ BODY *body = NULL;
+ char *firsttext = NULL;
+ STORE_S *so;
+ gf_io_t pc;
+ long partial_fetch_len = 0L;
+ SEARCHSET *ss, **sset;
+
+try_again:
+
+ /*
+ * Prevent wild prefetch, just get the one we're after.
+ * Can we get this somehow in the overview call in build_header_work?
+ */
+ ss = mail_newsearchset();
+ ss->first = idata->rawno;
+ sset = (SEARCHSET **) mail_parameters(idata->stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) idata->stream);
+ if(sset)
+ *sset = ss;
+
+ if((env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)) != NULL){
+ if(body){
+ char *subtype = NULL;
+ char *partno;
+
+ if((body->type == TYPETEXT
+ && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPEMULTIPART
+ && body->nested.part->body.nested.part
+ && body->nested.part->body.nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype))){
+
+ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ char buf[1025], *p;
+ unsigned char c;
+ int success;
+ int one_space_done = 0;
+
+ if(partial_fetch_len == 0L){
+ if(subtype && !strucmp(subtype, "html"))
+ partial_fetch_len = 1024L;
+ else if(subtype && !strucmp(subtype, "plain"))
+ partial_fetch_len = delete_quotes ? 128L : 64L;
+ else
+ partial_fetch_len = 256L;
+ }
+
+ if((body->type == TYPETEXT
+ && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype)))
+ partno = "1";
+ else
+ partno = "1.1";
+
+ gf_set_so_writec(&pc, so);
+ success = get_body_part_text(idata->stream, body, idata->rawno,
+ partno, partial_fetch_len, pc,
+ NULL, NULL,
+ GBPT_NOINTR | GBPT_PEEK |
+ (delete_quotes ? GBPT_DELQUOTES : 0));
+ gf_clear_so_writec(so);
+
+ if(success){
+ so_seek(so, 0L, 0);
+ p = buf;
+ while(p-buf < sizeof(buf)-1 && so_readc(&c, so)){
+ /* delete leading whitespace */
+ if(p == buf && isspace(c))
+ ;
+ /* and include just one space per run of whitespace */
+ else if(isspace(c)){
+ if(!one_space_done){
+ *p++ = SPACE;
+ one_space_done++;
+ }
+ }
+ else{
+ one_space_done = 0;
+ *p++ = c;
+ }
+ }
+
+ *p = '\0';
+
+ if(p > buf){
+ size_t l;
+
+ l = strlen(buf);
+ l += 100;
+ firsttext = fs_get((l+1) * sizeof(char));
+ firsttext[0] = '\0';
+ iutf8ncpy(firsttext, buf, l);
+ firsttext[l] = '\0';
+ removing_trailing_white_space(firsttext);
+ }
+ }
+
+ so_give(&so);
+
+ /* first if means we didn't fetch all of the data */
+ if(!(success > 1 && success < partial_fetch_len)){
+ if(partial_fetch_len < 4096L
+ && (!firsttext || utf8_width(firsttext) < 50)){
+ if(firsttext)
+ fs_give((void **) &firsttext);
+
+ partial_fetch_len = 4096L;
+ goto try_again;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+
+ return(firsttext);
+}
+
+
+/*
+ * fetch_date - called to get at the index entry's "Date:" field
+ */
+char *
+fetch_date(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies date is valid */
+ return(idata->date);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return((char *) env->date);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_header - called to get at the index entry's "Hdrname:" field
+ */
+char *
+fetch_header(INDEXDATA_S *idata, char *hdrname)
+{
+ if(idata->no_fetch)
+ idata->bogus = 1;
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ char *h, *p, *q, *decoded, *fields[2];
+ size_t retsize, decsize;
+ char *ret = NULL;
+ unsigned char *decode_buf = NULL;
+
+ fields[0] = hdrname;
+ fields[1] = NULL;
+ if(hdrname && hdrname[0]
+ && (h = pine_fetchheader_lines(idata->stream, idata->rawno,
+ NULL, fields))){
+
+ if(strlen(h) < strlen(hdrname) + 1){
+ fs_give((void **) &h);
+ return(cpystr(""));
+ }
+
+ /* skip "hdrname:" */
+ for(p = h + strlen(hdrname) + 1;
+ *p && isspace((unsigned char)*p); p++)
+ ;
+
+ decsize = (4 * strlen(p)) + 1;
+ decode_buf = (unsigned char *) fs_get(decsize * sizeof(unsigned char));
+ decoded = (char *) rfc1522_decode_to_utf8(decode_buf, decsize, p);
+ p = decoded;
+
+ retsize = strlen(decoded);
+ q = ret = (char *) fs_get((retsize+1) * sizeof(char));
+
+ *q = '\0';
+ while(q-ret < retsize && *p){
+ if(*p == '\015' || *p == '\012')
+ p++;
+ else if(*p == '\t'){
+ *q++ = SPACE;
+ p++;
+ }
+ else
+ *q++ = *p++;
+ }
+
+ *q = '\0';
+
+ fs_give((void **) &h);
+ if(decode_buf)
+ fs_give((void **) &decode_buf);
+
+ return(ret);
+ }
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_size - called to get at the index entry's "size" field
+ */
+long
+fetch_size(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies size is valid */
+ return(idata->size);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ MESSAGECACHE *mc;
+
+ if(idata->stream && idata->rawno > 0L
+ && idata->rawno <= idata->stream->nmsgs
+ && (mc = mail_elt(idata->stream, idata->rawno)))
+ return(mc->rfc822_size);
+
+ idata->bogus = 1;
+ }
+
+ return(0L);
+}
+
+
+/*
+ * fetch_body - called to get a the index entry's body structure
+ */
+BODY *
+fetch_body(INDEXDATA_S *idata)
+{
+ BODY *body;
+
+ if(idata->bogus || idata->no_fetch){
+ idata->bogus = 2;
+ return(NULL);
+ }
+
+ if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
+ return(body);
+
+ idata->bogus = 1;
+ return(NULL);
+}
+
+
+/*
+ * s is at least size width+1
+ */
+int
+set_index_addr(INDEXDATA_S *idata,
+ char *field,
+ struct mail_address *addr,
+ char *prefix,
+ int width,
+ char *s)
+{
+ ADDRESS *atmp;
+ char *p, *stmp = NULL, *sptr;
+ char *save_personal = NULL;
+ int orig_width;
+
+ s[0] = '\0';
+
+ for(atmp = addr; idata->stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *pref, *h, *fields[2];
+
+ if(idata->no_fetch){
+ idata->bogus = 1;
+ return(TRUE);
+ }
+
+ fields[0] = field;
+ fields[1] = NULL;
+ if((h = pine_fetchheader_lines(idata->stream, idata->rawno,
+ NULL, fields)) != NULL){
+ if(strlen(h) < strlen(field) + 1){
+ p = h + strlen(h);
+ }
+ else{
+ /* skip "field:" */
+ for(p = h + strlen(field) + 1;
+ *p && isspace((unsigned char)*p); p++)
+ ;
+ }
+
+ orig_width = width;
+ sptr = stmp = (char *) fs_get((orig_width+1) * sizeof(char));
+
+ /* add prefix */
+ for(pref = prefix; pref && *pref; pref++)
+ if(width){
+ *sptr++ = *pref;
+ width--;
+ }
+ else
+ break;
+
+ while(width--)
+ if(*p == '\015' || *p == '\012')
+ p++; /* skip CR LF */
+ else if(!*p)
+ *sptr++ = ' ';
+ else if(*p == '\t'){
+ *sptr++ = ' ';
+ p++;
+ }
+ else
+ *sptr++ = *p++;
+
+ *sptr = '\0'; /* tie off return string */
+
+ if(stmp){
+ iutf8ncpy(s, stmp, orig_width+1);
+ s[orig_width] = '\0';
+ fs_give((void **) &stmp);
+ }
+
+ fs_give((void **) &h);
+ return(TRUE);
+ }
+ /* else fall thru and display what c-client gave us */
+ }
+
+ if(addr && !addr->next /* only one address */
+ && addr->host /* not group syntax */
+ && addr->personal && addr->personal[0]){ /* there is a personal name */
+ char buftmp[MAILTMPLEN];
+ int l;
+
+ if((l = prefix ? strlen(prefix) : 0) != 0)
+ strncpy(s, prefix, width+1);
+
+ snprintf(buftmp, sizeof(buftmp), "%s", addr->personal);
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ removing_leading_and_trailing_white_space(p);
+
+ iutf8ncpy(s + l, p, width - l);
+
+ s[width] = '\0';
+
+ if(*(s+l))
+ return(TRUE);
+ else{
+ save_personal = addr->personal;
+ addr->personal = NULL;
+ }
+ }
+
+ if(addr){
+ char *a_string;
+ int l;
+
+ a_string = addr_list_string(addr, NULL, 0);
+ if(save_personal)
+ addr->personal = save_personal;
+
+ if((l = prefix ? strlen(prefix) : 0) != 0)
+ strncpy(s, prefix, width+1);
+
+ iutf8ncpy(s + l, a_string, width - l);
+ s[width] = '\0';
+
+ fs_give((void **)&a_string);
+
+ return(TRUE);
+ }
+
+ if(save_personal)
+ addr->personal = save_personal;
+
+ return(FALSE);
+}
+
+
+void
+index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
+{
+ if(!env){
+ idata->bogus = 2;
+ return;
+ }
+
+ idata->from = env->from;
+ idata->to = env->to;
+ idata->cc = env->cc;
+ idata->sender = env->sender;
+ idata->subject = env->subject;
+ idata->date = (char *) env->date;
+ idata->newsgroups = env->newsgroups;
+
+ idata->valid_to = 1; /* signal that everythings here */
+ idata->valid_cc = 1;
+ idata->valid_sender = 1;
+ idata->valid_news = 1;
+}
+
+
+/*
+ * Put a string representing the date into str. The source date is
+ * in the string datesrc. The format to be used is in type.
+ * Notice that type is an IndexColType, but really only a subset of
+ * IndexColType types are allowed.
+ *
+ * Args datesrc -- The source date string
+ * type -- What type of output we want
+ * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
+ * str -- Put the answer here.
+ * str_len -- Length of str
+ * monabb_width -- This is a hack to get dates to line up right. For
+ * example, in French (but without accents here)
+ * dec. 21
+ * fevr. 23
+ * mars 7
+ * For this monabb_width would be 5.
+ */
+void
+date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len,
+ int monabb_width)
+{
+ char year4[5], /* 4 digit year */
+ yearzero[3], /* zero padded, 2-digit year */
+ monzero[3], /* zero padded, 2-digit month */
+ mon[3], /* 1 or 2-digit month, no pad */
+ dayzero[3], /* zero padded, 2-digit day */
+ day[3], /* 1 or 2-digit day, no pad */
+ dayord[3], /* 2-letter ordinal label */
+ monabb[10], /* 3-letter month abbrev */
+ /* actually maybe not 3 if localized */
+ hour24[3], /* 2-digit, 24 hour clock hour */
+ hour12[3], /* 12 hour clock hour, no pad */
+ minzero[3], /* zero padded, 2-digit minutes */
+ timezone[6]; /* timezone, like -0800 or +... */
+ int hr12;
+ int curtype, lastmonthtype, lastyeartype, preftype;
+ int sdatetimetype, sdatetime24type;
+ struct date d;
+#define TODAYSTR N_("Today")
+
+ curtype = (type == iCurDate ||
+ type == iCurDateIso ||
+ type == iCurDateIsoS ||
+ type == iCurPrefDate ||
+ type == iCurPrefDateTime ||
+ type == iCurPrefTime ||
+ type == iCurTime24 ||
+ type == iCurTime12 ||
+ type == iCurDay ||
+ type == iCurDay2Digit ||
+ type == iCurDayOfWeek ||
+ type == iCurDayOfWeekAbb ||
+ type == iCurMon ||
+ type == iCurMon2Digit ||
+ type == iCurMonLong ||
+ type == iCurMonAbb ||
+ type == iCurYear ||
+ type == iCurYear2Digit);
+ lastmonthtype = (type == iLstMon ||
+ type == iLstMon2Digit ||
+ type == iLstMonLong ||
+ type == iLstMonAbb ||
+ type == iLstMonYear ||
+ type == iLstMonYear2Digit);
+ lastyeartype = (type == iLstYear ||
+ type == iLstYear2Digit);
+ sdatetimetype = (type == iSDateTime ||
+ type == iSDateTimeIso ||
+ type == iSDateTimeIsoS ||
+ type == iSDateTimeS1 ||
+ type == iSDateTimeS2 ||
+ type == iSDateTimeS3 ||
+ type == iSDateTimeS4 ||
+ type == iSDateTime24 ||
+ type == iSDateTimeIso24 ||
+ type == iSDateTimeIsoS24 ||
+ type == iSDateTimeS124 ||
+ type == iSDateTimeS224 ||
+ type == iSDateTimeS324 ||
+ type == iSDateTimeS424);
+ sdatetime24type = (type == iSDateTime24 ||
+ type == iSDateTimeIso24 ||
+ type == iSDateTimeIsoS24 ||
+ type == iSDateTimeS124 ||
+ type == iSDateTimeS224 ||
+ type == iSDateTimeS324 ||
+ type == iSDateTimeS424);
+ preftype = (type == iPrefDate ||
+ type == iPrefDateTime ||
+ type == iPrefTime ||
+ type == iCurPrefDate ||
+ type == iCurPrefDateTime ||
+ type == iCurPrefTime);
+ if(str_len > 0)
+ str[0] = '\0';
+
+ if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
+ return;
+
+ if(curtype || lastmonthtype || lastyeartype){
+ char dbuf[200];
+
+ rfc822_date(dbuf);
+ parse_date(dbuf, &d);
+
+ if(lastyeartype)
+ d.year--;
+ else if(lastmonthtype){
+ d.month--;
+ if(d.month <= 0){
+ d.month = 12;
+ d.year--;
+ }
+ }
+ }
+ else{
+ parse_date(F_ON(F_DATES_TO_LOCAL,ps_global)
+ ? convert_date_to_local(datesrc) : datesrc, &d);
+ if(d.year == -1 || d.month == -1 || d.day == -1){
+ sdatetimetype = 0;
+ sdatetime24type = 0;
+ preftype = 0;
+ switch(type){
+ case iSDate: case iSDateTime: case iSDateTime24:
+ type = iS1Date;
+ break;
+
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ case iPrefDate: case iPrefTime: case iPrefDateTime:
+ type = iDateIso;
+ break;
+
+ case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
+ type = iDateIsoS;
+ break;
+
+ case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
+ type = iS1Date;
+ break;
+
+ case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
+ type = iS1Date;
+ break;
+
+ case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
+ type = iS1Date;
+ break;
+
+ case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
+ type = iS1Date;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* some special ones to start with */
+ if(preftype){
+ struct tm tm, *tmptr = NULL;
+ time_t now;
+
+ /*
+ * Make sure we get the right one if we're using current time.
+ */
+ if(curtype){
+ now = time((time_t *) 0);
+ if(now != (time_t) -1)
+ tmptr = localtime(&now);
+ }
+
+ if(!tmptr){
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
+ tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
+ tm.tm_mday = MIN(MAX(d.day, 1), 31);
+ tm.tm_hour = MIN(MAX(d.hour, 0), 23);
+ tm.tm_min = MIN(MAX(d.minute, 0), 59);
+ tm.tm_wday = MIN(MAX(d.wkday, 0), 6);
+ tmptr = &tm;
+ }
+
+ switch(type){
+ case iPrefDate:
+ case iCurPrefDate:
+ our_strftime(str, str_len, "%x", tmptr);
+ break;
+ case iPrefTime:
+ case iCurPrefTime:
+ our_strftime(str, str_len, "%X", tmptr);
+ break;
+ case iPrefDateTime:
+ case iCurPrefDateTime:
+ our_strftime(str, str_len, "%c", tmptr);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return;
+ }
+
+ strncpy(monabb, (d.month > 0 && d.month < 13)
+ ? month_abbrev_locale(d.month) : "", sizeof(monabb));
+ monabb[sizeof(monabb)-1] = '\0';
+
+ strncpy(mon, (d.month > 0 && d.month < 13)
+ ? int2string(d.month) : "", sizeof(mon));
+ mon[sizeof(mon)-1] = '\0';
+
+ strncpy(day, (d.day > 0 && d.day < 32)
+ ? int2string(d.day) : "", sizeof(day));
+ day[sizeof(day)-1] = '\0';
+
+ strncpy(dayord,
+ (d.day <= 0 || d.day > 31) ? "" :
+ (d.day == 1 || d.day == 21 || d.day == 31) ? "st" :
+ (d.day == 2 || d.day == 22 ) ? "nd" :
+ (d.day == 3 || d.day == 23 ) ? "rd" : "th", sizeof(dayord));
+
+ dayord[sizeof(dayord)-1] = '\0';
+
+ strncpy(year4, (d.year >= 1000 && d.year < 10000)
+ ? int2string(d.year) : "????", sizeof(year4));
+ year4[sizeof(year4)-1] = '\0';
+
+ if(d.year >= 0){
+ if((d.year % 100) < 10){
+ yearzero[0] = '0';
+ strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
+ }
+ else
+ strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
+ }
+ else
+ strncpy(yearzero, "??", sizeof(yearzero));
+
+ yearzero[sizeof(yearzero)-1] = '\0';
+
+ if(d.month > 0 && d.month < 10){
+ monzero[0] = '0';
+ strncpy(monzero+1, int2string(d.month), sizeof(monzero)-1);
+ }
+ else if(d.month >= 10 && d.month <= 12)
+ strncpy(monzero, int2string(d.month), sizeof(monzero));
+ else
+ strncpy(monzero, "??", sizeof(monzero));
+
+ monzero[sizeof(monzero)-1] = '\0';
+
+ if(d.day > 0 && d.day < 10){
+ dayzero[0] = '0';
+ strncpy(dayzero+1, int2string(d.day), sizeof(dayzero)-1);
+ }
+ else if(d.day >= 10 && d.day <= 31)
+ strncpy(dayzero, int2string(d.day), sizeof(dayzero));
+ else
+ strncpy(dayzero, "??", sizeof(dayzero));
+
+ dayzero[sizeof(dayzero)-1] = '\0';
+
+ hr12 = (d.hour == 0) ? 12 :
+ (d.hour > 12) ? (d.hour - 12) : d.hour;
+ hour12[0] = '\0';
+ if(hr12 > 0 && hr12 <= 12)
+ strncpy(hour12, int2string(hr12), sizeof(hour12));
+
+ hour12[sizeof(hour12)-1] = '\0';
+
+ hour24[0] = '\0';
+ if(d.hour >= 0 && d.hour < 10){
+ hour24[0] = '0';
+ strncpy(hour24+1, int2string(d.hour), sizeof(hour24)-1);
+ }
+ else if(d.hour >= 10 && d.hour < 24)
+ strncpy(hour24, int2string(d.hour), sizeof(hour24));
+
+ hour24[sizeof(hour24)-1] = '\0';
+
+ minzero[0] = '\0';
+ if(d.minute >= 0 && d.minute < 10){
+ minzero[0] = '0';
+ strncpy(minzero+1, int2string(d.minute), sizeof(minzero)-1);
+ }
+ else if(d.minute >= 10 && d.minute <= 60)
+ strncpy(minzero, int2string(d.minute), sizeof(minzero));
+
+ minzero[sizeof(minzero)-1] = '\0';
+
+ if(sizeof(timezone) > 5){
+ if(d.hours_off_gmt <= 0){
+ timezone[0] = '-';
+ d.hours_off_gmt *= -1;
+ d.min_off_gmt *= -1;
+ }
+ else
+ timezone[0] = '+';
+
+ timezone[1] = '\0';
+ if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
+ timezone[1] = '0';
+ strncpy(timezone+2, int2string(d.hours_off_gmt), sizeof(timezone)-2);
+ }
+ else if(d.hours_off_gmt >= 10 && d.hours_off_gmt < 24)
+ strncpy(timezone+1, int2string(d.hours_off_gmt), sizeof(timezone)-1);
+ else{
+ timezone[1] = '0';
+ timezone[2] = '0';
+ }
+
+ timezone[3] = '\0';
+ if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
+ timezone[3] = '0';
+ strncpy(timezone+4, int2string(d.min_off_gmt), sizeof(timezone)-4);
+ }
+ else if(d.min_off_gmt >= 10 && d.min_off_gmt <= 60)
+ strncpy(timezone+3, int2string(d.min_off_gmt), sizeof(timezone)-3);
+ else{
+ timezone[3] = '0';
+ timezone[4] = '0';
+ }
+
+ timezone[5] = '\0';
+ timezone[sizeof(timezone)-1] = '\0';
+ }
+
+ switch(type){
+ case iRDate:
+ /* this one is not locale-specific */
+ snprintf(str, str_len, "%s%s%s %s %s",
+ (d.wkday != -1) ? day_abbrev(d.wkday) : "",
+ (d.wkday != -1) ? ", " : "",
+ day,
+ (d.month > 0 && d.month < 13) ? month_abbrev(d.month) : "",
+ year4);
+ break;
+ case iDayOfWeekAbb:
+ case iCurDayOfWeekAbb:
+ strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_abbrev_locale(d.wkday) : "", str_len);
+ str[str_len-1] = '\0';
+ break;
+ case iDayOfWeek:
+ case iCurDayOfWeek:
+ strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_name_locale(d.wkday) : "", str_len);
+ str[str_len-1] = '\0';
+ break;
+ case iYear:
+ case iCurYear:
+ case iLstYear:
+ case iLstMonYear:
+ strncpy(str, year4, str_len);
+ break;
+ case iDay2Digit:
+ case iCurDay2Digit:
+ strncpy(str, dayzero, str_len);
+ break;
+ case iMon2Digit:
+ case iCurMon2Digit:
+ case iLstMon2Digit:
+ strncpy(str, monzero, str_len);
+ break;
+ case iYear2Digit:
+ case iCurYear2Digit:
+ case iLstYear2Digit:
+ case iLstMonYear2Digit:
+ strncpy(str, yearzero, str_len);
+ break;
+ case iTimezone:
+ strncpy(str, timezone, str_len);
+ break;
+ case iDay:
+ case iCurDay:
+ strncpy(str, day, str_len);
+ break;
+ case iDayOrdinal:
+ snprintf(str, str_len, "%s%s", day, dayord);
+ break;
+ case iMon:
+ case iCurMon:
+ case iLstMon:
+ if(d.month > 0 && d.month <= 12)
+ strncpy(str, int2string(d.month), str_len);
+
+ break;
+ case iMonAbb:
+ case iCurMonAbb:
+ case iLstMonAbb:
+ strncpy(str, monabb, str_len);
+ break;
+ case iMonLong:
+ case iCurMonLong:
+ case iLstMonLong:
+ strncpy(str, (d.month > 0 && d.month < 13)
+ ? month_name_locale(d.month) : "", str_len);
+ break;
+ case iDate:
+ case iCurDate:
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+
+ break;
+ case iLDate:
+ if(v)
+ snprintf(str, str_len, "%s%s%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day,
+ ((monabb[0] || day[0]) && year4[0]) ? ", " : "",
+ year4);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s%c %4s",
+ monabb_width, monabb_width,
+ monabb, day,
+ (monabb[0] && day[0] && year4[0]) ? ',' : ' ', year4);
+ else
+ snprintf(str, str_len, "%s %2s%c %4s", monabb, day,
+ (monabb[0] && day[0] && year4[0]) ? ',' : ' ',
+ year4);
+ }
+
+ break;
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIso:
+ case iDateIsoS:
+ case iCurDateIso:
+ case iCurDateIsoS:
+ if(monzero[0] == '?' && dayzero[0] == '?' &&
+ yearzero[0] == '?')
+ snprintf(str, str_len, "%8s", "");
+ else{
+ switch(type){
+ case iS1Date:
+ snprintf(str, str_len, "%2s/%2s/%2s",
+ monzero, dayzero, yearzero);
+ break;
+ case iS2Date:
+ snprintf(str, str_len, "%2s/%2s/%2s",
+ dayzero, monzero, yearzero);
+ break;
+ case iS3Date:
+ snprintf(str, str_len, "%2s.%2s.%2s",
+ dayzero, monzero, yearzero);
+ break;
+ case iS4Date:
+ snprintf(str, str_len, "%2s.%2s.%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iDateIsoS:
+ case iCurDateIsoS:
+ snprintf(str, str_len, "%2s-%2s-%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iDateIso:
+ case iCurDateIso:
+ snprintf(str, str_len, "%4s-%2s-%2s",
+ year4, monzero, dayzero);
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+ case iTime24:
+ case iCurTime24:
+ snprintf(str, str_len, "%2s%c%2s",
+ (hour24[0] && minzero[0]) ? hour24 : "",
+ (hour24[0] && minzero[0]) ? ':' : ' ',
+ (hour24[0] && minzero[0]) ? minzero : "");
+ break;
+ case iTime12:
+ case iCurTime12:
+ snprintf(str, str_len, "%s%c%2s%s",
+ (hour12[0] && minzero[0]) ? hour12 : "",
+ (hour12[0] && minzero[0]) ? ':' : ' ',
+ (hour12[0] && minzero[0]) ? minzero : "",
+ (hour12[0] && minzero[0] && d.hour < 12) ? "am" :
+ (hour12[0] && minzero[0] && d.hour >= 12) ? "pm" :
+ " ");
+ break;
+ case iSDate: case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ { struct date now, last_day;
+ char dbuf[200];
+ int msg_day_of_year, now_day_of_year, today;
+ int diff, ydiff, last_day_of_year;
+
+ rfc822_date(dbuf);
+ parse_date(dbuf, &now);
+ today = day_of_week(&now) + 7;
+
+ if(today >= 0+7 && today <= 6+7){
+ now_day_of_year = day_of_year(&now);
+ msg_day_of_year = day_of_year(&d);
+ ydiff = now.year - d.year;
+
+ if(msg_day_of_year == -1)
+ diff = -100;
+ else if(ydiff == 0)
+ diff = now_day_of_year - msg_day_of_year;
+ else if(ydiff == 1){
+ last_day = d;
+ last_day.month = 12;
+ last_day.day = 31;
+ last_day_of_year = day_of_year(&last_day);
+
+ diff = now_day_of_year +
+ (last_day_of_year - msg_day_of_year);
+ }
+ else if(ydiff == -1){
+ last_day = now;
+ last_day.month = 12;
+ last_day.day = 31;
+ last_day_of_year = day_of_year(&last_day);
+
+ diff = -1 * (msg_day_of_year +
+ (last_day_of_year - now_day_of_year));
+ }
+ else if(ydiff > 1)
+ diff = 100;
+ else
+ diff = -100;
+
+ if(diff == 0)
+ strncpy(str, _(TODAYSTR), str_len);
+ else if(diff == 1)
+ strncpy(str, _("Yesterday"), str_len);
+ else if(diff > 1 && diff < 7)
+ snprintf(str, str_len, "%s", day_name_locale((today - diff) % 7));
+ else if(diff == -1)
+ strncpy(str, _("Tomorrow"), str_len);
+ else if(diff < -1 && diff > -7)
+ snprintf(str, str_len, _("Next %.3s!"),
+ day_name_locale((today - diff) % 7));
+ else if(diff > 0
+ && (ydiff == 0
+ || (ydiff == 1 && 12 + now.month - d.month < 6))){
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+ }
+ else{
+ if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
+ type = iSDateTimeIsoS;
+
+ switch(type){
+ case iSDate: case iSDateTime: case iSDateTime24:
+ {
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
+ tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
+ tm.tm_mday = MIN(MAX(d.day, 1), 31);
+ tm.tm_hour = MIN(MAX(d.hour, 0), 23);
+ tm.tm_min = MIN(MAX(d.minute, 0), 59);
+ our_strftime(str, str_len, "%x", &tm);
+ }
+
+ break;
+ case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
+ if(v)
+ snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s/%s/%s%s",
+ (mon[0] && mon[1]) ? "" : " ",
+ mon, dayzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
+ if(v)
+ snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s/%s/%s%s",
+ (day[0] && day[1]) ? "" : " ",
+ day, monzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
+ if(v)
+ snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s.%s.%s%s",
+ (day[0] && day[1]) ? "" : " ",
+ day, monzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
+ if(v)
+ snprintf(str, str_len, "%s.%s.%s%s",
+ yearzero, monzero, dayzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s.%s.%s%s",
+ yearzero, monzero, dayzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
+ snprintf(str, str_len, "%2s-%2s-%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ snprintf(str, str_len, "%4s-%2s-%2s",
+ year4, monzero, dayzero);
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+ else{
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ str[str_len-1] = '\0';
+
+ if(type == iSTime ||
+ (sdatetimetype && !strcmp(str, _(TODAYSTR)))){
+ struct date now, last_day;
+ char dbuf[200], *Ddd, *ampm;
+ int daydiff;
+
+ str[0] = '\0';
+ rfc822_date(dbuf);
+ parse_date(dbuf, &now);
+
+ /* Figure out if message date lands in the past week */
+
+ /* (if message dated this month or last month...) */
+ if((d.year == now.year && d.month >= now.month - 1) ||
+ (d.year == now.year - 1 && d.month == 12 && now.month == 1)){
+
+ daydiff = day_of_year(&now) - day_of_year(&d);
+
+ /*
+ * If msg in end of last year (and we're in first bit of "this"
+ * year), diff will be backwards; fix up by adding number of days
+ * in last year (usually 365, but occasionally 366)...
+ */
+ if(d.year == now.year - 1){
+ last_day = d;
+ last_day.month = 12;
+ last_day.day = 31;
+
+ daydiff += day_of_year(&last_day);
+ }
+ }
+ else
+ daydiff = -100; /* comfortably out of range (of past week) */
+
+ /* Build 2-digit hour and am/pm indicator, used below */
+
+ if(d.hour >= 0 && d.hour < 24){
+ snprintf(hour12, sizeof(hour12), "%02d", (d.hour % 12 == 0) ? 12 : d.hour % 12);
+ ampm = (d.hour < 12) ? "am" : "pm";
+ snprintf(hour24, sizeof(hour24), "%02d", d.hour);
+ }
+ else{
+ strncpy(hour12, "??", sizeof(hour12));
+ hour12[sizeof(hour12)-1] = '\0';
+ ampm = "__";
+ strncpy(hour24, "??", sizeof(hour24));
+ hour24[sizeof(hour24)-1] = '\0';
+ }
+
+ /* Build date/time in str, in format similar to that used by w(1) */
+
+ if(daydiff == 0){ /* If date is today, "HH:MMap" */
+ if(d.minute >= 0 && d.minute < 60)
+ snprintf(minzero, sizeof(minzero), "%02d", d.minute);
+ else{
+ strncpy(minzero, "??", sizeof(minzero));
+ minzero[sizeof(minzero)-1] = '\0';
+ }
+
+ snprintf(str, str_len, "%s:%s%s", sdatetime24type ? hour24 : hour12,
+ minzero, sdatetime24type ? "" : ampm);
+ }
+ else if(daydiff >= 1 && daydiff < 6){ /* If <1wk ago, "DddHHap" */
+
+ if(d.month >= 1 && d.day >= 1 && d.year >= 0 &&
+ d.month <= 12 && d.day <= 31 && d.year <= 9999)
+ Ddd = day_abbrev_locale(day_of_week(&d));
+ else
+ Ddd = "???";
+
+ snprintf(str, str_len, "%s%s%s", Ddd, hour12, ampm);
+ }
+ else{ /* date is old or future, "ddMmmyy" */
+ strncpy(monabb, (d.month >= 1 && d.month <= 12)
+ ? month_abbrev_locale(d.month) : "???", sizeof(monabb));
+ monabb[sizeof(monabb)-1] = '\0';
+
+ if(d.day >= 1 && d.day <= 31)
+ snprintf(dayzero, sizeof(dayzero), "%02d", d.day);
+ else{
+ strncpy(dayzero, "??", sizeof(dayzero));
+ dayzero[sizeof(dayzero)-1] = '\0';
+ }
+
+ if(d.year >= 0 && d.year <= 9999)
+ snprintf(yearzero, sizeof(yearzero), "%02d", d.year % 100);
+ else{
+ strncpy(yearzero, "??", sizeof(yearzero));
+ yearzero[sizeof(yearzero)-1] = '\0';
+ }
+
+ snprintf(str, str_len, "%s%s%s", dayzero, monabb, yearzero);
+ }
+
+ if(str[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
+ if(v)
+ memmove(str, str + 1, strlen(str));
+ else
+ str[0] = ' ';
+ }
+ }
+}
+
+
+/*
+ * Format a string representing the keywords into ice.
+ *
+ * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
+ *
+ * Args idata -- which message?
+ * kwtype -- keywords or kw initials
+ * ice -- index cache entry for message
+ */
+void
+key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
+{
+ int firstone = 1;
+ KEYWORD_S *kw;
+ char *word;
+ COLOR_PAIR *color = NULL;
+ SPEC_COLOR_S *sc = ps_global->kw_colors;
+ IELEM_S *ielem = NULL;
+ IFIELD_S *ourifield = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ if(kwtype == KWInit){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
+ word = (kw->nick && kw->nick[0]) ? kw->nick :
+ (kw->kw && kw->kw[0]) ? kw->kw : "";
+
+ /*
+ * Pick off the first initial. Since word is UTF-8 it may
+ * take more than one byte for the first initial.
+ */
+
+ if(word && word[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(word);
+ inputp = (unsigned char *) word;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->datalen = (unsigned) (inputp - (unsigned char *) word);
+ ielem->data = (char *) fs_get((ielem->datalen + 1) * sizeof(char));
+ strncpy(ielem->data, word, ielem->datalen);
+ ielem->data[ielem->datalen] = '\0';
+
+ if(pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem->color = color;
+ color = NULL;
+ }
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+ else if(kwtype == KW){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
+
+ if(!firstone){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ firstone = 0;
+
+ word = (kw->nick && kw->nick[0]) ? kw->nick :
+ (kw->kw && kw->kw[0]) ? kw->kw : "";
+
+ if(word[0]){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(word);
+ ielem->datalen = strlen(word);
+
+ if(pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem->color = color;
+ color = NULL;
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+
+ /*
+ * If we're coloring some of the fields then add a dummy field
+ * at the end that can soak up the rest of the space after the last
+ * colored keyword. Otherwise, the last one's color will extend to
+ * the end of the field.
+ */
+ if(pico_usingcolor()){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ ourifield->leftadj = 1;
+ set_ielem_widths_in_field(ourifield);
+}
+
+
+void
+prio_str(INDEXDATA_S *idata, IndexColType ctype, ICE_S *ice)
+{
+ IFIELD_S *ourifield = NULL;
+ IELEM_S *ielem = NULL;
+ char *hdrval;
+ PRIORITY_S *p;
+ int v;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ hdrval = fetch_header(idata, PRIORITYNAME);
+
+ if(hdrval && hdrval[0] && isdigit(hdrval[0])){
+ v = atoi(hdrval);
+ if(v >= 1 && v <= 5 && v != 3){
+
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+
+ switch(ctype){
+ case iPrio:
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = hdrval[0];
+ ielem->data[1] = '\0';
+ break;
+
+ case iPrioAlpha:
+ for(p = priorities; p && p->desc; p++)
+ if(p->val == v)
+ break;
+
+ if(p && p->desc)
+ ielem->data = cpystr(p->desc);
+
+ break;
+
+ case iPrioBang:
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = '\0';
+ switch(v){
+ case 1: case 2:
+ ielem->data[0] = '!';
+ break;
+
+ case 4: case 5:
+ /*
+ * We could put a Unicode downarrow in here but
+ * we have no way of knowing if the user's font
+ * will have it (I think).
+ */
+ ielem->data[0] = 'v';
+ break;
+ }
+
+ ielem->data[1] = '\0';
+
+ break;
+
+ default:
+ panic("Unhandled case in prio_str");
+ break;
+ }
+
+ if(!ielem->data)
+ ielem->data = cpystr("");
+
+ if(ielem && ielem->data)
+ ielem->datalen = strlen(ielem->data);
+
+ if((v == 1 || v == 2) && pico_usingcolor()
+ && ps_global->VAR_IND_HIPRI_FORE_COLOR
+ && ps_global->VAR_IND_HIPRI_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_HIPRI_FORE_COLOR, ps_global->VAR_IND_HIPRI_BACK_COLOR);
+ }
+ else if((v == 4 || v == 5) && pico_usingcolor()
+ && ps_global->VAR_IND_LOPRI_FORE_COLOR
+ && ps_global->VAR_IND_LOPRI_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_LOPRI_FORE_COLOR, ps_global->VAR_IND_LOPRI_BACK_COLOR);
+ }
+
+ ourifield->leftadj = 1;
+ set_ielem_widths_in_field(ourifield);
+ }
+
+ fs_give((void **) &hdrval);
+ }
+}
+
+
+void
+header_str(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok, ICE_S *ice)
+{
+ IFIELD_S *ourifield = NULL;
+ IELEM_S *ielem = NULL;
+ char *fieldval = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ fieldval = get_fieldval(idata, hdrtok);
+
+ if(fieldval){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = fieldval;
+ ielem->datalen = strlen(fieldval);
+ fieldval = NULL;
+ ourifield->leftadj = (hdrtok->adjustment == Left) ? 1 : 0;
+ }
+
+ set_ielem_widths_in_field(ourifield);
+}
+
+
+char *
+get_fieldval(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok)
+{
+ int sep, fieldnum;
+ char *hdrval = NULL, *testval;
+ char *fieldval = NULL, *firstval;
+ char *retfieldval = NULL;
+
+ if(!hdrtok)
+ return(retfieldval);
+
+ if(hdrtok && hdrtok->hdrname && hdrtok->hdrname[0])
+ hdrval = fetch_header(idata, hdrtok ? hdrtok->hdrname : "");
+
+ /* find start of fieldnum'th field */
+ fieldval = hdrval;
+ for(fieldnum = MAX(hdrtok->fieldnum-1, 0);
+ fieldnum > 0 && fieldval && *fieldval; fieldnum--){
+
+ firstval = NULL;
+ for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
+ testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
+ if(testval && (!firstval || testval < firstval))
+ firstval = testval;
+ }
+
+ fieldval = firstval;
+ if(fieldval && *fieldval)
+ fieldval++;
+ }
+
+ /* tie off end of field */
+ if(fieldval && *fieldval && hdrtok->fieldnum > 0){
+ firstval = NULL;
+ for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
+ testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
+ if(testval && (!firstval || testval < firstval))
+ firstval = testval;
+ }
+
+ if(firstval)
+ *firstval = '\0';
+ }
+
+ if(!fieldval)
+ fieldval = "";
+
+ retfieldval = cpystr(fieldval);
+
+ if(hdrval)
+ fs_give((void **) &hdrval);
+
+ return(retfieldval);
+}
+
+
+long
+scorevalfrommsg(MAILSTREAM *stream, MsgNo rawno, HEADER_TOK_S *hdrtok, int no_fetch)
+{
+ INDEXDATA_S idata;
+ MESSAGECACHE *mc;
+ char *fieldval = NULL;
+ long retval = 0L;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.stream = stream;
+ idata.no_fetch = no_fetch;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
+ idata.rawno = rawno;
+ if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, idata.rawno))){
+ idata.size = mc->rfc822_size;
+ index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
+ }
+ else
+ idata.bogus = 2;
+
+ fieldval = get_fieldval(&idata, hdrtok);
+
+ if(fieldval){
+ retval = atol(fieldval);
+ fs_give((void **) &fieldval);
+ }
+
+ return(retval);
+}
+
+
+/*
+ * Put a string representing the subject into str. Idata tells us which
+ * message we are referring to.
+ *
+ * This means we should ensure that all data ends up being UTF-8 data.
+ * That covers the data in ice ielems and str.
+ *
+ * Args idata -- which message?
+ * str -- destination buffer
+ * strsize -- size of str buffer
+ * kwtype -- prepend keywords or kw initials before the subject
+ * opening -- add first text from body of message if there's room
+ * ice -- index cache entry for message
+ */
+void
+subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int opening, ICE_S *ice)
+{
+ char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
+ char *p, *border, *q = NULL, *free_subj = NULL;
+ char *sp;
+ size_t len;
+ int width = -1;
+ int depth = 0, mult = 2;
+ int save;
+ int do_subj = 0, truncated_tree = 0;
+ PINETHRD_S *thd, *thdorig;
+ IELEM_S *ielem = NULL, *subjielem = NULL;
+ IFIELD_S *ourifield = NULL;
+
+ if(strsize <= 0)
+ return;
+
+ /*
+ * If we need the data at the start of the message and we're in
+ * a c-client callback, defer the data lookup until later.
+ */
+ if(opening && idata->no_fetch){
+ idata->bogus = 1;
+ return;
+ }
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working on */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ str[0] = str[strsize-1] = '\0';
+ origstr = str;
+ rawsubj = fetch_subject(idata);
+ if(!rawsubj)
+ rawsubj = "";
+
+ /*
+ * Before we do anything else, decode the character set in the subject and
+ * work with the result.
+ */
+ sp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, rawsubj);
+
+ len = strlen(sp);
+ len += 100; /* for possible charset, escaped characters */
+ origsubj = fs_get((len+1) * sizeof(unsigned char));
+ origsubj[0] = '\0';
+
+ iutf8ncpy(origsubj, sp, len);
+
+ origsubj[len] = '\0';
+ removing_trailing_white_space(origsubj);
+
+ /*
+ * origsubj is the original subject but it has been decoded. We need
+ * to free it at the end of this routine.
+ */
+
+
+ /*
+ * prepend_keyword will put the keyword stuff before the subject
+ * and split the subject up into its colored parts in subjielem.
+ * Subjielem is a local ielem which will have to be fit into the
+ * real ifield->ielem later. The print_format strings in subjielem will
+ * not be filled in by prepend_keyword because of the fact that we
+ * may have to adjust things for threading below.
+ * We use subjielem in case we want to insert some threading information
+ * at the front of the subject.
+ */
+ if(kwtype == KW || kwtype == KWInit){
+ subject = prepend_keyword_subject(idata->stream, idata->rawno,
+ origsubj, kwtype,
+ ourifield ? &subjielem : NULL,
+ ps_global->VAR_KW_BRACES);
+ free_subj = subject;
+ }
+ else{
+ subject = origsubj;
+ if(ourifield){
+ subjielem = new_ielem(&subjielem);
+ subjielem->type = eTypeCol;
+ subjielem->freedata = 1;
+ subjielem->data = cpystr(subject);
+ subjielem->datalen = strlen(subject);
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ subjielem->freecolor = 1;
+ subjielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+ }
+
+ /*
+ * This space is here so that if the subject does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ if(!opening){
+ ielem = new_ielem(&subjielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ if(!subject)
+ subject = "";
+
+ if(THREADING()
+ && (ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
+
+ /*
+ * Why do we want to truncate the subject and from strs?
+ * It's so we can put the [5] thread count things in below when
+ * we are threading and the thread structure runs off the right
+ * hand edge of the screen. This routine doesn't know that it
+ * is running off the edge unless it knows the actual width
+ * that we have to draw in.
+ */
+ if(pith_opt_truncate_sfstr
+ && (*pith_opt_truncate_sfstr)()
+ && ourifield
+ && ourifield->width > 0)
+ width = ourifield->width;
+
+ if(width < 0)
+ width = strsize-1;
+
+ width = MIN(width, strsize-1);
+
+ /*
+ * We're counting on the fact that this initial part of the
+ * string is ascii and we have one octet per character and
+ * characters are width 1 on the screen.
+ */
+ border = str + width;
+
+ thdorig = thd = fetch_thread(idata->stream, idata->rawno);
+
+ if(pith_opt_condense_thread_cue)
+ width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
+ thd && thd->next
+ && get_lflag(idata->stream,
+ NULL,idata->rawno,
+ MN_COLL));
+
+ /*
+ * width is < available strsize and
+ * border points to something less than or equal
+ * to the end of the buffer.
+ */
+
+ sptr = str;
+
+ if(thd)
+ while(thd->parent &&
+ (thd = fetch_thread(idata->stream, thd->parent)))
+ depth++;
+
+ if(depth > 0){
+ if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
+ mult = 1;
+
+ sptr += (mult*depth);
+ for(thd = thdorig, p = str + mult*depth - mult;
+ thd && thd->parent && p >= str;
+ thd = fetch_thread(idata->stream, thd->parent), p -= mult){
+ if(p + mult >= border && !q){
+ if(width >= 4 && depth < 100){
+ snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
+ q = str + width-4;
+ }
+ else if(width >= 5 && depth < 1000){
+ snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
+ q = str + width-5;
+ }
+ else{
+ snprintf(str, width+1, "%s", repeat_char(width, '.'));
+ q = str;
+ }
+
+ border = q;
+ truncated_tree++;
+ }
+
+ if(p < border){
+ p[0] = ' ';
+ if(p + 1 < border)
+ p[1] = ' ';
+
+ if(ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+
+ if(p + 1 < border && thd == thdorig)
+ p[1] = '-';
+ }
+ }
+ }
+ }
+
+ if(sptr && !truncated_tree){
+ /*
+ * Look to see if the subject is the same as the previous
+ * message in the thread, if any. If it is the same, don't
+ * reprint the subject.
+ *
+ * Note that when we're prepending keywords to the subject,
+ * and the user changes a keyword, we do invalidate
+ * the index cache for that message but we don't go to the
+ * trouble of invalidating the index cache for the the child
+ * of that node in the thread, so the MUTT subject line
+ * display for the child may be wrong. That is, it may show
+ * it is the same as this subject even though it no longer
+ * is, or vice versa.
+ */
+ if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ if(depth == 0)
+ do_subj++;
+ else{
+ if(thdorig->parent &&
+ (thd = fetch_thread(idata->stream, thdorig->parent))
+ && thd->rawno){
+ char *this_orig = NULL,
+ *prev_orig = NULL,
+ *free_prev_orig = NULL,
+ *this_prep = NULL, /* includes prepend */
+ *prev_prep = NULL;
+ ENVELOPE *env;
+ mailcache_t mc;
+ SORTCACHE *sc = NULL;
+
+ /* get the stripped subject of previous message */
+ mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
+ if(mc)
+ sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
+
+ if(sc && sc->subject)
+ prev_orig = sc->subject;
+ else{
+ char *stripthis;
+
+ env = pine_mail_fetchenvelope(idata->stream,
+ thd->rawno);
+ stripthis = (env && env->subject)
+ ? env->subject : "";
+
+ mail_strip_subject(stripthis, &prev_orig);
+
+ free_prev_orig = prev_orig;
+ }
+
+ mail_strip_subject(rawsubj, &this_orig);
+
+ if(kwtype == KW || kwtype == KWInit){
+ prev_prep = prepend_keyword_subject(idata->stream,
+ thd->rawno,
+ prev_orig,
+ kwtype, NULL,
+ ps_global->VAR_KW_BRACES);
+
+ this_prep = prepend_keyword_subject(idata->stream,
+ idata->rawno,
+ this_orig,
+ kwtype, NULL,
+ ps_global->VAR_KW_BRACES);
+
+ if((this_prep || prev_prep)
+ && ((this_prep && !prev_prep)
+ || (prev_prep && !this_prep)
+ || strucmp(this_prep, prev_prep)))
+ do_subj++;
+ }
+ else{
+ if((this_orig || prev_orig)
+ && ((this_orig && !prev_orig)
+ || (prev_orig && !this_orig)
+ || strucmp(this_orig, prev_orig)))
+ do_subj++;
+ }
+
+ /*
+ * If some of the thread is zoomed out of view, we
+ * want to display the subject of the first one that
+ * is in view. If any of the parents or grandparents
+ * etc of this message are visible, then we don't
+ * need to worry about it. If all of the parents have
+ * been zoomed away, then this is the first one.
+ *
+ * When you're looking at a particular case where
+ * some of the messages of a thread are selected it
+ * seems like we should look at not only our
+ * direct parents, but the siblings of the parent
+ * too. But that's not really correct, because those
+ * siblings are basically the starts of different
+ * branches, separate from our branch. They could
+ * have their own subjects, for example. This will
+ * give us cases where it looks like we are showing
+ * the subject too much, but it will be correct!
+ *
+ * In zoom_index() we clear_index_cache_ent for
+ * some lines which have subjects which might become
+ * visible when we zoom, and also in set_lflags
+ * where we might change subjects by unselecting
+ * something when zoomed.
+ */
+ if(!do_subj){
+ while(thd){
+ if(!msgline_hidden(idata->stream,
+ sp_msgmap(idata->stream),
+ mn_raw2m(sp_msgmap(idata->stream),
+ (long) thd->rawno),
+ 0)){
+ break; /* found a visible parent */
+ }
+
+ if(thd && thd->parent)
+ thd = fetch_thread(idata->stream,thd->parent);
+ else
+ thd = NULL;
+ }
+
+ if(!thd) /* none were visible */
+ do_subj++;
+ }
+
+ if(this_orig)
+ fs_give((void **) &this_orig);
+
+ if(this_prep)
+ fs_give((void **) &this_prep);
+
+ if(free_prev_orig)
+ fs_give((void **) &free_prev_orig);
+
+ if(prev_prep)
+ fs_give((void **) &prev_prep);
+ }
+ else
+ do_subj++;
+ }
+ }
+ else
+ do_subj++;
+
+ if(do_subj){
+ /*
+ * We don't need to worry about truncating to width
+ * here. If we go over the right hand edge it will be
+ * truncated.
+ */
+ strsize -= (sptr - str);
+
+ strncpy(sptr, subject, strsize-1);
+ sptr[strsize-1] = '\0';
+ }
+ else if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ strsize -= (sptr - str);
+
+ if(strsize > 0){
+ sptr[0] = '>';
+ sptr++;
+ }
+
+ /*
+ * We decided we don't need the subject so we'd better
+ * eliminate subjielem.
+ */
+ free_ielem(&subjielem);
+ }
+ }
+ else
+ free_ielem(&subjielem); /* no room for actual subject */
+
+ if(ourifield && sptr && sptr > origstr){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->type = eThreadInfo;
+ ielem->freedata = 1;
+ save = *sptr;
+ *sptr = '\0';
+ ielem->data = cpystr(origstr);
+ ielem->datalen = strlen(origstr);
+ *sptr = save;
+ }
+ }
+ else{
+ /*
+ * Not much to do for the non-threading case. Just copy the
+ * subject we have so far into str and truncate it.
+ */
+ strncpy(str, subject, strsize-1);
+ str[strsize-1] = '\0';
+ }
+
+ if(ourifield){
+ /*
+ * We need to add subjielem to the end of the ourifield->ielem list.
+ */
+ if(subjielem){
+ if(ourifield->ielem){
+ for(ielem = ourifield->ielem;
+ ielem && ielem->next; ielem = ielem->next)
+ ;
+
+ ielem->next = subjielem;
+ }
+ else
+ ourifield->ielem = subjielem;
+ }
+
+ ourifield->leftadj = 1;
+ }
+
+ if(opening && ourifield){
+ IELEM_S *ftielem = NULL;
+ size_t len;
+ char *first_text;
+
+ first_text = fetch_firsttext(idata, 0);
+
+ if(first_text){
+ char sep[200];
+ int seplen;
+
+ strncpy(sep, ps_global->VAR_OPENING_SEP ? ps_global->VAR_OPENING_SEP : " - ",
+ sizeof(sep));
+ sep[sizeof(sep)-1] = '\0';
+ removing_double_quotes(sep);
+ seplen = strlen(sep);
+
+ ftielem = new_ielem(&ftielem);
+ ftielem->type = eTypeCol;
+ ftielem->freedata = 1;
+ len = strlen(first_text) + seplen;
+ ftielem->data = (char *) fs_get((len + 1) * sizeof(char));
+
+ strncpy(ftielem->data, sep, seplen);
+ strncpy(ftielem->data+seplen, first_text, len+1-seplen);
+ ftielem->data[len] = '\0';
+
+ ftielem->datalen = strlen(ftielem->data);
+ if(first_text)
+ fs_give((void **) &first_text);
+
+ if(ftielem){
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_OP_FORE_COLOR
+ && ps_global->VAR_IND_OP_BACK_COLOR){
+ ftielem->freecolor = 1;
+ ftielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR, ps_global->VAR_IND_OP_BACK_COLOR);
+
+ /*
+ * This space is here so that if the opening text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ftielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ if(ourifield->ielem){
+ for(ielem = ourifield->ielem;
+ ielem && ielem->next; ielem = ielem->next)
+ ;
+
+ ielem->next = ftielem;
+ }
+ else
+ ourifield->ielem = ftielem;
+ }
+
+ ourifield->leftadj = 1;
+ }
+ }
+
+ if(ourifield)
+ set_ielem_widths_in_field(ourifield);
+
+ if(origsubj)
+ fs_give((void **) &origsubj);
+
+ if(free_subj)
+ fs_give((void **) &free_subj);
+}
+
+
+/*
+ * Returns an allocated string which is the passed in subject with a
+ * list of keywords prepended.
+ *
+ * If kwtype == KW you will end up with
+ *
+ * {keyword1 keyword2} subject
+ *
+ * (actually, keyword nicknames will be used instead of the actual keywords
+ * in the case that the user defined nicknames)
+ *
+ * If kwtype == KWInit you get
+ *
+ * {AB} subject
+ *
+ * where A is the first letter of the first keyword and B is the first letter
+ * of the second defined keyword. No space between them. There could be more
+ * than two.
+ *
+ * If an ielemp is passed in it will be filled out with the data and colors
+ * of the pieces of the subject but the print_format strings will not
+ * be set.
+ */
+char *
+prepend_keyword_subject(MAILSTREAM *stream, long int rawno, char *subject,
+ SubjKW kwtype, IELEM_S **ielemp, char *braces)
+{
+ char *p, *next_piece, *retsubj = NULL, *str;
+ char *left_brace = NULL, *right_brace = NULL;
+ size_t len;
+ int some_set = 0, save;
+ IELEM_S *ielem;
+ KEYWORD_S *kw;
+ COLOR_PAIR *color = NULL;
+ SPEC_COLOR_S *sc = ps_global->kw_colors;
+
+ if(!subject)
+ subject = "";
+
+ if(braces && *braces)
+ get_pair(braces, &left_brace, &right_brace, 1, 0);
+
+ len = (left_brace ? strlen(left_brace) : 0) +
+ (right_brace ? strlen(right_brace) : 0);
+
+ if(stream && rawno >= 0L && rawno <= stream->nmsgs){
+ for(kw = ps_global->keywords; kw; kw = kw->next)
+ if(user_flag_is_set(stream, rawno, kw->kw)){
+ if(kwtype == KW){
+ if(some_set)
+ len++; /* space between keywords */
+
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ len += strlen(str);
+ }
+ else if(kwtype == KWInit){
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ /* interested in only the first UTF-8 initial */
+ if(str && str[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(str);
+ inputp = (unsigned char *) str;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ len += (unsigned) (inputp - (unsigned char *) str);
+ }
+ }
+ }
+
+ some_set++;
+ }
+ }
+
+ if((kwtype == KW || kwtype == KWInit) && some_set){
+ len += strlen(subject); /* subject is already UTF-8 if needed */
+ retsubj = (char *) fs_get((len + 1) * sizeof(*retsubj));
+ memset(retsubj, 0, (len + 1) * sizeof(*retsubj));
+ next_piece = p = retsubj;
+
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(stream, rawno, kw->kw)){
+ if(p == retsubj){
+ if(left_brace && len > 0)
+ sstrncpy(&p, left_brace, len);
+ }
+ else if(kwtype == KW)
+ *p++ = ' ';
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ }
+
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+
+ if(kwtype == KWInit){
+ if(str && str[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(str);
+ inputp = (unsigned char *) str;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ if(len-(p-retsubj) > 0){
+ sstrncpy(&p, str, MIN(inputp - (unsigned char *) str,len-(p-retsubj)));
+ if(p > next_piece && ielemp && pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ save = *p;
+ *p = '\0';
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ ielem->color = color;
+ color = NULL;
+ *p = save;
+ next_piece = p;
+ }
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ else{
+ if(len-(p-retsubj) > 0)
+ sstrncpy(&p, str, len-(p-retsubj));
+
+ if(p > next_piece && ielemp && pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ save = *p;
+ *p = '\0';
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ ielem->color = color;
+ color = NULL;
+ *p = save;
+ next_piece = p;
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+
+ if(len-(p-retsubj) > 0 && right_brace)
+ sstrncpy(&p, right_brace, len-(p-retsubj));
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ }
+
+ if(len-(p-retsubj) > 0 && subject)
+ sstrncpy(&p, subject, len-(p-retsubj));
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->type = eTypeCol;
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+
+ retsubj[len] = '\0'; /* just making sure */
+ }
+ else{
+ if(ielemp){
+ ielem = new_ielem(ielemp);
+ ielem->type = eTypeCol;
+ ielem->freedata = 1;
+ ielem->data = cpystr(subject);
+ ielem->datalen = strlen(subject);
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+
+ retsubj = cpystr(subject);
+ }
+
+ if(braces){
+ if(left_brace)
+ fs_give((void **) &left_brace);
+
+ if(right_brace)
+ fs_give((void **) &right_brace);
+ }
+
+ return(retsubj);
+}
+
+
+/*
+ * This means we should ensure that all data ends up being UTF-8 data.
+ * That covers the data in ice ielems and str.
+ */
+void
+from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_S *ice)
+{
+ char *field, *newsgroups, *border, *p, *fptr = NULL, *q = NULL;
+ ADDRESS *addr;
+ int width = -1;
+ int depth = 0, mult = 2;
+ PINETHRD_S *thd, *thdorig;
+
+ if(THREADING()
+ && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
+ || ps_global->thread_disp_style == THREAD_INDENT_FROM2
+ || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
+
+ if(pith_opt_truncate_sfstr && (*pith_opt_truncate_sfstr)()){
+ IFIELD_S *ourifield = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working on */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(ourifield && ourifield->width > 0)
+ width = ourifield->width;
+ }
+
+ if(width < 0)
+ width = strsize-1;
+
+ width = MIN(width, strsize-1);
+
+ thdorig = thd = fetch_thread(idata->stream, idata->rawno);
+ border = str + width;
+ if(pith_opt_condense_thread_cue)
+ width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
+ thd && thd->next
+ && get_lflag(idata->stream,
+ NULL,idata->rawno,
+ MN_COLL));
+
+ fptr = str;
+
+ if(thd)
+ while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
+ depth++;
+
+ if(depth > 0){
+ if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
+ mult = 1;
+
+ fptr += (mult*depth);
+ for(thd = thdorig, p = str + mult*depth - mult;
+ thd && thd->parent && p >= str;
+ thd = fetch_thread(idata->stream, thd->parent), p -= mult){
+ if(p + mult >= border && !q){
+ if(width >= 4 && depth < 100){
+ snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
+ q = str + width-4;
+ }
+ else if(width >= 5 && depth < 1000){
+ snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
+ q = str + width-5;
+ }
+ else{
+ snprintf(str, width+1, "%s", repeat_char(width, '.'));
+ q = str;
+ }
+
+ border = q;
+ fptr = NULL;
+ }
+
+ if(p + 1 < border){
+ p[0] = p[1] = ' ';
+ if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+
+ if(thd == thdorig)
+ p[1] = '-';
+ }
+ }
+ else if(p < border){
+ p[0] = ' ';
+ if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+ }
+ }
+ }
+ }
+ }
+ else
+ fptr = str;
+
+ if(fptr){
+ strsize -= (fptr - str);
+ switch(ctype){
+ case iFromTo:
+ case iFromToNotNews:
+ if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
+ if(strsize-1 <= 4){
+ strncpy(fptr, "To: ", strsize-1);
+ fptr[strsize-1] = '\0';
+ break;
+ }
+ else{
+ if((field = ((addr = fetch_to(idata))
+ ? "To"
+ : (addr = fetch_cc(idata))
+ ? "Cc"
+ : NULL))
+ && set_index_addr(idata, field, addr, "To: ",
+ strsize-1, fptr))
+ break;
+
+ if(ctype == iFromTo &&
+ (newsgroups = fetch_newsgroups(idata)) &&
+ *newsgroups){
+ snprintf(fptr, strsize, "To: %-*.*s", strsize-1-4, strsize-1-4,
+ newsgroups);
+ break;
+ }
+
+ /* else fall thru to From: */
+ }
+ }
+ /* else fall thru to From: */
+
+ if(idata->bogus)
+ break;
+
+ case iFrom:
+ set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+ break;
+
+ case iAddress:
+ case iMailbox:
+ if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
+ char *mb = NULL, *hst = NULL, *at = NULL;
+ size_t len;
+
+ mb = addr->mailbox;
+ if(ctype == iAddress && addr->host && addr->host[0]
+ && addr->host[0] != '.'){
+ at = "@";
+ hst = addr->host;
+ }
+
+ len = strlen(mb);
+ if(!at || strsize-1 <= len)
+ snprintf(fptr, strsize, "%-*.*s", strsize-1, strsize-1, mb);
+ else
+ snprintf(fptr, strsize, "%s@%-*.*s", mb, strsize-1-len-1, strsize-1-len-1, hst);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/*
+ * Set up the elements contained in field so that they take up the
+ * whole field width. Data is assumed to be UTF-8.
+ */
+void
+set_ielem_widths_in_field(IFIELD_S *ifield)
+{
+ IELEM_S *ielem = NULL;
+ int datawidth, fmtwidth;
+
+ if(!ifield)
+ return;
+
+ fmtwidth = ifield->width;
+
+ for(ielem = ifield->ielem; ielem && fmtwidth > 0; ielem = ielem->next){
+ if(!ifield->leftadj && ielem->next){
+ dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield->ctype));
+ assert(0);
+ }
+
+ datawidth = (int) utf8_width(ielem->data);
+ if(datawidth >= fmtwidth || !ielem->next){
+ set_print_format(ielem, fmtwidth, ifield->leftadj);
+ fmtwidth = 0;
+ }
+ else{
+ set_print_format(ielem, datawidth, ifield->leftadj);
+ fmtwidth -= datawidth;
+ }
+ }
+}
+
+
+/*
+ * Simple hash function from K&R 2nd edition, p. 144.
+ *
+ * This one is modified to never return 0 so we can use that as a special
+ * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
+ * as a special value that can't be returned by line_hash.
+ */
+unsigned long
+line_hash(char *s)
+{
+ unsigned long hashval;
+
+ for(hashval = 0; *s != '\0'; s++)
+ hashval = *s + 31 * hashval;
+
+ hashval = hashval % LINE_HASH_N;
+
+ if(!hashval)
+ hashval++;
+
+ return(hashval);
+}
+
+
+/*
+ * Returns nonzero if considered hidden, 0 if not considered hidden.
+ */
+int
+msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
+{
+ int ret;
+
+ if(flags & MH_ANYTHD){
+ ret = ((any_lflagged(msgmap, MN_HIDE) > 0)
+ && get_lflag(stream, msgmap, msgno, MN_HIDE));
+ }
+ else if(flags & MH_THISTHD && THREADING() && sp_viewing_a_thread(stream)){
+ ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
+ || !get_lflag(stream, msgmap, msgno, MN_CHID2));
+ }
+ else{
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
+ || !get_lflag(stream, msgmap, msgno, MN_CHID2)
+ || get_lflag(stream, msgmap, msgno, MN_CHID));
+ }
+ else if(THRD_INDX()){
+ /*
+ * If this message is in the collapsed part of a thread,
+ * it's hidden. It must be a top-level of a thread to be
+ * considered visible. Even if it is top-level, it is only
+ * visible if some message in the thread is not hidden.
+ */
+ if(get_lflag(stream, msgmap, msgno, MN_CHID)) /* not top */
+ ret = 1;
+ else{
+ unsigned long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ rawno = mn_m2raw(msgmap, msgno);
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ ret = !thread_has_some_visible(stream, thrd);
+ }
+ }
+ else{
+ ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
+ && get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
+ }
+ }
+
+ dprint((10,
+ "msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
+
+ return(ret);
+}
+
+
+void
+adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long n, cur;
+ int dir;
+
+ cur = mn_get_cur(msgmap);
+
+ /* if current is hidden, adjust */
+ if(cur >= 1L && cur <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, cur, 0)){
+
+ dir = mn_get_revsort(msgmap) ? -1 : 1;
+
+ for(n = cur;
+ ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap))
+ && msgline_hidden(stream, msgmap, n, 0);
+ n -= dir)
+ ;
+
+ if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap)))
+ mn_reset_cur(msgmap, n);
+ else{ /* no visible in that direction */
+ for(n = cur;
+ ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap))
+ && msgline_hidden(stream, msgmap, n, 0);
+ n += dir)
+ ;
+
+ if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap)))
+ mn_reset_cur(msgmap, n);
+ /* else trouble! */
+ }
+ }
+}
+
+
+void
+setup_for_index_index_screen(void)
+{
+ format_index_line = format_index_index_line;
+ setup_header_widths = setup_index_header_widths;
+}
+
+
+void
+setup_for_thread_index_screen(void)
+{
+ format_index_line = format_thread_index_line;
+ setup_header_widths = setup_thread_header_widths;
+}
+
+
+unsigned long
+ice_hash(ICE_S *ice)
+{
+ char buf[MAX_SCREEN_COLS+1];
+
+ buf[0] = '\0';
+
+ if(ice)
+ simple_index_line(buf, sizeof(buf), ice, 0L);
+
+ buf[sizeof(buf) - 1] = '\0';
+
+ return(line_hash(buf));
+}
+
+
+char *
+left_adjust(int width)
+{
+ return(format_str(width, 1));
+}
+
+
+char *
+right_adjust(int width)
+{
+ return(format_str(width, 0));
+}
+
+
+/*
+ * Returns allocated and filled in format string.
+ */
+char *
+format_str(int width, int left)
+{
+ char *format;
+ size_t len;
+
+ len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
+ format = (char *) fs_get(len + 1);
+ copy_format_str(width, left, format, len);
+ format[len] = '\0';
+
+ return(format);
+}
+
+
+/*
+ * Put the left or right adjusted format string of width width into
+ * dest. Dest is of size n+1.
+ */
+char *
+copy_format_str(int width, int left, char *dest, int n)
+{
+ char *p;
+
+ p = int2string(width);
+
+ snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
+
+ dest[n] = '\0';
+
+ return(dest);
+}
+
+
+/*
+ * Sets up the print_format string to be width wide with left or right
+ * adjust. Takes care of memory freeing and allocation.
+ */
+void
+set_print_format(IELEM_S *ielem, int width, int leftadj)
+{
+ if(ielem){
+ ielem->wid = width;
+
+ if(ielem->print_format){
+ /* is there enough room? */
+ if(ielem->freeprintf < PRINT_FORMAT_LEN(width,leftadj)+1){
+ fs_resize((void **) &ielem->print_format,
+ (PRINT_FORMAT_LEN(width,leftadj)+1) * sizeof(char));
+ ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
+ }
+
+ copy_format_str(width, leftadj, ielem->print_format,
+ PRINT_FORMAT_LEN(width,leftadj));
+ }
+ else{
+ ielem->print_format = leftadj ? left_adjust(width)
+ : right_adjust(width);
+ ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
+ }
+ }
+}