Annotation of win32/gnome/gmime-x.x.x/gmime-utils.c, revision 1.1

1.1     ! paf         1: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
        !             2: /*
        !             3:  *  Authors: Michael Zucchi <notzed@helixcode.com>
        !             4:  *           Jeffrey Stedfast <fejj@helixcode.com>
        !             5:  *
        !             6:  *  Copyright 2000 Helix Code, Inc. (www.helixcode.com)
        !             7:  *
        !             8:  *  This program is free software; you can redistribute it and/or modify
        !             9:  *  it under the terms of the GNU General Public License as published by
        !            10:  *  the Free Software Foundation; either version 2 of the License, or
        !            11:  *  (at your option) any later version.
        !            12:  *
        !            13:  *  This program is distributed in the hope that it will be useful,
        !            14:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            15:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            16:  *  GNU General Public License for more details.
        !            17:  *
        !            18:  *  You should have received a copy of the GNU General Public License
        !            19:  *  along with this program; if not, write to the Free Software
        !            20:  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
        !            21:  *
        !            22:  */
        !            23: 
        !            24: 
        !            25: #ifdef HAVE_CONFIG_H
        !            26: #include <config.h>
        !            27: #endif
        !            28: 
        !            29: #include <stdio.h>
        !            30: #include <stdlib.h>
        !            31: #include <string.h>
        !            32: #include <ctype.h>
        !            33: #include <errno.h>
        !            34: 
        !            35: #ifdef HAVE_ALLOCA_H
        !            36: #include <alloca.h>
        !            37: #endif
        !            38: 
        !            39: #include "gmime-utils.h"
        !            40: #include "gmime-table-private.h"
        !            41: #include "gmime-part.h"
        !            42: #include "gmime-charset.h"
        !            43: #include "gmime-iconv.h"
        !            44: #include "gmime-iconv-utils.h"
        !            45: #include "unicode.h"
        !            46: 
        !            47: #define d(x)
        !            48: #define w(x) x
        !            49: 
        !            50: #ifndef HAVE_ISBLANK
        !            51: #define isblank(c) (c == ' ' || c == '\t')
        !            52: #endif
        !            53: 
        !            54: #define GMIME_UUENCODE_CHAR(c) ((c) ? (c) + ' ' : '`')
        !            55: #define        GMIME_UUDECODE_CHAR(c) (((c) - ' ') & 077)
        !            56: 
        !            57: #define GMIME_FOLD_PREENCODED  (GMIME_FOLD_LEN / 2)
        !            58: 
        !            59: /* date parser macros */
        !            60: #define NUMERIC_CHARS          "1234567890"
        !            61: #define WEEKDAY_CHARS          "SundayMondayTuesdayWednesdayThursdayFridaySaturday"
        !            62: #define MONTH_CHARS            "JanuaryFebruaryMarchAprilMayJuneJulyAugustSeptemberOctoberNovemberDecember"
        !            63: #define TIMEZONE_ALPHA_CHARS   "UTCGMTESTEDTCSTCDTMSTPSTPDTZAMNY()"
        !            64: #define TIMEZONE_NUMERIC_CHARS "-+1234567890"
        !            65: #define TIME_CHARS             "1234567890:"
        !            66: 
        !            67: #define DATE_TOKEN_NON_NUMERIC          (1 << 0)
        !            68: #define DATE_TOKEN_NON_WEEKDAY          (1 << 1)
        !            69: #define DATE_TOKEN_NON_MONTH            (1 << 2)
        !            70: #define DATE_TOKEN_NON_TIME             (1 << 3)
        !            71: #define DATE_TOKEN_HAS_COLON            (1 << 4)
        !            72: #define DATE_TOKEN_NON_TIMEZONE_ALPHA   (1 << 5)
        !            73: #define DATE_TOKEN_NON_TIMEZONE_NUMERIC (1 << 6)
        !            74: #define DATE_TOKEN_HAS_SIGN             (1 << 7)
        !            75: 
        !            76: /* from gmime.c */
        !            77: extern int gmime_interfaces_utf8;
        !            78: 
        !            79: static char *base64_alphabet =
        !            80: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        !            81: 
        !            82: static unsigned char tohex[16] = {
        !            83:        '0', '1', '2', '3', '4', '5', '6', '7',
        !            84:        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        !            85: };
        !            86: 
        !            87: static unsigned char gmime_base64_rank[256] = {
        !            88:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !            89:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !            90:        255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
        !            91:         52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
        !            92:        255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
        !            93:         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
        !            94:        255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        !            95:         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
        !            96:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !            97:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !            98:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !            99:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           100:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           101:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           102:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           103:        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
        !           104: };
        !           105: 
        !           106: static unsigned char gmime_uu_rank[256] = {
        !           107:         32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        !           108:         48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
        !           109:          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        !           110:         16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        !           111:         32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        !           112:         48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
        !           113:          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        !           114:         16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        !           115:         32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        !           116:         48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
        !           117:          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        !           118:         16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        !           119:         32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        !           120:         48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
        !           121:          0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        !           122:         16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        !           123: };
        !           124: 
        !           125: static unsigned char gmime_datetok_table[256] = {
        !           126:        128,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           127:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           128:        111,111,111,111,111,111,111,111, 79, 79,111,175,111,175,111,111,
        !           129:         38, 38, 38, 38, 38, 38, 38, 38, 38, 38,119,111,111,111,111,111,
        !           130:        111, 75,111, 79, 75, 79,105, 79,111,111,107,111,111, 73, 75,107,
        !           131:         79,111,111, 73, 77, 79,111,109,111, 79, 79,111,111,111,111,111,
        !           132:        111,105,107,107,109,105,111,107,105,105,111,111,107,107,105,105,
        !           133:        107,111,105,105,105,105,107,111,111,105,111,111,111,111,111,111,
        !           134:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           135:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           136:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           137:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           138:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           139:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           140:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           141:        111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,
        !           142: };
        !           143: 
        !           144: /* hrm, is there a library for this shit? */
        !           145: static struct {
        !           146:        char *name;
        !           147:        int offset;
        !           148: } tz_offsets [] = {
        !           149:        { "UT", 0 },
        !           150:        { "GMT", 0 },
        !           151:        { "EST", -500 },        /* these are all US timezones.  bloody yanks */
        !           152:        { "EDT", -400 },
        !           153:        { "CST", -600 },
        !           154:        { "CDT", -500 },
        !           155:        { "MST", -700 },
        !           156:        { "MDT", -600 },
        !           157:        { "PST", -800 },
        !           158:        { "PDT", -700 },
        !           159:        { "Z", 0 },
        !           160:        { "A", -100 },
        !           161:        { "M", -1200 },
        !           162:        { "N", 100 },
        !           163:        { "Y", 1200 },
        !           164: };
        !           165: 
        !           166: static char *tm_months[] = {
        !           167:        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        !           168:        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        !           169: };
        !           170: 
        !           171: static char *tm_days[] = {
        !           172:        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
        !           173: };
        !           174: 
        !           175: 
        !           176: /**
        !           177:  * g_mime_utils_header_format_date:
        !           178:  * @time: time_t date representation
        !           179:  * @offset: Timezone offset
        !           180:  *
        !           181:  * Allocates a string buffer containing the rfc822 formatted date
        !           182:  * string represented by @time and @offset.
        !           183:  *
        !           184:  * Returns a valid string representation of the date.
        !           185:  **/
        !           186: char *
        !           187: g_mime_utils_header_format_date (time_t time, int offset)
        !           188: {
        !           189:        struct tm tm;
        !           190:        
        !           191:        time += ((offset / 100) * (60 * 60)) + (offset % 100) * 60;
        !           192:        
        !           193:        memcpy (&tm, gmtime (&time), sizeof (tm));
        !           194:        
        !           195:        return g_strdup_printf ("%s, %02d %s %04d %02d:%02d:%02d %+05d",
        !           196:                                tm_days[tm.tm_wday], tm.tm_mday,
        !           197:                                tm_months[tm.tm_mon],
        !           198:                                tm.tm_year + 1900,
        !           199:                                tm.tm_hour, tm.tm_min, tm.tm_sec,
        !           200:                                offset);
        !           201: }
        !           202: 
        !           203: /* This is where it gets ugly... */
        !           204: 
        !           205: struct _date_token {
        !           206:        struct _date_token *next;
        !           207:        const unsigned char *start;
        !           208:        unsigned int len;
        !           209:        unsigned int mask;
        !           210: };
        !           211: 
        !           212: static struct _date_token *
        !           213: datetok (const char *date)
        !           214: {
        !           215:        struct _date_token *tokens = NULL, *token, *tail = (struct _date_token *) &tokens;
        !           216:        const unsigned char *start, *end;
        !           217:        unsigned int mask;
        !           218:        
        !           219:        start = date;
        !           220:        while (*start) {
        !           221:                /* kill leading whitespace */
        !           222:                for ( ; *start && isspace ((int) *start); start++);
        !           223:                
        !           224:                mask = 0;
        !           225:                
        !           226:                /* find the end of this token */
        !           227:                for (end = start; *end && !strchr ("-/,\t\r\n ", *end); end++) {
        !           228:                        mask |= gmime_datetok_table[*end];
        !           229:                }
        !           230:                
        !           231:                if (end != start) {
        !           232:                        token = g_malloc (sizeof (struct _date_token));
        !           233:                        token->next = NULL;
        !           234:                        token->start = start;
        !           235:                        token->len = end - start;
        !           236:                        token->mask = mask;
        !           237:                        
        !           238:                        tail->next = token;
        !           239:                        tail = token;
        !           240:                }
        !           241:                
        !           242:                if (*end)
        !           243:                        start = end + 1;
        !           244:                else
        !           245:                        break;
        !           246:        }
        !           247:        
        !           248:        return tokens;
        !           249: }
        !           250: 
        !           251: static int
        !           252: decode_int (const unsigned char *in, unsigned int inlen)
        !           253: {
        !           254:        register const unsigned char *inptr;
        !           255:        const unsigned char *inend;
        !           256:        int sign = 1, val = 0;
        !           257:        
        !           258:        inptr = in;
        !           259:        inend = in + inlen;
        !           260:        
        !           261:        if (*inptr == '-') {
        !           262:                sign = -1;
        !           263:                inptr++;
        !           264:        } else if (*inptr == '+')
        !           265:                inptr++;
        !           266:        
        !           267:        for ( ; inptr < inend; inptr++) {
        !           268:                if (!isdigit ((int) *inptr))
        !           269:                        return  -1;
        !           270:                else
        !           271:                        val = (val * 10) + (*inptr - '0');
        !           272:        }
        !           273:        
        !           274:        val *= sign;
        !           275:        
        !           276:        return val;
        !           277: }
        !           278: 
        !           279: #if 0
        !           280: static int
        !           281: get_days_in_month (int month, int year)
        !           282: {
        !           283:         switch (month) {
        !           284:        case 1:
        !           285:        case 3:
        !           286:        case 5:
        !           287:        case 7:
        !           288:        case 8:
        !           289:        case 10:
        !           290:        case 12:
        !           291:                return 31;
        !           292:        case 4:
        !           293:        case 6:
        !           294:        case 9:
        !           295:        case 11:
        !           296:                return 30;
        !           297:        case 2:
        !           298:                if (g_date_is_leap_year (year))
        !           299:                        return 29;
        !           300:                else
        !           301:                        return 28;
        !           302:        default:
        !           303:                return 0;
        !           304:        }
        !           305: }
        !           306: #endif
        !           307: 
        !           308: static int
        !           309: get_wday (const unsigned char *in, unsigned int inlen)
        !           310: {
        !           311:        int wday;
        !           312:        
        !           313:        g_return_val_if_fail (in != NULL, -1);
        !           314:        
        !           315:        if (inlen < 3)
        !           316:                return -1;
        !           317:        
        !           318:        for (wday = 0; wday < 7; wday++)
        !           319:                if (!strncasecmp (in, tm_days[wday], 3))
        !           320:                        return wday;
        !           321:        
        !           322:        return -1;  /* unknown week day */
        !           323: }
        !           324: 
        !           325: static int
        !           326: get_mday (const unsigned char *in, unsigned int inlen)
        !           327: {
        !           328:        int mday;
        !           329:        
        !           330:        g_return_val_if_fail (in != NULL, -1);
        !           331:        
        !           332:        mday = decode_int (in, inlen);
        !           333:        
        !           334:        if (mday < 0 || mday > 31)
        !           335:                mday = -1;
        !           336:        
        !           337:        return mday;
        !           338: }
        !           339: 
        !           340: static int
        !           341: get_month (const unsigned char *in, unsigned int inlen)
        !           342: {
        !           343:        int i;
        !           344:        
        !           345:        g_return_val_if_fail (in != NULL, -1);
        !           346:        
        !           347:        if (inlen < 3)
        !           348:                return -1;
        !           349:        
        !           350:        for (i = 0; i < 12; i++)
        !           351:                if (!strncasecmp (in, tm_months[i], 3))
        !           352:                        return i;
        !           353:        
        !           354:        return -1;  /* unknown month */
        !           355: }
        !           356: 
        !           357: static int
        !           358: get_year (const unsigned char *in, unsigned int inlen)
        !           359: {
        !           360:        int year;
        !           361:        
        !           362:        g_return_val_if_fail (in != NULL, -1);
        !           363:        
        !           364:        year = decode_int (in, inlen);
        !           365:        if (year == -1)
        !           366:                return -1;
        !           367:        
        !           368:        if (year < 100)
        !           369:                year += (year < 70) ? 2000 : 1900;
        !           370:        
        !           371:        if (year < 1969)
        !           372:                return -1;
        !           373:        
        !           374:        return year;
        !           375: }
        !           376: 
        !           377: static gboolean
        !           378: get_time (const unsigned char *in, unsigned int inlen, int *hour, int *min, int *sec)
        !           379: {
        !           380:        register const unsigned char *inptr;
        !           381:        const unsigned char *inend;
        !           382:        int *val, colons = 0;
        !           383:        
        !           384:        *hour = *min = *sec = 0;
        !           385:        
        !           386:        inend = in + inlen;
        !           387:        val = hour;
        !           388:        for (inptr = in; inptr < inend; inptr++) {
        !           389:                if (*inptr == ':') {
        !           390:                        colons++;
        !           391:                        switch (colons) {
        !           392:                        case 1:
        !           393:                                val = min;
        !           394:                                break;
        !           395:                        case 2:
        !           396:                                val = sec;
        !           397:                                break;
        !           398:                        default:
        !           399:                                return FALSE;
        !           400:                        }
        !           401:                } else if (!isdigit ((int) *inptr))
        !           402:                        return FALSE;
        !           403:                else
        !           404:                        *val = (*val * 10) + (*inptr - '0');
        !           405:        }
        !           406:        
        !           407:        return TRUE;
        !           408: }
        !           409: 
        !           410: static int
        !           411: get_tzone (struct _date_token **token)
        !           412: {
        !           413:        int i;
        !           414:        
        !           415:        for (i = 0; *token && i < 2; *token = (*token)->next, i++) {
        !           416:                const unsigned char *inptr = (*token)->start;
        !           417:                unsigned int inlen = (*token)->len;
        !           418:                
        !           419:                if (*inptr == '+' || *inptr == '-') {
        !           420:                        return decode_int (inptr, inlen);
        !           421:                } else {
        !           422:                        int t;
        !           423:                        
        !           424:                        if (*inptr == '(')
        !           425:                                inptr++;
        !           426:                        
        !           427:                        for (t = 0; t < 15; t++) {
        !           428:                                unsigned int len = MIN (strlen (tz_offsets[t].name), inlen - 1);
        !           429:                                
        !           430:                                if (!strncmp (inptr, tz_offsets[t].name, len))
        !           431:                                        return tz_offsets[t].offset;
        !           432:                        }
        !           433:                }
        !           434:        }
        !           435:        
        !           436:        return -1;
        !           437: }
        !           438: 
        !           439: static time_t
        !           440: parse_rfc822_date (struct _date_token *tokens, int *tzone)
        !           441: {
        !           442:        int hour, min, sec, offset, n;
        !           443:        struct _date_token *token;
        !           444:        struct tm tm;
        !           445:        time_t t;
        !           446:        
        !           447:        g_return_val_if_fail (tokens != NULL, (time_t) 0);
        !           448:        
        !           449:        token = tokens;
        !           450:        
        !           451:        memset ((void *) &tm, 0, sizeof (struct tm));
        !           452:        
        !           453:        if ((n = get_wday (token->start, token->len)) != -1) {
        !           454:                /* not all dates may have this... */
        !           455:                tm.tm_wday = n;
        !           456:                token = token->next;
        !           457:        }
        !           458:        
        !           459:        /* get the mday */
        !           460:        if (!token || (n = get_mday (token->start, token->len)) == -1)
        !           461:                return (time_t) 0;
        !           462:        
        !           463:        tm.tm_mday = n;
        !           464:        token = token->next;
        !           465:        
        !           466:        /* get the month */
        !           467:        if (!token || (n = get_month (token->start, token->len)) == -1)
        !           468:                return (time_t) 0;
        !           469:        
        !           470:        tm.tm_mon = n;
        !           471:        token = token->next;
        !           472:        
        !           473:        /* get the year */
        !           474:        if (!token || (n = get_year (token->start, token->len)) == -1)
        !           475:                return (time_t) 0;
        !           476:        
        !           477:        tm.tm_year = n - 1900;
        !           478:        token = token->next;
        !           479:        
        !           480:        /* get the hour/min/sec */
        !           481:        if (!token || !get_time (token->start, token->len, &hour, &min, &sec))
        !           482:                return (time_t) 0;
        !           483:        
        !           484:        tm.tm_hour = hour;
        !           485:        tm.tm_min = min;
        !           486:        tm.tm_sec = sec;
        !           487:        token = token->next;
        !           488:        
        !           489:        /* get the timezone */
        !           490:        if (!token || (n = get_tzone (&token)) == -1) {
        !           491:                /* I guess we assume tz is GMT? */
        !           492:                offset = 0;
        !           493:        } else {
        !           494:                offset = n;
        !           495:        }
        !           496:        
        !           497:        t = mktime (&tm);
        !           498: #if defined (HAVE_TIMEZONE)
        !           499:        t -= timezone;
        !           500: #elif defined (HAVE_TM_GMTOFF)
        !           501:        t += tm.tm_gmtoff;
        !           502: #else
        !           503: #error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
        !           504: #endif
        !           505:        
        !           506:        /* t is now GMT of the time we want, but not offset by the timezone ... */
        !           507:        
        !           508:        /* this should convert the time to the GMT equiv time */
        !           509:        t -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
        !           510:        
        !           511:        if (tzone)
        !           512:                *tzone = offset;
        !           513:        
        !           514:        return t;
        !           515: }
        !           516: 
        !           517: 
        !           518: #define date_token_mask(t)  (((struct _date_token *) t)->mask)
        !           519: #define is_numeric(t)       ((date_token_mask (t) & DATE_TOKEN_NON_NUMERIC) == 0)
        !           520: #define is_weekday(t)       ((date_token_mask (t) & DATE_TOKEN_NON_WEEKDAY) == 0)
        !           521: #define is_month(t)         ((date_token_mask (t) & DATE_TOKEN_NON_MONTH) == 0)
        !           522: #define is_time(t)          (((date_token_mask (t) & DATE_TOKEN_NON_TIME) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_COLON))
        !           523: #define is_tzone_alpha(t)   ((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_ALPHA) == 0)
        !           524: #define is_tzone_numeric(t) (((date_token_mask (t) & DATE_TOKEN_NON_TIMEZONE_NUMERIC) == 0) && (date_token_mask (t) & DATE_TOKEN_HAS_SIGN))
        !           525: #define is_tzone(t)         (is_tzone_alpha (t) || is_tzone_numeric (t))
        !           526: 
        !           527: static time_t
        !           528: parse_broken_date (struct _date_token *tokens, int *tzone)
        !           529: {
        !           530:        gboolean got_wday, got_month, got_tzone;
        !           531:        int hour, min, sec, offset, n;
        !           532:        struct _date_token *token;
        !           533:        struct tm tm;
        !           534:        time_t t;
        !           535:        
        !           536:        memset ((void *) &tm, 0, sizeof (struct tm));
        !           537:        got_wday = got_month = got_tzone = FALSE;
        !           538:        offset = 0;
        !           539:        
        !           540:        token = tokens;
        !           541:        while (token) {
        !           542:                if (is_weekday (token) && !got_wday) {
        !           543:                        if ((n = get_wday (token->start, token->len)) != -1) {
        !           544:                                d(printf ("weekday; "));
        !           545:                                got_wday = TRUE;
        !           546:                                tm.tm_wday = n;
        !           547:                                goto next_token;
        !           548:                        }
        !           549:                }
        !           550:                
        !           551:                if (is_month (token) && !got_month) {
        !           552:                        if ((n = get_month (token->start, token->len)) != -1) {
        !           553:                                d(printf ("month; "));
        !           554:                                got_month = TRUE;
        !           555:                                tm.tm_mon = n;
        !           556:                                goto next_token;
        !           557:                        }
        !           558:                }
        !           559:                
        !           560:                if (is_time (token) && !tm.tm_hour && !tm.tm_min && !tm.tm_sec) {
        !           561:                        if (get_time (token->start, token->len, &hour, &min, &sec)) {
        !           562:                                d(printf ("time; "));
        !           563:                                tm.tm_hour = hour;
        !           564:                                tm.tm_min = min;
        !           565:                                tm.tm_sec = sec;
        !           566:                                goto next_token;
        !           567:                        }
        !           568:                }
        !           569:                
        !           570:                if (is_tzone (token) && !got_tzone) {
        !           571:                        struct _date_token *t = token;
        !           572:                        
        !           573:                        if ((n = get_tzone (&t)) != -1) {
        !           574:                                d(printf ("tzone; "));
        !           575:                                got_tzone = TRUE;
        !           576:                                offset = n;
        !           577:                                goto next_token;
        !           578:                        }
        !           579:                }
        !           580:                
        !           581:                if (is_numeric (token)) {
        !           582:                        if (token->len == 4 && !tm.tm_year) {
        !           583:                                if ((n = get_year (token->start, token->len)) != -1) {
        !           584:                                        d(printf ("year; "));
        !           585:                                        tm.tm_year = n - 1900;
        !           586:                                        goto next_token;
        !           587:                                }
        !           588:                        } else {
        !           589:                                if (!got_month && !got_wday && token->next && is_numeric (token->next)) {
        !           590:                                        d(printf ("mon; "));
        !           591:                                        n = decode_int (token->start, token->len);
        !           592:                                        got_month = TRUE;
        !           593:                                        tm.tm_mon = n - 1;
        !           594:                                        goto next_token;
        !           595:                                } else if (!tm.tm_mday && (n = get_mday (token->start, token->len)) != -1) {
        !           596:                                        d(printf ("mday; "));
        !           597:                                        tm.tm_mday = n;
        !           598:                                        goto next_token;
        !           599:                                } else if (!tm.tm_year) {
        !           600:                                        d(printf ("2-digit year; "));
        !           601:                                        n = get_year (token->start, token->len);
        !           602:                                        tm.tm_year = n - 1900;
        !           603:                                        goto next_token;
        !           604:                                }
        !           605:                        }
        !           606:                }
        !           607:                
        !           608:                d(printf ("???; "));
        !           609:                
        !           610:        next_token:
        !           611:                
        !           612:                token = token->next;
        !           613:        }
        !           614:        
        !           615:        d(printf ("\n"));
        !           616:        
        !           617:        t = mktime (&tm);
        !           618: #if defined (HAVE_TIMEZONE)
        !           619:        t -= timezone;
        !           620: #elif defined (HAVE_TM_GMTOFF)
        !           621:        t += tm.tm_gmtoff;
        !           622: #else
        !           623: #error Neither HAVE_TIMEZONE nor HAVE_TM_GMTOFF defined. Rerun autoheader, autoconf, etc.
        !           624: #endif
        !           625:        
        !           626:        /* t is now GMT of the time we want, but not offset by the timezone ... */
        !           627:        
        !           628:        /* this should convert the time to the GMT equiv time */
        !           629:        t -= ((offset / 100) * 60 * 60) + (offset % 100) * 60;
        !           630:        
        !           631:        if (tzone)
        !           632:                *tzone = offset;
        !           633:        
        !           634:        return t;
        !           635: }
        !           636: 
        !           637: #if 0
        !           638: static void
        !           639: gmime_datetok_table_init ()
        !           640: {
        !           641:        int i;
        !           642:        
        !           643:        memset (gmime_datetok_table, 0, sizeof (gmime_datetok_table));
        !           644:        
        !           645:        for (i = 0; i < 256; i++) {
        !           646:                if (!strchr (NUMERIC_CHARS, i))
        !           647:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_NUMERIC;
        !           648:                
        !           649:                if (!strchr (WEEKDAY_CHARS, i))
        !           650:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_WEEKDAY;
        !           651:                
        !           652:                if (!strchr (MONTH_CHARS, i))
        !           653:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_MONTH;
        !           654:                
        !           655:                if (!strchr (TIME_CHARS, i))
        !           656:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_TIME;
        !           657:                
        !           658:                if (!strchr (TIMEZONE_ALPHA_CHARS, i))
        !           659:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_ALPHA;
        !           660:                
        !           661:                if (!strchr (TIMEZONE_NUMERIC_CHARS, i))
        !           662:                        gmime_datetok_table[i] |= DATE_TOKEN_NON_TIMEZONE_NUMERIC;
        !           663:                
        !           664:                if (((char) i) == ':')
        !           665:                        gmime_datetok_table[i] |= DATE_TOKEN_HAS_COLON;
        !           666:                
        !           667:                if (strchr ("+-", i))
        !           668:                        gmime_datetok_table[i] |= DATE_TOKEN_HAS_SIGN;
        !           669:        }
        !           670:        
        !           671:        printf ("static unsigned char gmime_datetok_table[256] = {");
        !           672:        for (i = 0; i < 256; i++) {
        !           673:                if (i % 16 == 0)
        !           674:                        printf ("\n\t");
        !           675:                printf ("%3d,", gmime_datetok_table[i]);
        !           676:        }
        !           677:        printf ("\n};\n");
        !           678: }
        !           679: #endif
        !           680: 
        !           681: 
        !           682: /**
        !           683:  * g_mime_utils_header_decode_date:
        !           684:  * @in: input date string
        !           685:  * @saveoffset:
        !           686:  *
        !           687:  * Decodes the rfc822 date string and saves the GMT offset into
        !           688:  * @saveoffset if non-NULL.
        !           689:  *
        !           690:  * Returns the time_t representation of the date string specified by
        !           691:  * @in. If 'saveoffset' is non-NULL, the value of the timezone offset
        !           692:  * will be stored.
        !           693:  **/
        !           694: time_t
        !           695: g_mime_utils_header_decode_date (const char *in, int *saveoffset)
        !           696: {
        !           697:        struct _date_token *token, *tokens;
        !           698:        time_t date;
        !           699:        
        !           700:        tokens = datetok (in);
        !           701:        
        !           702:        date = parse_rfc822_date (tokens, saveoffset);
        !           703:        if (!date)
        !           704:                date = parse_broken_date (tokens, saveoffset);
        !           705:        
        !           706:        /* cleanup */
        !           707:        while (tokens) {
        !           708:                token = tokens;
        !           709:                tokens = tokens->next;
        !           710:                g_free (token);
        !           711:        }
        !           712:        
        !           713:        return date;
        !           714: }
        !           715: 
        !           716: /*
        !           717: static void
        !           718: g_string_append_len (GString *out, const char *in, size_t len)
        !           719: {
        !           720:        char *buf;
        !           721:        
        !           722:        buf = alloca (len + 1);
        !           723:        strlcpy (buf, in, len);
        !           724:        
        !           725:        g_string_append (out, buf);
        !           726: }
        !           727: */
        !           728: /**
        !           729:  * g_mime_utils_header_fold:
        !           730:  * @in: input header string
        !           731:  *
        !           732:  * Folds a header according to the rules in rfc822.
        !           733:  *
        !           734:  * Returns an allocated string containing the folded header.
        !           735:  **/
        !           736: char *
        !           737: g_mime_utils_header_fold (const char *in)
        !           738: {
        !           739:        gboolean last_was_lwsp = FALSE;
        !           740:        register const char *inptr;
        !           741:        size_t len, outlen, i;
        !           742:        GString *out;
        !           743:        char *ret;
        !           744:        
        !           745:        inptr = in;
        !           746:        len = strlen (in);
        !           747:        if (len <= GMIME_FOLD_LEN)
        !           748:                return g_strdup (in);
        !           749:        
        !           750:        out = g_string_new ("");
        !           751:        outlen = 0;
        !           752:        while (*inptr) {
        !           753:                len = strcspn (inptr, " \t");
        !           754:                
        !           755:                if (outlen + len > GMIME_FOLD_LEN) {                    
        !           756:                        if (last_was_lwsp)
        !           757:                                g_string_truncate (out, out->len - 1);
        !           758:                        
        !           759:                        g_string_append (out, "\n\t");
        !           760:                        outlen = 1;
        !           761:                        
        !           762:                        /* check for very long words, just cut them up */
        !           763:                        while (outlen + len > GMIME_FOLD_LEN) {
        !           764:                                for (i = 0; i < GMIME_FOLD_LEN - outlen; i++)
        !           765:                                        g_string_append_c (out, inptr[i]);
        !           766:                                inptr += GMIME_FOLD_LEN - outlen;
        !           767:                                len -= GMIME_FOLD_LEN - outlen;
        !           768:                                g_string_append (out, "\n\t");
        !           769:                                outlen = 1;
        !           770:                        }
        !           771:                        last_was_lwsp = FALSE;
        !           772:                } else if (len > 0) {
        !           773:                        outlen += len;
        !           774:                        g_string_append_len (out, inptr, len);
        !           775:                        inptr += len;
        !           776:                        last_was_lwsp = FALSE;
        !           777:                } else {
        !           778:                        if (*inptr == '\t') {
        !           779:                                /* tabs are a good place to fold, odds
        !           780:                                    are that this is where the previous
        !           781:                                    mailer folded it */
        !           782:                                g_string_append (out, "\n\t");
        !           783:                                outlen = 1;
        !           784:                                inptr++;
        !           785:                                last_was_lwsp = FALSE;
        !           786:                        } else {
        !           787:                                g_string_append_c (out, *inptr++);
        !           788:                                outlen++;
        !           789:                                last_was_lwsp = TRUE;
        !           790:                        }
        !           791:                }
        !           792:        }
        !           793:        
        !           794:        ret = out->str;
        !           795:        g_string_free (out, FALSE);
        !           796:        
        !           797:        return ret;
        !           798: }
        !           799: 
        !           800: 
        !           801: /**
        !           802:  * g_mime_utils_header_printf:
        !           803:  * @format: string format
        !           804:  * @Varargs: arguments
        !           805:  *
        !           806:  * Allocates a buffer containing a formatted header specified by the
        !           807:  * @Varargs.
        !           808:  *
        !           809:  * Returns an allocated string containing the folded header specified
        !           810:  * by @format and the following arguments.
        !           811:  **/
        !           812: char *
        !           813: g_mime_utils_header_printf (const char *format, ...)
        !           814: {
        !           815:        char *buf, *ret;
        !           816:        va_list ap;
        !           817:        
        !           818:        va_start (ap, format);
        !           819:        buf = g_strdup_vprintf (format, ap);
        !           820:        va_end (ap);
        !           821:        
        !           822:        ret = g_mime_utils_header_fold (buf);
        !           823:        g_free (buf);
        !           824:        
        !           825:        return ret;
        !           826: }
        !           827: 
        !           828: static gboolean
        !           829: need_quotes (const char *string)
        !           830: {
        !           831:        gboolean quoted = FALSE;
        !           832:        const char *inptr;
        !           833:        
        !           834:        inptr = string;
        !           835:        
        !           836:        while (*inptr) {
        !           837:                if (*inptr == '\\')
        !           838:                        inptr++;
        !           839:                else if (*inptr == '"')
        !           840:                        quoted = !quoted;
        !           841:                else if (!quoted && (is_tspecial (*inptr) || *inptr == '.'))
        !           842:                        return TRUE;
        !           843:                
        !           844:                if (*inptr)
        !           845:                        inptr++;
        !           846:        }
        !           847:        
        !           848:        return FALSE;
        !           849: }
        !           850: 
        !           851: /**
        !           852:  * g_mime_utils_quote_string:
        !           853:  * @string: input string
        !           854:  *
        !           855:  * Quotes @string as needed according to the rules in rfc2045.
        !           856:  * 
        !           857:  * Returns an allocated string containing the escaped and quoted (if
        !           858:  * needed to be) input string. The decision to quote the string is
        !           859:  * based on whether or not the input string contains any 'tspecials'
        !           860:  * as defined by rfc2045.
        !           861:  **/
        !           862: char *
        !           863: g_mime_utils_quote_string (const char *string)
        !           864: {
        !           865:        gboolean quote;
        !           866:        const char *c;
        !           867:        char *qstring;
        !           868:        GString *out;
        !           869:        
        !           870:        out = g_string_new ("");
        !           871:        quote = need_quotes (string);
        !           872:        
        !           873:        for (c = string; *c; c++) {
        !           874:                if ((*c == '"' && quote) || *c == '\\')
        !           875:                        g_string_append_c (out, '\\');
        !           876:                
        !           877:                g_string_append_c (out, *c);
        !           878:        }
        !           879:        
        !           880:        if (quote) {
        !           881:                g_string_prepend_c (out, '"');
        !           882:                g_string_append_c (out, '"');
        !           883:        }
        !           884:        
        !           885:        qstring = out->str;
        !           886:        g_string_free (out, FALSE);
        !           887:        
        !           888:        return qstring;
        !           889: }
        !           890: 
        !           891: 
        !           892: /**
        !           893:  * g_mime_utils_unquote_string: Unquote a string.
        !           894:  * @string: string
        !           895:  * 
        !           896:  * Unquotes and unescapes a string.
        !           897:  **/
        !           898: void
        !           899: g_mime_utils_unquote_string (char *string)
        !           900: {
        !           901:        /* if the string is quoted, unquote it */
        !           902:        char *inptr, *inend;
        !           903:        
        !           904:        if (!string)
        !           905:                return;
        !           906:        
        !           907:        inptr = string;
        !           908:        inend = string + strlen (string);
        !           909:        
        !           910:        /* get rid of the wrapping quotes */
        !           911:        if (*inptr == '"' && *(inend - 1) == '"') {
        !           912:                inend--;
        !           913:                *inend = '\0';
        !           914:                if (*inptr)
        !           915:                        memmove (inptr, inptr + 1, inend - inptr);
        !           916:        }
        !           917:        
        !           918:        /* un-escape the string */
        !           919:        inend--;
        !           920:        while (inptr < inend) {
        !           921:                if (*inptr == '\\') {
        !           922:                        memmove (inptr, inptr + 1, inend - inptr);
        !           923:                        inend--;
        !           924:                }
        !           925:                
        !           926:                inptr++;
        !           927:        }
        !           928: }
        !           929: 
        !           930: 
        !           931: /**
        !           932:  * g_mime_utils_text_is_8bit:
        !           933:  * @text: text to check for 8bit chars
        !           934:  * @len: text length
        !           935:  *
        !           936:  * Determines if @text contains 8bit characters within the first @len
        !           937:  * bytes.
        !           938:  *
        !           939:  * Returns TRUE if the text contains 8bit characters or FALSE
        !           940:  * otherwise.
        !           941:  **/
        !           942: gboolean
        !           943: g_mime_utils_text_is_8bit (const unsigned char *text, size_t len)
        !           944: {
        !           945:        const unsigned char *c, *inend;
        !           946:        
        !           947:        g_return_val_if_fail (text != NULL, FALSE);
        !           948:        
        !           949:        inend = text + len;
        !           950:        for (c = text; c < inend; c++)
        !           951:                if (*c > (unsigned char) 127)
        !           952:                        return TRUE;
        !           953:        
        !           954:        return FALSE;
        !           955: }
        !           956: 
        !           957: 
        !           958: /**
        !           959:  * g_mime_utils_best_encoding:
        !           960:  * @text: text to encode
        !           961:  * @len: text length
        !           962:  *
        !           963:  * Determines the best content encoding for the first @len bytes of
        !           964:  * @text.
        !           965:  *
        !           966:  * Returns a GMimePartEncodingType that is determined to be the best
        !           967:  * encoding type for the specified block of text. ("best" in this
        !           968:  * particular case means best compression)
        !           969:  **/
        !           970: GMimePartEncodingType
        !           971: g_mime_utils_best_encoding (const unsigned char *text, size_t len)
        !           972: {
        !           973:        const unsigned char *ch, *inend;
        !           974:        size_t count = 0;
        !           975:        
        !           976:        inend = text + len;
        !           977:        for (ch = text; ch < inend; ch++)
        !           978:                if (*ch > (unsigned char) 127)
        !           979:                        count++;
        !           980:        
        !           981:        if ((float) count <= len * 0.17)
        !           982:                return GMIME_PART_ENCODING_QUOTEDPRINTABLE;
        !           983:        else
        !           984:                return GMIME_PART_ENCODING_BASE64;
        !           985: }
        !           986: 
        !           987: /* this decodes rfc2047's version of quoted-printable */
        !           988: static ssize_t
        !           989: quoted_decode (const unsigned char *in, size_t len, unsigned char *out)
        !           990: {
        !           991:        register const unsigned char *inptr;
        !           992:        register unsigned char *outptr;
        !           993:        const unsigned char *inend;
        !           994:        unsigned char c, c1;
        !           995:        
        !           996:        inend = in + len;
        !           997:        outptr = out;
        !           998:        
        !           999:        inptr = in;
        !          1000:        while (inptr < inend) {
        !          1001:                c = *inptr++;
        !          1002:                if (c == '=') {
        !          1003:                        if (inend - inptr >= 2) {
        !          1004:                                c = toupper (*inptr++);
        !          1005:                                c1 = toupper (*inptr++);
        !          1006:                                *outptr++ = (((c >= 'A' ? c - 'A' + 10 : c - '0') & 0x0f) << 4)
        !          1007:                                        | ((c1 >= 'A' ? c1 - 'A' + 10 : c1 - '0') & 0x0f);
        !          1008:                        } else {
        !          1009:                                /* data was truncated */
        !          1010:                                return -1;
        !          1011:                        }
        !          1012:                } else if (c == '_') {
        !          1013:                        /* _'s are an rfc2047 shortcut for encoding spaces */
        !          1014:                        *outptr++ = ' ';
        !          1015:                } else {
        !          1016:                        *outptr++ = c;
        !          1017:                }
        !          1018:        }
        !          1019:        
        !          1020:        return (outptr - out);
        !          1021: }
        !          1022: 
        !          1023: #define is_rfc2047_encoded_word(atom, len) (len >= 7 && !strncmp (atom, "=?", 2) && !strncmp (atom + len - 2, "?=", 2))
        !          1024: 
        !          1025: static unsigned char *
        !          1026: rfc2047_decode_word (const unsigned char *in, size_t inlen)
        !          1027: {
        !          1028:        const register unsigned char *inptr;
        !          1029:        const unsigned char *inend;
        !          1030:        
        !          1031:        inptr = in + 2;
        !          1032:        inend = in + inlen - 2;
        !          1033:        
        !          1034:        inptr = memchr (inptr, '?', inend - inptr);
        !          1035:        if (inptr && inptr[2] == '?') {
        !          1036:                unsigned char *decoded;
        !          1037:                ssize_t declen;
        !          1038:                int state = 0;
        !          1039:                int save = 0;
        !          1040:                
        !          1041:                inptr++;
        !          1042:                
        !          1043:                switch (*inptr) {
        !          1044:                case 'B':
        !          1045:                case 'b':
        !          1046:                        inptr += 2;
        !          1047:                        decoded = alloca (inend - inptr);
        !          1048:                        declen = g_mime_utils_base64_decode_step (inptr, inend - inptr, decoded, &state, &save);
        !          1049:                        break;
        !          1050:                case 'Q':
        !          1051:                case 'q':
        !          1052:                        inptr += 2;
        !          1053:                        decoded = alloca (inend - inptr);
        !          1054:                        declen = quoted_decode (inptr, inend - inptr, decoded);
        !          1055:                        
        !          1056:                        if (declen == -1) {
        !          1057:                                d(fprintf (stderr, "encountered broken 'Q' encoding\n"));
        !          1058:                                return NULL;
        !          1059:                        }
        !          1060:                        break;
        !          1061:                default:
        !          1062:                        d(fprintf (stderr, "unknown encoding\n"));
        !          1063:                        return NULL;
        !          1064:                }
        !          1065:                
        !          1066:                if (gmime_interfaces_utf8) {
        !          1067:                        const char *charset;
        !          1068:                        unsigned char *buf;
        !          1069:                        char *charenc, *p;
        !          1070:                        size_t len;
        !          1071:                        iconv_t cd;
        !          1072:                        
        !          1073:                        len = (inptr - 3) - (in + 2);
        !          1074:                        charenc = alloca (len + 1);
        !          1075:                        memcpy (charenc, in + 2, len);
        !          1076:                        charenc[len] = '\0';
        !          1077:                        charset = charenc;
        !          1078:                        
        !          1079:                        /* rfc2231 updates rfc2047 encoded words...
        !          1080:                         * The ABNF given in RFC 2047 for encoded-words is:
        !          1081:                         *   encoded-word := "=?" charset "?" encoding "?" encoded-text "?="
        !          1082:                         * This specification changes this ABNF to:
        !          1083:                         *   encoded-word := "=?" charset ["*" language] "?" encoding "?" encoded-text "?="
        !          1084:                         */
        !          1085:                        
        !          1086:                        /* trim off the 'language' part if it's there... */
        !          1087:                        p = strchr (charset, '*');
        !          1088:                        if (p)
        !          1089:                                *p = '\0';
        !          1090:                        
        !          1091:                        /* slight optimization */
        !          1092:                        if (!strcasecmp (charset, "UTF-8"))
        !          1093:                                return g_strndup (decoded, declen);
        !          1094:                        
        !          1095:                        cd = g_mime_iconv_open ("UTF-8", charset);
        !          1096:                        if (cd == (iconv_t) -1) {
        !          1097:                                w(g_warning ("Cannot convert from %s to UTF-8, header display may "
        !          1098:                                             "be corrupt: %s", charset, g_strerror (errno)));
        !          1099:                                charset = g_mime_charset_locale_name ();
        !          1100:                                cd = g_mime_iconv_open ("UTF-8", charset);
        !          1101:                                if (cd == (iconv_t) -1)
        !          1102:                                        return NULL;
        !          1103:                        }
        !          1104:                        
        !          1105:                        buf = g_mime_iconv_strndup (cd, decoded, declen);
        !          1106:                        g_mime_iconv_close (cd);
        !          1107:                        
        !          1108:                        if (!buf) {
        !          1109:                                w(g_warning ("Failed to convert \"%.*s\" to UTF-8, display may be "
        !          1110:                                             "corrupt: %s", declen, decoded, g_strerror (errno)));
        !          1111:                        }
        !          1112:                        
        !          1113:                        return buf;
        !          1114:                } else {
        !          1115:                        return g_strndup (decoded, declen);
        !          1116:                }
        !          1117:        }
        !          1118:        
        !          1119:        return NULL;
        !          1120: }
        !          1121: 
        !          1122: 
        !          1123: /**
        !          1124:  * g_mime_utils_8bit_header_decode:
        !          1125:  * @in: header to decode
        !          1126:  *
        !          1127:  * Decodes and rfc2047 encoded header.
        !          1128:  *
        !          1129:  * Returns the mime encoded header as 8bit text.
        !          1130:  **/
        !          1131: char *
        !          1132: g_mime_utils_8bit_header_decode (const unsigned char *in)
        !          1133: {
        !          1134:        GString *out, *lwsp, *atom;
        !          1135:        const unsigned char *inptr;
        !          1136:        unsigned char *decoded;
        !          1137:        gboolean last_was_encoded = FALSE;
        !          1138:        gboolean last_was_space = FALSE;
        !          1139:        
        !          1140:        out = g_string_sized_new (256);
        !          1141:        lwsp = g_string_sized_new (256);
        !          1142:        atom = g_string_sized_new (256);
        !          1143:        inptr = in;
        !          1144:        
        !          1145:        while (inptr && *inptr) {
        !          1146:                unsigned char c = *inptr++;
        !          1147:                
        !          1148:                if (!is_atom (c) && !last_was_space) {
        !          1149:                        /* we reached the end of an atom */
        !          1150:                        unsigned char *dword = NULL;
        !          1151:                        const unsigned char *word;
        !          1152:                        gboolean was_encoded;
        !          1153:                        
        !          1154:                        if ((was_encoded = is_rfc2047_encoded_word (atom->str, atom->len)))
        !          1155:                                word = dword = rfc2047_decode_word (atom->str, atom->len);
        !          1156:                        else
        !          1157:                                word = atom->str;
        !          1158:                        
        !          1159:                        if (word) {
        !          1160:                                if (!(last_was_encoded && was_encoded)) {
        !          1161:                                        /* rfc2047 states that you
        !          1162:                                            must ignore all whitespace
        !          1163:                                            between encoded words */
        !          1164:                                        g_string_append (out, lwsp->str);
        !          1165:                                }
        !          1166:                                
        !          1167:                                g_string_append (out, word);
        !          1168:                                g_free (dword);
        !          1169:                        } else {
        !          1170:                                was_encoded = FALSE;
        !          1171:                                g_string_append (out, lwsp->str);
        !          1172:                                g_string_append (out, atom->str);
        !          1173:                        }
        !          1174:                        
        !          1175:                        last_was_encoded = was_encoded;
        !          1176:                        
        !          1177:                        g_string_truncate (lwsp, 0);
        !          1178:                        g_string_truncate (atom, 0);
        !          1179:                        
        !          1180:                        if (is_lwsp (c)) {
        !          1181:                                g_string_append_c (lwsp, c);
        !          1182:                                last_was_space = TRUE;
        !          1183:                        } else {
        !          1184:                                /* This is mostly here for interoperability with broken
        !          1185:                                    mailers that might do something stupid like:
        !          1186:                                    =?iso-8859-1?Q?blah?=:\t=?iso-8859-1?Q?I_am_broken?= */
        !          1187:                                g_string_append_c (out, c);
        !          1188:                                last_was_encoded = FALSE;
        !          1189:                                last_was_space = FALSE;
        !          1190:                        }
        !          1191:                        
        !          1192:                        continue;
        !          1193:                }
        !          1194:                
        !          1195:                if (is_atom (c)) {
        !          1196:                        g_string_append_c (atom, c);
        !          1197:                        last_was_space = FALSE;
        !          1198:                } else {
        !          1199:                        g_string_append_c (lwsp, c);
        !          1200:                        last_was_space = TRUE;
        !          1201:                }
        !          1202:        }
        !          1203:        
        !          1204:        if (atom->len || lwsp->len) {
        !          1205:                unsigned char *dword = NULL;
        !          1206:                const unsigned char *word;
        !          1207:                gboolean was_encoded;
        !          1208:                
        !          1209:                if ((was_encoded = is_rfc2047_encoded_word (atom->str, atom->len)))
        !          1210:                        word = dword = rfc2047_decode_word (atom->str, atom->len);
        !          1211:                else
        !          1212:                        word = atom->str;
        !          1213:                
        !          1214:                if (word) {
        !          1215:                        if (!(last_was_encoded && was_encoded)) {
        !          1216:                                /* rfc2047 states that you
        !          1217:                                   must ignore all whitespace
        !          1218:                                   between encoded words */
        !          1219:                                g_string_append (out, lwsp->str);
        !          1220:                        }
        !          1221:                        
        !          1222:                        g_string_append (out, word);
        !          1223:                        g_free (dword);
        !          1224:                } else {
        !          1225:                        g_string_append (out, lwsp->str);
        !          1226:                        g_string_append (out, atom->str);
        !          1227:                }
        !          1228:        }
        !          1229:        
        !          1230:        g_string_free (lwsp, TRUE);
        !          1231:        g_string_free (atom, TRUE);
        !          1232:        
        !          1233:        decoded = out->str;
        !          1234:        g_string_free (out, FALSE);
        !          1235:        
        !          1236:        return (char *) decoded;
        !          1237: }
        !          1238: 
        !          1239: /* rfc2047 version of quoted-printable */
        !          1240: static size_t
        !          1241: quoted_encode (const unsigned char *in, size_t len, unsigned char *out, gushort safemask)
        !          1242: {
        !          1243:        register const unsigned char *inptr;
        !          1244:        register unsigned char *outptr;
        !          1245:        const unsigned char *inend;
        !          1246:        unsigned char c;
        !          1247:        
        !          1248:        inptr = in;
        !          1249:        inend = in + len;
        !          1250:        outptr = out;
        !          1251:        
        !          1252:        while (inptr < inend) {
        !          1253:                c = *inptr++;
        !          1254:                if (c == ' ') {
        !          1255:                        *outptr++ = '_';
        !          1256:                } else if (gmime_special_table[c] & safemask) {
        !          1257:                        *outptr++ = c;
        !          1258:                } else {
        !          1259:                        *outptr++ = '=';
        !          1260:                        *outptr++ = tohex[(c >> 4) & 0xf];
        !          1261:                        *outptr++ = tohex[c & 0xf];
        !          1262:                }
        !          1263:        }
        !          1264:        
        !          1265:        return (outptr - out);
        !          1266: }
        !          1267: 
        !          1268: static void
        !          1269: rfc2047_encode_word (GString *string, const unsigned char *word, size_t len,
        !          1270:                     const char *charset, gushort safemask)
        !          1271: {
        !          1272:        unsigned char *encoded, *ptr;
        !          1273:        unsigned char *uword = NULL;
        !          1274:        iconv_t cd = (iconv_t) -1;
        !          1275:        size_t enclen, pos;
        !          1276:        int state = 0;
        !          1277:        int save = 0;
        !          1278:        char encoding;
        !          1279:        
        !          1280:        if (gmime_interfaces_utf8) {
        !          1281:                if (strcasecmp (charset, "UTF-8") != 0)
        !          1282:                        cd = g_mime_iconv_open (charset, "UTF-8");
        !          1283:                
        !          1284:                if (cd != (iconv_t) -1) {
        !          1285:                        uword = g_mime_iconv_strndup (cd, word, len);
        !          1286:                        g_mime_iconv_close (cd);
        !          1287:                }
        !          1288:                
        !          1289:                if (uword) {
        !          1290:                        len = strlen (uword);
        !          1291:                        word = uword;
        !          1292:                } else {
        !          1293:                        charset = "UTF-8";
        !          1294:                }
        !          1295:        }
        !          1296:        
        !          1297:        switch (g_mime_utils_best_encoding (word, len)) {
        !          1298:        case GMIME_PART_ENCODING_BASE64:
        !          1299:                enclen = BASE64_ENCODE_LEN (len);
        !          1300:                encoded = alloca (enclen);
        !          1301:                
        !          1302:                encoding = 'b';
        !          1303:                
        !          1304:                pos = g_mime_utils_base64_encode_close (word, len, encoded, &state, &save);
        !          1305:                encoded[pos] = '\0';
        !          1306:                
        !          1307:                /* remove \n chars as headers need to be wrapped differently */
        !          1308:                ptr = encoded;
        !          1309:                while ((ptr = memchr (ptr, '\n', strlen (ptr))))
        !          1310:                        memmove (ptr, ptr + 1, strlen (ptr));
        !          1311:                
        !          1312:                break;
        !          1313:        case GMIME_PART_ENCODING_QUOTEDPRINTABLE:
        !          1314:                enclen = QP_ENCODE_LEN (len);
        !          1315:                encoded = alloca (enclen);
        !          1316:                
        !          1317:                encoding = 'q';
        !          1318:                
        !          1319:                pos = quoted_encode (word, len, encoded, safemask);
        !          1320:                encoded[pos] = '\0';
        !          1321:                
        !          1322:                break;
        !          1323:        default:
        !          1324:                g_assert_not_reached ();
        !          1325:        }
        !          1326:        
        !          1327:        g_free (uword);
        !          1328:        
        !          1329:        g_string_sprintfa (string, "=?%s?%c?%s?=", charset, encoding, encoded);
        !          1330: }
        !          1331: 
        !          1332: 
        !          1333: /**
        !          1334:  * g_mime_utils_8bit_header_encode_phrase:
        !          1335:  * @in: header to encode
        !          1336:  *
        !          1337:  * Encodes a header phrase according to the rules in rfc2047.
        !          1338:  *
        !          1339:  * Returns the header phrase as 1 encoded atom. Useful for encoding
        !          1340:  * internet addresses.
        !          1341:  **/
        !          1342: char *
        !          1343: g_mime_utils_8bit_header_encode_phrase (const unsigned char *in)
        !          1344: {
        !          1345:        const char *charset;
        !          1346:        GString *string;
        !          1347:        size_t len;
        !          1348:        char *str;
        !          1349:        
        !          1350:        if (in == NULL)
        !          1351:                return NULL;
        !          1352:        
        !          1353:        len = strlen (in);
        !          1354:        
        !          1355:        if (gmime_interfaces_utf8) {
        !          1356:                charset = g_mime_charset_best (in, len);
        !          1357:                charset = charset ? charset : "iso-8859-1";
        !          1358:        } else {
        !          1359:                charset = g_mime_charset_locale_name ();
        !          1360:        }
        !          1361:        
        !          1362:        string = g_string_new ("");
        !          1363:        
        !          1364:        rfc2047_encode_word (string, in, strlen (in), charset, IS_ESAFE);
        !          1365:        
        !          1366:        str = string->str;
        !          1367:        g_string_free (string, FALSE);
        !          1368:        
        !          1369:        return str;
        !          1370: }
        !          1371: 
        !          1372: 
        !          1373: enum _phrase_word_t {
        !          1374:        WORD_ATOM,
        !          1375:        WORD_2047
        !          1376: };
        !          1377: 
        !          1378: struct _phrase_word {
        !          1379:        struct _phrase_word *next;
        !          1380:        const unsigned char *start, *end;
        !          1381:        enum _phrase_word_t type;
        !          1382:        int encoding;
        !          1383: };
        !          1384: 
        !          1385: static gboolean
        !          1386: word_types_compatable (enum _phrase_word_t type1, enum _phrase_word_t type2)
        !          1387: {
        !          1388:        switch (type1) {
        !          1389:        case WORD_ATOM:
        !          1390:                return FALSE;
        !          1391:        case WORD_2047:
        !          1392:                return type2 == WORD_2047;
        !          1393:        default:
        !          1394:                return FALSE;
        !          1395:        }
        !          1396: }
        !          1397: 
        !          1398: static struct _phrase_word *
        !          1399: rfc2047_encode_phrase_get_words (const unsigned char *in)
        !          1400: {
        !          1401:        const unsigned char *inptr, *start, *last;
        !          1402:        struct _phrase_word *words, *tail, *word;
        !          1403:        enum _phrase_word_t type = WORD_ATOM;
        !          1404:        int count = 0, encoding = 0;
        !          1405:        
        !          1406:        words = NULL;
        !          1407:        tail = (struct _phrase_word *) &words;
        !          1408:        
        !          1409:        last = start = inptr = in;
        !          1410:        while (inptr && *inptr) {
        !          1411:                gboolean is_space;
        !          1412:                unichar c;
        !          1413:                
        !          1414:                if (gmime_interfaces_utf8) {
        !          1415:                        const char *newinptr;
        !          1416:                        
        !          1417:                        newinptr = unicode_next_char (inptr);
        !          1418:                        c = unicode_get_char (inptr);
        !          1419:                        if (newinptr == NULL || !unichar_validate (c)) {
        !          1420:                                w(g_warning ("Invalid UTF-8 sequence encountered"));
        !          1421:                                inptr++;
        !          1422:                                continue;
        !          1423:                        }
        !          1424:                        
        !          1425:                        inptr = newinptr;
        !          1426:                        
        !          1427:                        is_space = unichar_isspace (c);
        !          1428:                } else {
        !          1429:                        is_space = isspace ((int) *inptr);
        !          1430:                        c = *inptr++;
        !          1431:                }
        !          1432:                
        !          1433:                if (is_space) {
        !          1434:                        if (count > 0) {
        !          1435:                                word = g_new (struct _phrase_word, 1);
        !          1436:                                word->next = NULL;
        !          1437:                                word->start = start;
        !          1438:                                word->end = last;
        !          1439:                                word->type = type;
        !          1440:                                word->encoding = encoding;
        !          1441:                                
        !          1442:                                tail->next = word;
        !          1443:                                tail = word;
        !          1444:                                count = 0;
        !          1445:                        }
        !          1446:                        
        !          1447:                        start = inptr;
        !          1448:                        type = WORD_ATOM;
        !          1449:                        encoding = 0;
        !          1450:                } else {
        !          1451:                        count++;
        !          1452:                        if (c > 127 && c < 256) {
        !          1453:                                type = WORD_2047;
        !          1454:                                encoding = MAX (encoding, 2);
        !          1455:                        } else if (c >= 256) {
        !          1456:                                type = WORD_2047;
        !          1457:                                encoding = 2;
        !          1458:                        }
        !          1459:                }
        !          1460:                
        !          1461:                last = inptr;
        !          1462:        }
        !          1463:        
        !          1464:        if (count > 0) {
        !          1465:                word = g_new (struct _phrase_word, 1);
        !          1466:                word->next = NULL;
        !          1467:                word->start = start;
        !          1468:                word->end = last;
        !          1469:                word->type = type;
        !          1470:                word->encoding = encoding;
        !          1471:                
        !          1472:                tail->next = word;
        !          1473:                tail = word;
        !          1474:        }
        !          1475:        
        !          1476:        return words;
        !          1477: }
        !          1478: 
        !          1479: static gboolean
        !          1480: rfc2047_encode_phrase_merge_words (struct _phrase_word **wordsp)
        !          1481: {
        !          1482:        struct _phrase_word *word, *next, *words = *wordsp;
        !          1483:        gboolean merged = FALSE;
        !          1484:        
        !          1485:        /* scan the list, checking for words of similar types that can be merged */
        !          1486:        word = words;
        !          1487:        while (word) {
        !          1488:                next = word->next;
        !          1489:                
        !          1490:                while (next) {
        !          1491:                        /* merge nodes of the same type AND we are not creating too long a string */
        !          1492:                        if (word_types_compatable (word->type, next->type)) {
        !          1493:                                if (next->end - word->start < GMIME_FOLD_PREENCODED) {
        !          1494:                                        /* the resulting word type is the MAX of the 2 types */
        !          1495:                                        word->type = MAX (word->type, next->type);
        !          1496:                                        
        !          1497:                                        word->end = next->end;
        !          1498:                                        word->next = next->next;
        !          1499:                                        
        !          1500:                                        g_free (next);
        !          1501:                                        
        !          1502:                                        next = word->next;
        !          1503:                                        
        !          1504:                                        merged = TRUE;
        !          1505:                                } else {
        !          1506:                                        /* if it is going to be too long, make sure we include the
        !          1507:                                           separating whitespace */
        !          1508:                                        word->end = next->start;
        !          1509:                                        break;
        !          1510:                                }
        !          1511:                        } else {
        !          1512:                                break;
        !          1513:                        }
        !          1514:                }
        !          1515:                
        !          1516:                word = word->next;
        !          1517:        }
        !          1518:        
        !          1519:        *wordsp = words;
        !          1520:        
        !          1521:        return merged;
        !          1522: }
        !          1523: 
        !          1524: static char *
        !          1525: rfc2047_encode_phrase (const unsigned char *in)
        !          1526: {
        !          1527:        struct _phrase_word *words, *word, *prev = NULL;
        !          1528:        GString *out;
        !          1529:        char *outstr;
        !          1530:        
        !          1531:        if (in == NULL)
        !          1532:                return NULL;
        !          1533:        
        !          1534:        words = rfc2047_encode_phrase_get_words (in);
        !          1535:        if (!words)
        !          1536:                return NULL;
        !          1537:        
        !          1538:        while (rfc2047_encode_phrase_merge_words (&words))
        !          1539:                ;
        !          1540:        
        !          1541:        out = g_string_new ("");
        !          1542:        
        !          1543:        /* output words now with spaces between them */
        !          1544:        word = words;
        !          1545:        while (word) {
        !          1546:                const char *start;
        !          1547:                size_t len;
        !          1548:                
        !          1549:                /* append correct number of spaces between words */
        !          1550:                if (prev && !(prev->type == WORD_2047 && word->type == WORD_2047)) {
        !          1551:                        /* one or both of the words are not encoded so we write the spaces out untouched */
        !          1552:                        len = word->start - prev->end;
        !          1553:                        g_string_append_len (out, prev->end, len);
        !          1554:                }
        !          1555:                
        !          1556:                switch (word->type) {
        !          1557:                case WORD_ATOM:
        !          1558:                        g_string_append_len (out, word->start, word->end - word->start);
        !          1559:                        break;
        !          1560:                case WORD_2047:
        !          1561:                        if (prev && prev->type == WORD_2047) {
        !          1562:                                /* include the whitespace chars between these 2 words in the
        !          1563:                                    resulting rfc2047 encoded word. */
        !          1564:                                len = word->end - prev->end;
        !          1565:                                start = prev->end;
        !          1566:                                
        !          1567:                                /* encoded words need to be separated by linear whitespace */
        !          1568:                                g_string_append_c (out, ' ');
        !          1569:                        } else {
        !          1570:                                len = word->end - word->start;
        !          1571:                                start = word->start;
        !          1572:                        }
        !          1573:                        
        !          1574:                        if (word->encoding == 1 || !gmime_interfaces_utf8)
        !          1575:                                rfc2047_encode_word (out, start, len, "iso-8859-1", IS_PSAFE);
        !          1576:                        else
        !          1577:                                rfc2047_encode_word (out, start, len,
        !          1578:                                                     g_mime_charset_best (start, len), IS_PSAFE);
        !          1579:                        break;
        !          1580:                }
        !          1581:                
        !          1582:                g_free (prev);
        !          1583:                prev = word;
        !          1584:                word = word->next;
        !          1585:        }
        !          1586:        
        !          1587:        g_free (prev);
        !          1588:        
        !          1589:        outstr = out->str;
        !          1590:        g_string_free (out, FALSE);
        !          1591:        
        !          1592:        return outstr;
        !          1593: }
        !          1594: 
        !          1595: 
        !          1596: /**
        !          1597:  * g_mime_utils_8bit_header_encode:
        !          1598:  * @in: header to encode
        !          1599:  *
        !          1600:  * Encodes a header according to the rules in rfc2047.
        !          1601:  *
        !          1602:  * Returns the header as several encoded atoms. Useful for encoding
        !          1603:  * headers like "Subject".
        !          1604:  **/
        !          1605: char *
        !          1606: g_mime_utils_8bit_header_encode (const unsigned char *in)
        !          1607: {
        !          1608:        return rfc2047_encode_phrase (in);
        !          1609: }
        !          1610: 
        !          1611: 
        !          1612: /**
        !          1613:  * g_mime_utils_base64_encode_close:
        !          1614:  * @in: input stream
        !          1615:  * @inlen: length of the input
        !          1616:  * @out: output string
        !          1617:  * @state: holds the number of bits that are stored in @save
        !          1618:  * @save: leftover bits that have not yet been encoded
        !          1619:  *
        !          1620:  * Base64 encodes the input stream to the output stream. Call this
        !          1621:  * when finished encoding data with g_mime_utils_base64_encode_step to
        !          1622:  * flush off the last little bit.
        !          1623:  *
        !          1624:  * Returns the number of bytes encoded.
        !          1625:  **/
        !          1626: size_t
        !          1627: g_mime_utils_base64_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
        !          1628: {
        !          1629:        unsigned char *outptr = out;
        !          1630:        int c1, c2;
        !          1631:        
        !          1632:        if (inlen > 0)
        !          1633:                outptr += g_mime_utils_base64_encode_step (in, inlen, outptr, state, save);
        !          1634:        
        !          1635:        c1 = ((unsigned char *)save)[1];
        !          1636:        c2 = ((unsigned char *)save)[2];
        !          1637:        
        !          1638:        switch (((unsigned char *)save)[0]) {
        !          1639:        case 2:
        !          1640:                outptr[2] = base64_alphabet [(c2 & 0x0f) << 2];
        !          1641:                goto skip;
        !          1642:        case 1:
        !          1643:                outptr[2] = '=';
        !          1644:        skip:
        !          1645:                outptr[0] = base64_alphabet [c1 >> 2];
        !          1646:                outptr[1] = base64_alphabet [c2 >> 4 | ((c1 & 0x3) << 4)];
        !          1647:                outptr[3] = '=';
        !          1648:                outptr += 4;
        !          1649:                break;
        !          1650:        }
        !          1651:        
        !          1652:        *outptr++ = '\n';
        !          1653:        
        !          1654:        *save = 0;
        !          1655:        *state = 0;
        !          1656:        
        !          1657:        return (outptr - out);
        !          1658: }
        !          1659: 
        !          1660: 
        !          1661: /**
        !          1662:  * g_mime_utils_base64_encode_step:
        !          1663:  * @in: input stream
        !          1664:  * @inlen: length of the input
        !          1665:  * @out: output string
        !          1666:  * @state: holds the number of bits that are stored in @save
        !          1667:  * @save: leftover bits that have not yet been encoded
        !          1668:  *
        !          1669:  * Base64 encodes a chunk of data. Performs an 'encode step', only
        !          1670:  * encodes blocks of 3 characters to the output at a time, saves
        !          1671:  * left-over state in state and save (initialise to 0 on first
        !          1672:  * invocation).
        !          1673:  *
        !          1674:  * Returns the number of bytes encoded.
        !          1675:  **/
        !          1676: size_t
        !          1677: g_mime_utils_base64_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
        !          1678: {
        !          1679:        const register unsigned char *inptr;
        !          1680:        register unsigned char *outptr;
        !          1681:        
        !          1682:        if (inlen <= 0)
        !          1683:                return 0;
        !          1684:        
        !          1685:        inptr = in;
        !          1686:        outptr = out;
        !          1687:        
        !          1688:        if (inlen + ((unsigned char *)save)[0] > 2) {
        !          1689:                const unsigned char *inend = in + inlen - 2;
        !          1690:                register int c1 = 0, c2 = 0, c3 = 0;
        !          1691:                register int already;
        !          1692:                
        !          1693:                already = *state;
        !          1694:                
        !          1695:                switch (((char *)save)[0]) {
        !          1696:                case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
        !          1697:                case 2: c1 = ((unsigned char *)save)[1];
        !          1698:                        c2 = ((unsigned char *)save)[2]; goto skip2;
        !          1699:                }
        !          1700:                
        !          1701:                /* yes, we jump into the loop, no i'm not going to change it, its beautiful! */
        !          1702:                while (inptr < inend) {
        !          1703:                        c1 = *inptr++;
        !          1704:                skip1:
        !          1705:                        c2 = *inptr++;
        !          1706:                skip2:
        !          1707:                        c3 = *inptr++;
        !          1708:                        *outptr++ = base64_alphabet [c1 >> 2];
        !          1709:                        *outptr++ = base64_alphabet [(c2 >> 4) | ((c1 & 0x3) << 4)];
        !          1710:                        *outptr++ = base64_alphabet [((c2 & 0x0f) << 2) | (c3 >> 6)];
        !          1711:                        *outptr++ = base64_alphabet [c3 & 0x3f];
        !          1712:                        /* this is a bit ugly ... */
        !          1713:                        if ((++already) >= 19) {
        !          1714:                                *outptr++ = '\n';
        !          1715:                                already = 0;
        !          1716:                        }
        !          1717:                }
        !          1718:                
        !          1719:                ((unsigned char *)save)[0] = 0;
        !          1720:                inlen = 2 - (inptr - inend);
        !          1721:                *state = already;
        !          1722:        }
        !          1723:        
        !          1724:        d(printf ("state = %d, inlen = %d\n", (int)((char *)save)[0], inlen));
        !          1725:        
        !          1726:        if (inlen > 0) {
        !          1727:                register char *saveout;
        !          1728:                
        !          1729:                /* points to the slot for the next char to save */
        !          1730:                saveout = & (((char *)save)[1]) + ((char *)save)[0];
        !          1731:                
        !          1732:                /* inlen can only be 0 1 or 2 */
        !          1733:                switch (inlen) {
        !          1734:                case 2: *saveout++ = *inptr++;
        !          1735:                case 1: *saveout++ = *inptr++;
        !          1736:                }
        !          1737:                ((char *)save)[0] += inlen;
        !          1738:        }
        !          1739:        
        !          1740:        d(printf ("mode = %d\nc1 = %c\nc2 = %c\n",
        !          1741:                  (int)((char *)save)[0],
        !          1742:                  (int)((char *)save)[1],
        !          1743:                  (int)((char *)save)[2]));
        !          1744:        
        !          1745:        return (outptr - out);
        !          1746: }
        !          1747: 
        !          1748: /**
        !          1749:  * g_mime_utils_base64_decode_step:
        !          1750:  * @in: input stream
        !          1751:  * @inlen: max length of data to decode
        !          1752:  * @out: output stream
        !          1753:  * @state: holds the number of bits that are stored in @save
        !          1754:  * @save: leftover bits that have not yet been decoded
        !          1755:  *
        !          1756:  * Decodes a chunk of base64 encoded data.
        !          1757:  *
        !          1758:  * Returns the number of bytes decoded (which have been dumped in @out).
        !          1759:  **/
        !          1760: size_t
        !          1761: g_mime_utils_base64_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
        !          1762: {
        !          1763:        const register unsigned char *inptr;
        !          1764:        register unsigned char *outptr;
        !          1765:        const unsigned char *inend;
        !          1766:        register guint32 saved;
        !          1767:        unsigned char c;
        !          1768:        int i;
        !          1769:        
        !          1770:        inend = in + inlen;
        !          1771:        outptr = out;
        !          1772:        
        !          1773:        /* convert 4 base64 bytes to 3 normal bytes */
        !          1774:        saved = *save;
        !          1775:        i = *state;
        !          1776:        inptr = in;
        !          1777:        while (inptr < inend) {
        !          1778:                c = gmime_base64_rank[*inptr++];
        !          1779:                if (c != 0xff) {
        !          1780:                        saved = (saved << 6) | c;
        !          1781:                        i++;
        !          1782:                        if (i == 4) {
        !          1783:                                *outptr++ = saved >> 16;
        !          1784:                                *outptr++ = saved >> 8;
        !          1785:                                *outptr++ = saved;
        !          1786:                                i = 0;
        !          1787:                        }
        !          1788:                }
        !          1789:        }
        !          1790:        
        !          1791:        *save = saved;
        !          1792:        *state = i;
        !          1793:        
        !          1794:        /* quick scan back for '=' on the end somewhere */
        !          1795:        /* fortunately we can drop 1 output char for each trailing = (upto 2) */
        !          1796:        i = 2;
        !          1797:        while (inptr > in && i) {
        !          1798:                inptr--;
        !          1799:                if (gmime_base64_rank[*inptr] != 0xff) {
        !          1800:                        if (*inptr == '=' && outptr > out)
        !          1801:                                outptr--;
        !          1802:                        i--;
        !          1803:                }
        !          1804:        }
        !          1805:        
        !          1806:        /* if i != 0 then there is a truncation error! */
        !          1807:        return (outptr - out);
        !          1808: }
        !          1809: 
        !          1810: 
        !          1811: /**
        !          1812:  * g_mime_utils_uuencode_close:
        !          1813:  * @in: input stream
        !          1814:  * @inlen: input stream length
        !          1815:  * @out: output stream
        !          1816:  * @uubuf: temporary buffer of 60 bytes
        !          1817:  * @state: holds the number of bits that are stored in @save
        !          1818:  * @save: leftover bits that have not yet been encoded
        !          1819:  *
        !          1820:  * Uuencodes a chunk of data. Call this when finished encoding data
        !          1821:  * with g_mime_utils_uuencode_step to flush off the last little bit.
        !          1822:  *
        !          1823:  * Returns the number of bytes encoded.
        !          1824:  **/
        !          1825: size_t
        !          1826: g_mime_utils_uuencode_close (const unsigned char *in, size_t inlen, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save)
        !          1827: {
        !          1828:        register unsigned char *outptr, *bufptr;
        !          1829:        register guint32 saved;
        !          1830:        int uulen, uufill, i;
        !          1831:        
        !          1832:        outptr = out;
        !          1833:        
        !          1834:        if (inlen > 0)
        !          1835:                outptr += g_mime_utils_uuencode_step (in, inlen, out, uubuf, state, save);
        !          1836:        
        !          1837:        uufill = 0;
        !          1838:        
        !          1839:        saved = *save;
        !          1840:        i = *state & 0xff;
        !          1841:        uulen = (*state >> 8) & 0xff;
        !          1842:        
        !          1843:        bufptr = uubuf + ((uulen / 3) * 4);
        !          1844:        
        !          1845:        if (i > 0) {
        !          1846:                while (i < 3) {
        !          1847:                        saved <<= 8 | 0;
        !          1848:                        uufill++;
        !          1849:                        i++;
        !          1850:                }
        !          1851:                
        !          1852:                if (i == 3) {
        !          1853:                        /* convert 3 normal bytes into 4 uuencoded bytes */
        !          1854:                        unsigned char b0, b1, b2;
        !          1855:                        
        !          1856:                        b0 = saved >> 16;
        !          1857:                        b1 = saved >> 8 & 0xff;
        !          1858:                        b2 = saved & 0xff;
        !          1859:                        
        !          1860:                        *bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
        !          1861:                        *bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
        !          1862:                        *bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
        !          1863:                        *bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
        !          1864:                        
        !          1865:                        i = 0;
        !          1866:                        saved = 0;
        !          1867:                        uulen += 3;
        !          1868:                }
        !          1869:        }
        !          1870:        
        !          1871:        if (uulen > 0) {
        !          1872:                int cplen = ((uulen / 3) * 4);
        !          1873:                
        !          1874:                *outptr++ = GMIME_UUENCODE_CHAR ((uulen - uufill) & 0xff);
        !          1875:                memcpy (outptr, uubuf, cplen);
        !          1876:                outptr += cplen;
        !          1877:                *outptr++ = '\n';
        !          1878:                uulen = 0;
        !          1879:        }
        !          1880:        
        !          1881:        *outptr++ = GMIME_UUENCODE_CHAR (uulen & 0xff);
        !          1882:        *outptr++ = '\n';
        !          1883:        
        !          1884:        *save = 0;
        !          1885:        *state = 0;
        !          1886:        
        !          1887:        return (outptr - out);
        !          1888: }
        !          1889: 
        !          1890: 
        !          1891: /**
        !          1892:  * g_mime_utils_uuencode_step:
        !          1893:  * @in: input stream
        !          1894:  * @inlen: input stream length
        !          1895:  * @out: output stream
        !          1896:  * @uubuf: temporary buffer of 60 bytes
        !          1897:  * @state: holds the number of bits that are stored in @save
        !          1898:  * @save: leftover bits that have not yet been encoded
        !          1899:  *
        !          1900:  * Uuencodes a chunk of data. Performs an 'encode step', only encodes
        !          1901:  * blocks of 45 characters to the output at a time, saves left-over
        !          1902:  * state in @uubuf, @state and @save (initialize to 0 on first
        !          1903:  * invocation).
        !          1904:  *
        !          1905:  * Returns the number of bytes encoded.
        !          1906:  **/
        !          1907: size_t
        !          1908: g_mime_utils_uuencode_step (const unsigned char *in, size_t inlen, unsigned char *out, unsigned char *uubuf, int *state, guint32 *save)
        !          1909: {
        !          1910:        register unsigned char *outptr, *bufptr;
        !          1911:        const register unsigned char *inptr;
        !          1912:        const unsigned char *inend;
        !          1913:        register guint32 saved;
        !          1914:        int uulen, i;
        !          1915:        
        !          1916:        saved = *save;
        !          1917:        i = *state & 0xff;
        !          1918:        uulen = (*state >> 8) & 0xff;
        !          1919:        
        !          1920:        inptr = in;
        !          1921:        inend = in + inlen;
        !          1922:        
        !          1923:        outptr = out;
        !          1924:        
        !          1925:        bufptr = uubuf + ((uulen / 3) * 4);
        !          1926:        
        !          1927:        while (inptr < inend) {
        !          1928:                while (uulen < 45 && inptr < inend) {
        !          1929:                        while (i < 3 && inptr < inend) {
        !          1930:                                saved = (saved << 8) | *inptr++;
        !          1931:                                i++;
        !          1932:                        }
        !          1933:                        
        !          1934:                        if (i == 3) {
        !          1935:                                /* convert 3 normal bytes into 4 uuencoded bytes */
        !          1936:                                unsigned char b0, b1, b2;
        !          1937:                                
        !          1938:                                b0 = saved >> 16;
        !          1939:                                b1 = saved >> 8 & 0xff;
        !          1940:                                b2 = saved & 0xff;
        !          1941:                                
        !          1942:                                *bufptr++ = GMIME_UUENCODE_CHAR ((b0 >> 2) & 0x3f);
        !          1943:                                *bufptr++ = GMIME_UUENCODE_CHAR (((b0 << 4) | ((b1 >> 4) & 0xf)) & 0x3f);
        !          1944:                                *bufptr++ = GMIME_UUENCODE_CHAR (((b1 << 2) | ((b2 >> 6) & 0x3)) & 0x3f);
        !          1945:                                *bufptr++ = GMIME_UUENCODE_CHAR (b2 & 0x3f);
        !          1946:                                
        !          1947:                                i = 0;
        !          1948:                                saved = 0;
        !          1949:                                uulen += 3;
        !          1950:                        }
        !          1951:                }
        !          1952:                
        !          1953:                if (uulen >= 45) {
        !          1954:                        *outptr++ = GMIME_UUENCODE_CHAR (uulen & 0xff);
        !          1955:                        memcpy (outptr, uubuf, ((uulen / 3) * 4));
        !          1956:                        outptr += ((uulen / 3) * 4);
        !          1957:                        *outptr++ = '\n';
        !          1958:                        uulen = 0;
        !          1959:                        bufptr = uubuf;
        !          1960:                }
        !          1961:        }
        !          1962:        
        !          1963:        *save = saved;
        !          1964:        *state = ((uulen & 0xff) << 8) | (i & 0xff);
        !          1965:        
        !          1966:        return (outptr - out);
        !          1967: }
        !          1968: 
        !          1969: 
        !          1970: /**
        !          1971:  * g_mime_utils_uudecode_step:
        !          1972:  * @in: input stream
        !          1973:  * @inlen: max length of data to decode (normally strlen (in) ??)
        !          1974:  * @out: output stream
        !          1975:  * @state: holds the number of bits that are stored in @save
        !          1976:  * @save: leftover bits that have not yet been decoded
        !          1977:  *
        !          1978:  * Uudecodes a chunk of data. Performs a 'decode step' on a chunk of
        !          1979:  * uuencoded data. Assumes the "begin <mode> <file name>" line has
        !          1980:  * been stripped off.
        !          1981:  *
        !          1982:  * Returns the number of bytes decoded.
        !          1983:  **/
        !          1984: size_t
        !          1985: g_mime_utils_uudecode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, guint32 *save)
        !          1986: {
        !          1987:        const register unsigned char *inptr;
        !          1988:        register unsigned char *outptr;
        !          1989:        const unsigned char *inend;
        !          1990:        unsigned char ch;
        !          1991:        register guint32 saved;
        !          1992:        gboolean last_was_eoln;
        !          1993:        int uulen, i;
        !          1994:        
        !          1995:        if (*state & GMIME_UUDECODE_STATE_END)
        !          1996:                return 0;
        !          1997:        
        !          1998:        saved = *save;
        !          1999:        i = *state & 0xff;
        !          2000:        uulen = (*state >> 8) & 0xff;
        !          2001:        if (uulen == 0)
        !          2002:                last_was_eoln = TRUE;
        !          2003:        else
        !          2004:                last_was_eoln = FALSE;
        !          2005:        
        !          2006:        inend = in + inlen;
        !          2007:        outptr = out;
        !          2008:        
        !          2009:        inptr = in;
        !          2010:        while (inptr < inend) {
        !          2011:                if (*inptr == '\n') {
        !          2012:                        last_was_eoln = TRUE;
        !          2013:                        
        !          2014:                        inptr++;
        !          2015:                        continue;
        !          2016:                } else if (!uulen || last_was_eoln) {
        !          2017:                        /* first octet on a line is the uulen octet */
        !          2018:                        uulen = gmime_uu_rank[*inptr];
        !          2019:                        last_was_eoln = FALSE;
        !          2020:                        if (uulen == 0) {
        !          2021:                                *state |= GMIME_UUDECODE_STATE_END;
        !          2022:                                break;
        !          2023:                        }
        !          2024:                        
        !          2025:                        inptr++;
        !          2026:                        continue;
        !          2027:                }
        !          2028:                
        !          2029:                ch = *inptr++;
        !          2030:                
        !          2031:                if (uulen > 0) {
        !          2032:                        /* save the byte */
        !          2033:                        saved = (saved << 8) | ch;
        !          2034:                        i++;
        !          2035:                        if (i == 4) {
        !          2036:                                /* convert 4 uuencoded bytes to 3 normal bytes */
        !          2037:                                unsigned char b0, b1, b2, b3;
        !          2038:                                
        !          2039:                                b0 = saved >> 24;
        !          2040:                                b1 = saved >> 16 & 0xff;
        !          2041:                                b2 = saved >> 8 & 0xff;
        !          2042:                                b3 = saved & 0xff;
        !          2043:                                
        !          2044:                                if (uulen >= 3) {
        !          2045:                                        *outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
        !          2046:                                        *outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
        !          2047:                                        *outptr++ = gmime_uu_rank[b2] << 6 | gmime_uu_rank[b3];
        !          2048:                                } else {
        !          2049:                                        if (uulen >= 1) {
        !          2050:                                                *outptr++ = gmime_uu_rank[b0] << 2 | gmime_uu_rank[b1] >> 4;
        !          2051:                                        }
        !          2052:                                        if (uulen >= 2) {
        !          2053:                                                *outptr++ = gmime_uu_rank[b1] << 4 | gmime_uu_rank[b2] >> 2;
        !          2054:                                        }
        !          2055:                                }
        !          2056:                                
        !          2057:                                i = 0;
        !          2058:                                saved = 0;
        !          2059:                                uulen -= 3;
        !          2060:                        }
        !          2061:                } else {
        !          2062:                        break;
        !          2063:                }
        !          2064:        }
        !          2065:        
        !          2066:        *save = saved;
        !          2067:        *state = (*state & GMIME_UUDECODE_STATE_MASK) | ((uulen & 0xff) << 8) | (i & 0xff);
        !          2068:        
        !          2069:        return (outptr - out);
        !          2070: }
        !          2071: 
        !          2072: 
        !          2073: /**
        !          2074:  * g_mime_utils_quoted_encode_close:
        !          2075:  * @in: input stream
        !          2076:  * @inlen: length of the input
        !          2077:  * @out: output string
        !          2078:  * @state: holds the number of bits that are stored in @save
        !          2079:  * @save: leftover bits that have not yet been encoded
        !          2080:  *
        !          2081:  * Quoted-printable encodes a block of text. Call this when finished
        !          2082:  * encoding data with g_mime_utils_quoted_encode_step to flush off the
        !          2083:  * last little bit.
        !          2084:  *
        !          2085:  * Returns the number of bytes encoded.
        !          2086:  **/
        !          2087: size_t
        !          2088: g_mime_utils_quoted_encode_close (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
        !          2089: {
        !          2090:        register unsigned char *outptr = out;
        !          2091:        int last;
        !          2092:        
        !          2093:        if (inlen > 0)
        !          2094:                outptr += g_mime_utils_quoted_encode_step (in, inlen, outptr, state, save);
        !          2095:        
        !          2096:        last = *state;
        !          2097:        if (last != -1) {
        !          2098:                /* space/tab must be encoded if its the last character on
        !          2099:                   the line */
        !          2100:                if (is_qpsafe (last) && !isblank (last)) {
        !          2101:                        *outptr++ = last;
        !          2102:                } else {
        !          2103:                        *outptr++ = '=';
        !          2104:                        *outptr++ = tohex[(last >> 4) & 0xf];
        !          2105:                        *outptr++ = tohex[last & 0xf];
        !          2106:                }
        !          2107:        }
        !          2108:        
        !          2109:        *outptr++ = '\n';
        !          2110:        
        !          2111:        *save = 0;
        !          2112:        *state = -1;
        !          2113:        
        !          2114:        return (outptr - out);
        !          2115: }
        !          2116: 
        !          2117: 
        !          2118: /**
        !          2119:  * g_mime_utils_quoted_encode_step:
        !          2120:  * @in: input stream
        !          2121:  * @inlen: length of the input
        !          2122:  * @out: output string
        !          2123:  * @state: holds the number of bits that are stored in @save
        !          2124:  * @save: leftover bits that have not yet been encoded
        !          2125:  *
        !          2126:  * Quoted-printable encodes a block of text. Performs an 'encode
        !          2127:  * step', saves left-over state in state and save (initialise to -1 on
        !          2128:  * first invocation).
        !          2129:  *
        !          2130:  * Returns the number of bytes encoded.
        !          2131:  **/
        !          2132: size_t
        !          2133: g_mime_utils_quoted_encode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *state, int *save)
        !          2134: {
        !          2135:        const register unsigned char *inptr, *inend;
        !          2136:        register unsigned char *outptr;
        !          2137:        unsigned char c;
        !          2138:        register int sofar = *save;  /* keeps track of how many chars on a line */
        !          2139:        register int last = *state;  /* keeps track if last char to end was a space cr etc */
        !          2140:        
        !          2141:        inptr = in;
        !          2142:        inend = in + inlen;
        !          2143:        outptr = out;
        !          2144:        while (inptr < inend) {
        !          2145:                c = *inptr++;
        !          2146:                if (c == '\r') {
        !          2147:                        if (last != -1) {
        !          2148:                                *outptr++ = '=';
        !          2149:                                *outptr++ = tohex[(last >> 4) & 0xf];
        !          2150:                                *outptr++ = tohex[last & 0xf];
        !          2151:                                sofar += 3;
        !          2152:                        }
        !          2153:                        last = c;
        !          2154:                } else if (c == '\n') {
        !          2155:                        if (last != -1 && last != '\r') {
        !          2156:                                *outptr++ = '=';
        !          2157:                                *outptr++ = tohex[(last >> 4) & 0xf];
        !          2158:                                *outptr++ = tohex[last & 0xf];
        !          2159:                        }
        !          2160:                        *outptr++ = '\n';
        !          2161:                        sofar = 0;
        !          2162:                        last = -1;
        !          2163:                } else {
        !          2164:                        if (last != -1) {
        !          2165:                                if (is_qpsafe (last)) {
        !          2166:                                        *outptr++ = last;
        !          2167:                                        sofar++;
        !          2168:                                } else {
        !          2169:                                        *outptr++ = '=';
        !          2170:                                        *outptr++ = tohex[(last >> 4) & 0xf];
        !          2171:                                        *outptr++ = tohex[last & 0xf];
        !          2172:                                        sofar += 3;
        !          2173:                                }
        !          2174:                        }
        !          2175:                        
        !          2176:                        if (is_qpsafe (c)) {
        !          2177:                                if (sofar > 74) {
        !          2178:                                        *outptr++ = '=';
        !          2179:                                        *outptr++ = '\n';
        !          2180:                                        sofar = 0;
        !          2181:                                }
        !          2182:                                
        !          2183:                                /* delay output of space char */
        !          2184:                                if (isblank (c)) {
        !          2185:                                        last = c;
        !          2186:                                } else {
        !          2187:                                        *outptr++ = c;
        !          2188:                                        sofar++;
        !          2189:                                        last = -1;
        !          2190:                                }
        !          2191:                        } else {
        !          2192:                                if (sofar > 72) {
        !          2193:                                        *outptr++ = '=';
        !          2194:                                        *outptr++ = '\n';
        !          2195:                                        sofar = 3;
        !          2196:                                } else
        !          2197:                                        sofar += 3;
        !          2198:                                
        !          2199:                                *outptr++ = '=';
        !          2200:                                *outptr++ = tohex[(c >> 4) & 0xf];
        !          2201:                                *outptr++ = tohex[c & 0xf];
        !          2202:                                last = -1;
        !          2203:                        }
        !          2204:                }
        !          2205:        }
        !          2206:        *save = sofar;
        !          2207:        *state = last;
        !          2208:        
        !          2209:        return (outptr - out);
        !          2210: }
        !          2211: 
        !          2212: 
        !          2213: /**
        !          2214:  * g_mime_utils_quoted_decode_step: decode a chunk of QP encoded data
        !          2215:  * @in: input stream
        !          2216:  * @inlen: max length of data to decode
        !          2217:  * @out: output stream
        !          2218:  * @savestate: holds the number of bits that are stored in @save
        !          2219:  * @saved: leftover bits that have not yet been decoded
        !          2220:  *
        !          2221:  * Decodes a block of quoted-printable encoded data. Performs a
        !          2222:  * 'decode step' on a chunk of QP encoded data.
        !          2223:  *
        !          2224:  * Returns the number of bytes decoded.
        !          2225:  **/
        !          2226: size_t
        !          2227: g_mime_utils_quoted_decode_step (const unsigned char *in, size_t inlen, unsigned char *out, int *savestate, int *saved)
        !          2228: {
        !          2229:        /* FIXME: this does not strip trailing spaces from lines (as
        !          2230:         * it should, rfc 2045, section 6.7) Should it also
        !          2231:         * canonicalise the end of line to CR LF??
        !          2232:         *
        !          2233:         * Note: Trailing rubbish (at the end of input), like = or =x
        !          2234:         * or =\r will be lost.
        !          2235:         */
        !          2236:        const register unsigned char *inptr;
        !          2237:        register unsigned char *outptr;
        !          2238:        const unsigned char *inend;
        !          2239:        unsigned char c;
        !          2240:        int state, save;
        !          2241:        
        !          2242:        inend = in + inlen;
        !          2243:        outptr = out;
        !          2244:        
        !          2245:        d(printf ("quoted-printable, decoding text '%.*s'\n", inlen, in));
        !          2246:        
        !          2247:        state = *savestate;
        !          2248:        save = *saved;
        !          2249:        inptr = in;
        !          2250:        while (inptr < inend) {
        !          2251:                switch (state) {
        !          2252:                case 0:
        !          2253:                        while (inptr < inend) {
        !          2254:                                c = *inptr++;
        !          2255:                                /* FIXME: use a specials table to avoid 3 comparisons for the common case */
        !          2256:                                if (c == '=') { 
        !          2257:                                        state = 1;
        !          2258:                                        break;
        !          2259:                                }
        !          2260: #ifdef CANONICALISE_EOL
        !          2261:                                /*else if (c=='\r') {
        !          2262:                                        state = 3;
        !          2263:                                } else if (c=='\n') {
        !          2264:                                        *outptr++ = '\r';
        !          2265:                                        *outptr++ = c;
        !          2266:                                        } */
        !          2267: #endif
        !          2268:                                else {
        !          2269:                                        *outptr++ = c;
        !          2270:                                }
        !          2271:                        }
        !          2272:                        break;
        !          2273:                case 1:
        !          2274:                        c = *inptr++;
        !          2275:                        if (c == '\n') {
        !          2276:                                /* soft break ... unix end of line */
        !          2277:                                state = 0;
        !          2278:                        } else {
        !          2279:                                save = c;
        !          2280:                                state = 2;
        !          2281:                        }
        !          2282:                        break;
        !          2283:                case 2:
        !          2284:                        c = *inptr++;
        !          2285:                        if (isxdigit (c) && isxdigit (save)) {
        !          2286:                                c = toupper (c);
        !          2287:                                save = toupper (save);
        !          2288:                                *outptr++ = (((save >= 'A' ? save - 'A' + 10 : save - '0') & 0x0f) << 4)
        !          2289:                                        | ((c >= 'A' ? c - 'A' + 10 : c - '0') & 0x0f);
        !          2290:                        } else if (c == '\n' && save == '\r') {
        !          2291:                                /* soft break ... canonical end of line */
        !          2292:                        } else {
        !          2293:                                /* just output the data */
        !          2294:                                *outptr++ = '=';
        !          2295:                                *outptr++ = save;
        !          2296:                                *outptr++ = c;
        !          2297:                        }
        !          2298:                        state = 0;
        !          2299:                        break;
        !          2300: #ifdef CANONICALISE_EOL
        !          2301:                case 3:
        !          2302:                        /* convert \n -> to \r\n, leaves \r\n alone */
        !          2303:                        c = *inptr++;
        !          2304:                        if (c == '\n') {
        !          2305:                                *outptr++ = '\r';
        !          2306:                                *outptr++ = c;
        !          2307:                        } else {
        !          2308:                                *outptr++ = '\r';
        !          2309:                                *outptr++ = '\n';
        !          2310:                                *outptr++ = c;
        !          2311:                        }
        !          2312:                        state = 0;
        !          2313:                        break;
        !          2314: #endif
        !          2315:                }
        !          2316:        }
        !          2317:        
        !          2318:        *savestate = state;
        !          2319:        *saved = save;
        !          2320:        
        !          2321:        return (outptr - out);
        !          2322: }

E-mail: