/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2003, 2004 rzyjontko

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

   ----------------------------------------------------------------------

   Different sring manipulation routines.
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <stdarg.h>
#include <string.h>
#include <ctype.h>

#include "eprintf.h"
#include "mail.h"
#include "xmalloc.h"
#include "wrapbox.h"
#include "misc.h"
#include "keymap.h"
#include "str.h"
#include "gettext.h"
#include "mime.h"
#include "rstring.h"
#include "property.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#define FIRST_ALLOC 20

enum justify { JUSTIFY_LEFT = 0, JUSTIFY_RIGHT = 1};

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

/**
 * This structure describes formatting sequence (i.e. %-020n)
 * everything between % and the first letter is used to format
 * a string.
 * In the example above -020 means that we want the desired string
 * be exactly 20 characters long, filled with spaces (leading 0),
 * and justified to right (dash).
 *
 * To be more precise:
 *    len       = 20
 *    character = 'n'
 *    fill      = YES
 *    justify   = JUSTIFY_RIGHT
 */
struct fmt_seq {
        int  fs_len;
  
        int  len;
        char character;
        struct {
                enum yes_no  fill    : 1;
                enum justify justify : 1;
        } flags;
};

/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/
/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

static void
eval_fmt_seq (struct fmt_seq *fs, const char *s)
{
        char *rest;
  
        fs->fs_len        = 0;
        fs->len           = 0;
        fs->character     = '\0';
        fs->flags.fill    = NO;
        fs->flags.justify = JUSTIFY_LEFT;

        if (*s != '%')
                return;

        s++;

        if (*s == '-'){
                fs->flags.justify = JUSTIFY_RIGHT;
                s++;
                fs->fs_len++;
        }

        if (*s == '0'){
                fs->flags.fill = YES;
                s++;
                fs->fs_len++;
        }

        if (isdigit (*s)){
                fs->len = strtol (s, &rest, 10);
                if (rest)
                        fs->fs_len += rest - s;
                s = rest;
        }

        fs->character = *s;
        fs->fs_len++;
}


static void
put_string_fs (str_t *s, const char *t, struct fmt_seq *fs)
{
        int spaces;
        int t_len;
        int w_len;
  
        if (fs->len < 0)
                fs->len = 0;
        
        if (! t)
                t = "";

        t_len = strlen (t);

        if (t_len <= fs->len){
                w_len  = t_len;
                spaces = fs->len - t_len;
        }
        else if (fs->len != 0){
                w_len  = fs->len;
                spaces = 0;
        }
        else {
                w_len  = t_len;
                spaces = 0;
        }

        if (fs->flags.justify == JUSTIFY_LEFT){
                str_put_string_len (s, t, w_len);
                if (fs->flags.fill)
                        str_put_nchar (s, ' ', spaces);
        }
        else {
                if (fs->flags.fill)
                        str_put_nchar (s, ' ', spaces);
                str_put_string_len (s, t, w_len);
        }
}


/**
 * Suppose a mail:
 *  From: Johnny Masta Killa <johnny@masta.com>
 *  To: Bob The Ripper <bob@ripper.com>
 *  Subject: Bow before me!
 *  Date: Wed, 7 May 2003 15:36:46 +0200
 *
 */
static int
put_mail_special (str_t *s, const char *fmt, mail_t *mail)
{
        int   size;
        char *date;
        struct fmt_seq fs;

        eval_fmt_seq (&fs, fmt);
  
        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                        /* Johnny Masta Killa */
                case 'f':
                        put_string_fs (s, address_name (mail->from), &fs);
                        break;

                        /* Johnny Masta Killa <johnny@masta.com> */
                case 'F':
                        if (mail->from)
                                put_string_fs (s, mail->from->full, &fs);
                        else
                                put_string_fs (s, NULL, &fs);
                        break;

                        /* Bob The Ripper */
                case 't':
                        if (mail->to)
                                put_string_fs (s, address_name (mail->to->array[0]), &fs);
                        else
                                put_string_fs (s, NULL, &fs);
                        break;
      
                        /* Bob The Ripper <bob@ripper.com> */
                case 'T':
                        if (mail->to && mail->to->count)
                                put_string_fs (s, mail->to->array[0]->full, &fs);
                        else
                                put_string_fs (s, NULL, &fs);
                        break;

                        /* Wed, 7 May 2003 15:36:46 +0200 */
                case 'd':
                        put_string_fs (s, mail->date_str, &fs);
                        break;

                        /* 07 May */
                case 'D':
                        date = date_string ("%b %d", mail->date);
                        put_string_fs (s, date, &fs);
                        xfree (date);
                        break;

                        /* Bow before me! */
                case 's':
                        put_string_fs (s, mail->subject, &fs);
                        break;

                        /* JMK */
                case 'i':
                        if (mail->from)
                                put_string_fs (s, mail->from->initials, &fs);
                        else
                                put_string_fs (s, NULL, &fs);
                        break;

                        /* (3.5K) */
                case 'S':
                        size = wrapbox_mail_size (mail);
                        put_string_fs (s, human_size (size), &fs);
                        break;

                        /* these are flags */
                case '$':
                        if (mail->flags & FLAG_FLAGGED)
                                str_put_char (s, 'F');
                        else if (mail->flags & FLAG_READ)
                                str_put_char (s, ' ');
                        else if (mail->flags & FLAG_OLD)
                                str_put_char (s, 'O');
                        else
                                str_put_char (s, 'N');

                        if (mail->flags & FLAG_TRASHED)
                                str_put_char (s, 'D');
                        else if (mail->flags & FLAG_FETCHED)
                                str_put_char (s, 'F');
                        else if (mail->flags & FLAG_ANSWERED)
                                str_put_char (s, 'r');
                        else if (mail->flags & FLAG_PASSED)
                                str_put_char (s, 'f');
                        else
                                str_put_char (s, ' ');
                        break;

                        /* attachments indicator */
                case '?':
                        str_put_char (s,
                                      mime_attachment_indicator (mail->mime));
                        break;
                        
                        /* spam indicator */
                case '#':
                        if (mail->flags & FLAG_SPAM)
                                str_put_char (s, '#');
                        else if (mail->flags & FLAG_LEGITIMATE
                                 || mail->flags & FLAG_NOSPAM)
                                str_put_char (s, ' ');
                        else
                                str_put_char (s, '?');
                        break;
        }

        return fs.fs_len;
}



static int
put_mail_special_desc (str_t *s, const char *fmt)
{
        struct fmt_seq fs;

        eval_fmt_seq (&fs, fmt);

        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'f':
                case 'F':
                        put_string_fs (s, _("From"), &fs);
                        break;

                case 't':
                case 'T':
                        put_string_fs (s, _("To"), &fs);
                        break;

                case 'd':
                case 'D':
                        put_string_fs (s, _("Date  "), &fs);
                        break;

                case 's':
                        put_string_fs (s, _("Subject"), &fs);
                        break;

                case 'i':
                        put_string_fs (s, _("Initials"), &fs);
                        break;

                case 'S':
                        put_string_fs (s, _("Size"), &fs);
                        break;

                case '$':
                        str_put_string_len (s, "  ", 2);
                        break;

                case '?':
                        str_put_char (s, ' ');
                        break;

                case '#':
                        str_put_char (s, ' ');
                        break;
        }

        return fs.fs_len;
}



static int
put_command_special (str_t *s, const char *fmt, const eprintf_command_t *cmd)
{
        struct fmt_seq fs;

        eval_fmt_seq (&fs, fmt);

        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'p':
                        put_string_fs (s, cmd->command, & fs);
                        break;
                
                case 'f':
                        put_string_fs (s, cmd->fname, & fs);
                        break;

                case 'm':
                        put_string_fs (s, cmd->mime_type, & fs);
                        break;

                case 'l':
                        str_sprintf (s, "%d", cmd->lineno);
                        break;

                case 'c':
                        str_sprintf (s, "%d", cmd->col);
                        break;

                case 'o':
                        str_sprintf (s, "%d", cmd->pos);
                        break;
        }

        return fs.fs_len;
}


static int
put_addr_special (str_t *s, const char *fmt, address_t *addr)
{
        char           *tmp;
        struct fmt_seq  fs;

        eval_fmt_seq (&fs, fmt);
  
        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'n':
                        put_string_fs (s, addr->name, &fs);
                        break;

                case 'e':
                        put_string_fs (s, addr->email, &fs);
                        break;

                case 'o':
                        if (addr->flags.bits.official)
                                str_put_char (s, 'o');
                        else
                                str_put_char (s, ' ');
                        break;

                case 'f':
                        if (addr->flags.bits.foreign)
                                str_put_char (s, 'f');
                        else
                                str_put_char (s, ' ');
                        break;

                case 's':
                        if (addr->flags.bits.sex == SEX_MALE)
                                str_put_char (s, 'M');
                        else if (addr->flags.bits.sex == SEX_FEMALE)
                                str_put_char (s, 'F');
                        else
                                str_put_char (s, '?');
                        break;

                case 'g':
                        if (addr->groups == NULL)
                                put_string_fs (s, NULL, & fs);
                        else {
                                tmp = rstring_flatten (addr->groups, ", ");
                                put_string_fs (s, tmp, & fs);
                                xfree (tmp);
                        }
                        break;
                                
        }
        return fs.fs_len;
}


static int
put_addr_desc_special (str_t *s, const char *fmt)
{
        struct fmt_seq fs;

        eval_fmt_seq (& fs, fmt);

        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'n':
                        put_string_fs (s, _("Name"), & fs);
                        break;

                case 'e':
                        put_string_fs (s, _("Email"), & fs);
                        break;

                case 'o':
                        str_put_char (s, ' ');
                        break;

                case 'f':
                        str_put_char (s, ' ');
                        break;

                case 's':
                        str_put_char (s, ' ');
                        break;

                case 'g':
                        put_string_fs (s, _("Groups"), & fs);
                        break;

        }

        return fs.fs_len;
}


static int
put_key_special (str_t *s, const char *fmt, struct key *key, int meta)
{
        struct fmt_seq fs;

        eval_fmt_seq (&fs, fmt);
  
        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'k':
                        if (key == NULL)
                                put_string_fs (s, NULL, & fs);
                        else
                                put_string_fs (s,
                                               key2string (key->key, meta),
                                               &fs);
                        break;

                case 'f':
                        if (key == NULL || key->exec == NULL)
                                put_string_fs (s, NULL, & fs);
                        else
                                put_string_fs (s, key->exec->name, &fs);
                        break;

                case 'd':
                        if (key == NULL || key->exec == NULL)
                                put_string_fs (s, NULL, & fs);
                        else
                                put_string_fs (s, key->exec->desc, &fs);
                        break;
        }

        return fs.fs_len;
}



static int
put_box_special (str_t *s, const char *fmt, const char *box)
{
        int            unread;
        char           buf[16];
        struct fmt_seq fs;

        eval_fmt_seq (& fs, fmt);

        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'n':
                        put_string_fs (s, box, & fs);
                        break;

                case 't':
                        sprintf (buf, "%d",
                                 wrapbox_box_mail_count (box, NULL));
                        put_string_fs (s, buf, & fs);
                        break;

                case 'u':
                        wrapbox_box_mail_count (box, & unread);
                        sprintf (buf, "%d", unread);
                        put_string_fs (s, buf, & fs);
                        break;
        }

        return fs.fs_len;
}



static int
put_mime_special (str_t *s, const char *fmt, mime_t *mime)
{
        char           *fname;
        char           *enc;
        char           *type;
        struct fmt_seq  fs;
        
        eval_fmt_seq (& fs, fmt);

        switch (fs.character){

                case '%':
                        str_put_char (s, '%');
                        break;

                case 'f':
                        if (mime->file_name == NULL)
                                fname = "<mail content>";
                        else {
                                fname = strrchr (mime->file_name, '/');
                                if (fname)
                                        fname++;
                                else
                                        fname = mime->file_name;
                        }
                        put_string_fs (s, fname, & fs);
                        break;

                case 't':
                        type = (mime->type) ? mime->type : "text/plain";
                        put_string_fs (s, type, & fs);
                        break;

                case 's':
                        put_string_fs (s, human_size (mime->off_end
                                                      - mime->off_start),
                                       & fs);
                        break;

                case 'c':
                        if (mime->charset)
                                put_string_fs (s, mime->charset, & fs);
                        break;

                case 'C':
                        if (mime->charset){
                                str_put_char (s, ',');
                                str_put_char (s, ' ');
                                fs.len -= 2;
                                put_string_fs (s, mime->charset, & fs);
                        }
                        break;
                
                case 'e':
                        put_string_fs (s, mime_encoding_str (mime), & fs);
                        break;

                case 'E':
                        enc = mime_encoding_str (mime);
                        if (enc && *enc){
                                str_put_char (s, ',');
                                str_put_char (s, ' ');
                                fs.len -= 2;
                                put_string_fs (s, enc, & fs);
                        }
                        break;
        }

        return fs.fs_len;
}



static int
put_rstring_special (str_t *s, const char *fmt, rstring_t *values)
{
        int             num;
        char           *txt;
        struct fmt_seq  fs;
        
        eval_fmt_seq (& fs, fmt);

        if (islower (fs.character)){
                num = fs.character - 'a';
                if (num < values->count){
                        txt = property_get (values->array[num]);
                        put_string_fs (s, txt, & fs);
                        xfree (txt);
                }
        }
        else {
                switch (fs.character){

                        case '%':
                                str_put_char (s, '%');
                                break;
                }
        }

        return fs.fs_len;
}


static void
substitute_mail (const char *fmt, mail_t *mail, str_t *str)
{
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_mail_special (str, seek, mail);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
}



static void
substitute_mail_desc (const char *fmt, str_t *str)
{
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len  = put_mail_special_desc (str, seek);
                        seek   += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
        str_put_string (str, _("Subject"));
}



static char *
substitute_command (const char *fmt, const eprintf_command_t *cmd)
{
        int         fs_len;
        const char *seek;
        str_t      *str = str_create ();

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_command_special (str, seek, cmd);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }

        return str_finished (str);
}


static void
substitute_addr (const char *fmt, address_t *addr, str_t *str)
{
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_addr_special (str, seek, addr);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
}


static void
substitute_addr_desc (const char *fmt, str_t *str)
{
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_addr_desc_special (str, seek);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
}


static char *
substitute_key (const char *fmt, struct key *key, int meta)
{
        str_t      *str = str_create ();
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_key_special (str, seek, key, meta);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }

        return str_finished (str);
}



static void
substitute_box (const char *fmt, const char *box, str_t *str)
{
        const char *seek;
        int         fs_len;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_box_special (str, seek, box);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
}


static void
substitute_mime (const char *fmt, mime_t *mime, str_t *str)
{
        const char *seek;
        int         fs_len;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_mime_special (str, seek, mime);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }
}


static char *
substitute_rstring (const char *fmt, rstring_t *values)
{
        str_t      *str = str_create ();
        int         fs_len;
        const char *seek;

        for (seek = fmt; *seek; seek++){
                if (*seek == '%'){
                        fs_len = put_rstring_special (str, seek, values);
                        seek  += fs_len;
                }
                else
                        str_put_char (str, *seek);
        }

        return str_finished (str);
}

/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/

void
eprintf_mail_str (const char *fmt, mail_t *mail, str_t *str)
{
        str_clear (str);
        substitute_mail (fmt, mail, str);
}


char *
eprintf_mail (const char *fmt, mail_t *mail)
{
        str_t *str = str_create ();

        substitute_mail (fmt, mail, str);
        return str_finished (str);
}



void
eprintf_mail_desc (const char *fmt, str_t *str)
{
        str_clear (str);
        substitute_mail_desc (fmt, str);
}



char *
eprintf_command (const char *fmt, const eprintf_command_t *cmd)
{
        char *result;

        result = substitute_command (fmt, cmd);
        return result;
}


void
eprintf_addr_str (const char *fmt, address_t *addr, str_t *str)
{
        str_clear (str);
        substitute_addr (fmt, addr, str);
}


void
eprintf_addr_desc (const char *fmt, str_t *str)
{
        str_clear (str);
        substitute_addr_desc (fmt, str);
}


char *
eprintf_key (const char *fmt, struct key *key, int meta)
{
        char *result;

        result = substitute_key (fmt, key, meta);
        return result;
}


void
eprintf_box_str (const char *fmt, const char *box, str_t *str)
{
        str_clear (str);
        substitute_box (fmt, box, str);
}


void
eprintf_mime_str (const char *fmt, mime_t *mime, str_t *str)
{
        str_clear (str);
        substitute_mime (fmt, mime, str);
}


char *
eprintf_rstring (const char *fmt, rstring_t *values)
{
        char *result;

        result = substitute_rstring (fmt, values);
        return result;
}


/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE eprintf.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1