home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libi18n / unicvt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  51.0 KB  |  1,787 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* unicvrt.c   
  19.  * ---------   
  20.  *                
  21.  *
  22.  * This file implements conversions from one Unicode format to another
  23.  * Unicode format.
  24.  *
  25.  * There are no conversions to/from other encodings.
  26.  *
  27.  * There are streams conversion between UTF8 and UCS2, and UTF8 and UTF7.
  28.  * It generates a DLL on Win 32, and at present, normal libraries on mac, X, and
  29.  * Win16. 
  30.  */
  31.  
  32. #define _UNICVT_DLL_  1
  33.  
  34. #include "intlpriv.h"
  35. #include "unicpriv.h"
  36. #include "xp.h"
  37. #include <string.h>
  38.  
  39. #ifdef XP_WIN32
  40. #define XP_ALLOC_PRIV    malloc
  41. #else
  42. #define XP_ALLOC_PRIV    XP_ALLOC
  43. #endif
  44.  
  45. typedef struct utf7_encoding_method_data {
  46.     int16            *fromb64;
  47.     unsigned char    *tob64;
  48.     unsigned char    *shift;
  49.     unsigned char    startshift;
  50.     unsigned char    endshift;
  51. } utf7_encoding_method_data;
  52.  
  53.  
  54. int32
  55. ucs2_to_utf8_buffer(const uint16 *ucs2p, int32 num_chars, 
  56.         unsigned char *utf8p, int32 num_utf8_bytes, int32 *utf8_bytes_written);
  57.  
  58.  
  59. /* Private Helper function prototypes */
  60.  
  61. PRIVATE int16 one_utf8_to_ucs2_char(const unsigned char *utf8p, const unsigned char *utf8endp, 
  62.                                uint16 *onecharp);
  63.  
  64. PRIVATE int16 one_ucs2_to_utf8_char(unsigned char *tobufp, 
  65.         unsigned char *tobufendp, uint16 onechar);
  66.  
  67. PRIVATE unsigned char *intl_utf72utf8(    CCCDataObject        obj,
  68.                 const unsigned char    *utf7buf,
  69.                 int32                utf7bufsz,
  70.                 utf7_encoding_method_data*    opt
  71.                 );
  72. PRIVATE unsigned char *intl_utf82utf7(    CCCDataObject        obj,
  73.                 const unsigned char    *utf8buf,    
  74.                 int32                utf8bufsz,
  75.                 utf7_encoding_method_data*    opt
  76.                 );
  77.  
  78.  
  79. PRIVATE uint16  pad_and_write(uint32 buffer, unsigned char *tobufp, 
  80.                             int16 bufferBitCount, utf7_encoding_method_data*    opt);
  81.  
  82. PRIVATE void swap_ucs2_bytes(unsigned char *ucsbuf, int32 ucsbufsz); 
  83.  
  84.  
  85. /* Private constants */
  86.  
  87. #define MAX_UCS2            0xFFFF     
  88. #define DEFAULT_CHAR        0x003F    /* Default char is "?" */
  89. #define BYTE_MASK            0xBF
  90. #define BYTE_MARK            0x80
  91.  
  92. #define MAX_ASCII            0x7F
  93. #define NOT_BASE64            -1
  94.  
  95.  
  96.  
  97.  
  98.  
  99. /* Take care of different API for different platforms */
  100.  
  101.  
  102. #ifdef XP_WIN32
  103.  
  104. /* UNICVTAPI def now accomplished in libi18n.h */
  105. /*#define  UNICVTAPI __declspec(dllexport)*/
  106.  
  107.  
  108. /* THIS #define IS VERY BAD AND SHOULD BE CHANGED WHEN WE REVISIT
  109.  * THE ERROR HANDLING STUFF AND MOVE IT ALL OUT OF XPSTR.H
  110.  * THE CALL SHOULD BE: extern int MK_OUT_OF_MEMORY; BUT WE HAVE
  111.  * CHICKEN AND EGG LINKING PROBLEMS ON WIN32 BECAUSE THE DLL
  112.  * MUST BE COMPILED BEFORE THE int IS DECLARED.
  113.  */
  114.  
  115. #define MK_OUT_OF_MEMORY    -207
  116.  
  117. #else /* !XP_WIN32 */
  118.  
  119. /* UNICVTAPI def now accomplished in libi18n.h */
  120. /*#define UNICVTAPI*/
  121.  
  122. extern int MK_OUT_OF_MEMORY;
  123.  
  124. #endif /*!XP_WIN32 */
  125.  
  126.  
  127.  
  128. /* UCS-2 to UTF-8 conversion routines */
  129.  
  130. /*
  131.  * mz_ucs2utf8
  132.  * -----------
  133.  * 
  134.  * Takes a CCCDataObject, a buffer of UCS-2 data, and the size of that buffer.
  135.  * Allocates and returns the translation of the UCS-2 data in UTF-8. The caller
  136.  * is responsible for freeing the allocated memory. If the UCS-2 data is not
  137.  * complete, and ends on a character boundary, the extra byte of data is stored
  138.  * in uncvtbuf, and will be used the next time this function is called.
  139.  *
  140.  * Note about swapping: UCS-2 data can come in big-endian or little-endian
  141.  * order, so we need to be aware of the need to potentially swap the data.
  142.  * On the very first block of the stream we will discover (because UCS-2
  143.  * always begins with a byte order mark) whether the data is of the same or
  144.  * opposite endian-ness from us. 
  145.  * The information is store in FromCSID
  146.  * The use of uncvtbuf:
  147.  *   uncvtbuf[0] is 0 or 1
  148.  *     uncvtbuf[0] == 0 - there are no left over last time
  149.  *     uncvtbuf[0] == 1 - there one byte left over last time stored in uncvtbuf[1]
  150.  *
  151.  */
  152. MODULE_PRIVATE UNICVTAPI unsigned char *
  153. mz_ucs2utf8(    CCCDataObject        obj,
  154.                 const unsigned char    *ucsbuf,    /* UCS-2 buf for conv */
  155.                 int32                ucsbufsz)    /* UCS-2 buf size in bytes */
  156. {
  157.     int32    tobufsz;
  158.     unsigned char *tobuf = NULL;
  159.     unsigned char *tobufp, *tobufendp,*ucsp, *ucsendp;
  160.     int16    numUTF8bytes;
  161.     uint16     onechar;
  162.     XP_Bool needToSwap = FALSE;
  163.     int     scanstate = 0;
  164.     unsigned p1=0, p2;
  165.     unsigned char *uncvtbuf =INTL_GetCCCUncvtbuf(obj);
  166.  
  167.  
  168.     if(INTL_GetCCCFromCSID(obj) ==  CS_UCS2_SWAP)
  169.         needToSwap = TRUE;
  170.  
  171.     /* Allocate Memory */
  172.     /* In the worst case, one UCS2 could expand to three byte */
  173.     /* so, the ration is 2:3     */
  174.     tobufsz = (3*(ucsbufsz + 1)) / 2 + 2;
  175.     if ((tobuf = (unsigned char *)XP_ALLOC_PRIV(tobufsz)) == (unsigned char *)NULL) 
  176.     {
  177.         INTL_SetCCCRetval(obj, MK_OUT_OF_MEMORY);
  178.         return(NULL);
  179.     }
  180.  
  181.     /* do the set up */
  182.     tobufendp = tobuf + tobufsz;    /* point to the end of buffer */
  183.     tobufp = tobuf;                    /* point to the begining of buffer */
  184.     ucsp = (unsigned char *)ucsbuf;
  185.     ucsendp = (unsigned char *)ucsbuf + ucsbufsz;
  186.  
  187.     /* Get the unconvert byte */
  188.     if(uncvtbuf[0] > 0)
  189.     {
  190.         p1 = uncvtbuf[1];
  191.         scanstate++; 
  192.     }
  193.     /* Do the conversion */
  194.     while( ucsp < ucsendp ) 
  195.      {
  196.         if(scanstate++ == 0)
  197.         {
  198.             p1 = *ucsp;
  199.         }
  200.         else 
  201.         {
  202.             p2 = *ucsp;
  203.             scanstate = 0;
  204.             onechar = (p1 << 8) | (p2);
  205.             /* Look for (and strip) BYTE_ORDER_MARK */
  206.             if(onechar == NEEDS_SWAP_MARK) 
  207.             {
  208.                 INTL_SetCCCFromCSID(obj, CS_UCS2_SWAP);
  209.                 needToSwap = TRUE;
  210.             } 
  211.             else if(onechar == BYTE_ORDER_MARK)  
  212.             {
  213.                 INTL_SetCCCFromCSID(obj, CS_UCS2);
  214.                 needToSwap = FALSE;
  215.             } 
  216.             else
  217.             {
  218.                 if(needToSwap)
  219.                     numUTF8bytes = one_ucs2_to_utf8_char(tobufp, tobufendp, 
  220.                                     (uint16)((p2 << 8) | (p1)));
  221.                 else
  222.                     numUTF8bytes = one_ucs2_to_utf8_char(tobufp, tobufendp, onechar);
  223.  
  224.                 if(numUTF8bytes == -1) 
  225.                     break; /* out of space in tobuf */
  226.  
  227.                 tobufp += numUTF8bytes;
  228.             }
  229.         }
  230.         ucsp ++;
  231.     }
  232.     *tobufp = '\0';                                /* NULL terminate dest. data */
  233.     INTL_SetCCCLen(obj, tobufp - tobuf);        /* length of processed data, in bytes */
  234.  
  235.     /* If there are left over, set it to uncvtbuf[1] */
  236.     if((uncvtbuf[0] = scanstate) != 0)
  237.         uncvtbuf[1] = p1;
  238.     return(tobuf);
  239. }
  240.  
  241. /* UTF-8 to UCS-2 */
  242.  
  243.  /* 
  244.   * mz_utf82ucs
  245.   * -----------
  246.   *
  247.   * This function takes a streams object, a buffer of utf8 data, and the
  248.   * size of that buffer. It allocates, fills, and returns a buffer of the 
  249.   * equivalent UCS-2 data. The caller is responsible for freeing that
  250.   * data. If the UTF-8 data cannot be completely converted, the unconverted
  251.   * final bytes will be stored in uncvtbuf and used on the next call.
  252.   * 
  253.   * Note: UCS-2 data must always begin with a byte order mark, so we
  254.   * must write that at the beginning of our stream. This function 
  255.   * employs obj->cvtflag to determine if it is indeed at the beginning
  256.   * of the stream. obj->cvtflag starts at 0, and we switch it to 1
  257.   * as we write the byte order mark. 
  258.   *
  259.   * A note on endian-ness: This function will return UCS-2 data of the
  260.   * same endian-ness as the machine we are running on. To generate data
  261.   * of the opposite endian-ness, use mz_utf82ucsswap.
  262.   */
  263.  
  264.  
  265. MODULE_PRIVATE UNICVTAPI unsigned char *
  266. mz_utf82ucs(    CCCDataObject        obj,
  267.                 const unsigned char    *utf8buf,    /* UTF-8 buf for conv */
  268.                 int32                utf8bufsz)    /* UTF-8 buf size in bytes */
  269.  
  270.  
  271. {
  272.  
  273.     unsigned char    *tobuf = NULL;
  274.     int32            tobufsz;
  275.     unsigned char    *tobufp, *utf8p;        /* current byte in bufs    */
  276.      unsigned char    *tobufendp, *utf8endp;    /* end of buffers        */
  277.      int32                    uncvtlen;
  278.     unsigned char *uncvtbuf = INTL_GetCCCUncvtbuf(obj);
  279.  
  280.  
  281.  
  282.     uint16 onechar;
  283.     int16 numoctets;
  284.  
  285.  
  286. #define ucsbufsz    tobufsz
  287. #define ucsbuf        tobuf
  288. #define ucsp        tobufp
  289. #define ucsendp    tobufendp
  290.                                                   /* Allocate a dest buffer:        */
  291.  
  292.  
  293.     /* At worst, all the octets are ASCII, and each 1 byte of UTF 8
  294.      * will take 2 bytes of UCS-2, plus 2 for NULL termination (and
  295.      * possibly 2 for byte order mark)
  296.      */
  297.  
  298.     uncvtlen = strlen((char *)uncvtbuf);
  299.     tobufsz = 2*(utf8bufsz + uncvtlen) + 4;  
  300.     if (!tobufsz) {
  301.         return NULL;
  302.     }
  303.  
  304.     if ((tobuf = (unsigned char *)XP_ALLOC_PRIV(tobufsz)) == (unsigned char *)NULL) {
  305.         INTL_SetCCCRetval(obj, MK_OUT_OF_MEMORY);
  306.         return(NULL);
  307.     }
  308.  
  309.     
  310.     /* Initialize pointers, etc.    */
  311.      utf8p = (unsigned char *)utf8buf;
  312.      utf8endp = utf8p + utf8bufsz - 1; /* leave room for NULL termination (as sentinel?)*/
  313.  
  314. #define uncvtp    tobufp        /* use tobufp as temp index for uncvtbuf */                  
  315.                             /* If prev. unconverted chars, append unconverted
  316.                              * chars w/new chars and try to process.
  317.                              */
  318.  
  319.      if (uncvtbuf[0] != '\0') {
  320.          uncvtp = uncvtbuf + uncvtlen;
  321.          while (uncvtp < (uncvtbuf + UNCVTBUF_SIZE) &&
  322.                                                         utf8p <= utf8endp)
  323.              *uncvtp++ = *utf8p++;
  324.  
  325.          *uncvtp = '\0';                        /* nul terminate as sentinel */
  326.          utf8p = uncvtbuf;                /* process unconverted first */
  327.          utf8endp = uncvtp - 1;
  328.  
  329.      } 
  330.  
  331. #undef uncvtp
  332.      
  333.      tobufp = tobuf;
  334.      tobufendp = tobufp + tobufsz - 3;        /* save space for terminating null */
  335.  
  336.     /* write byte order mark */
  337.  
  338.       if(!(INTL_GetCCCCvtflag(obj))) {
  339.         *((uint16 *) tobufp) = (uint16) BYTE_ORDER_MARK;
  340.         tobufp += 2;
  341.         INTL_SetCCCCvtflag(obj, TRUE);
  342.       }
  343.  
  344.  WHILELOOP:
  345.  
  346.     while( (tobufp <= tobufendp) && (utf8p <= utf8endp) ) {
  347.  
  348.  
  349.         numoctets = one_utf8_to_ucs2_char(utf8p, utf8endp, &onechar);
  350.         if(numoctets == -1) break; /* not enought utf8 data */
  351.         utf8p += numoctets;
  352.  
  353.  
  354.  
  355.         /* Check to make sure there's space to write onechar */
  356.         if((tobufp+2) >= tobufendp) break;
  357.  
  358.         *((uint16 *) tobufp) = (onechar <= MAX_UCS2 ? onechar :  DEFAULT_CHAR);
  359.             
  360.         tobufp +=2;
  361.     
  362.     }
  363.     if(uncvtbuf[0] != '\0') {            /* Just processed unconverted chars.
  364.                                              * ucsp points to 1st unprocessed char 
  365.                                              * in ucsbuf. Some may have been 
  366.                                              * processed while processing unconverted
  367.                                              * chars, so setup ptrs. not to process
  368.                                              * them twice.
  369.                                              */
  370.  
  371.                                             /* If nothing was converted, there wasn't
  372.                                              * enough UCS-2 data. Stop and get more
  373.                                              * data.
  374.                                              */
  375.  
  376.         if(utf8p == uncvtbuf) {        /* nothing was converted */
  377.             *tobufp = '\0';
  378.             return(NULL);
  379.         }
  380.         utf8endp = (unsigned char *) utf8buf + utf8bufsz - 1; 
  381.         utf8p =     (unsigned char *) utf8buf + (utf8p - uncvtbuf - uncvtlen);
  382.         uncvtbuf[0] = '\0';           /* No more unconverted chars.*/
  383.         goto WHILELOOP;                       /* Process new data */
  384.     }
  385.  
  386.      *tobufp = '\0';                /* NULL terminate dest. data */
  387.  
  388.     INTL_SetCCCLen(obj, tobufp - tobuf);        /* length of processed data, in bytes */
  389.  
  390.     if(utf8p <= utf8endp) {            /*  unconverted utf8 left? */
  391.         tobufp = uncvtbuf;        /* just using tobufp as a temp index. */
  392.         while (utf8p <= utf8endp)
  393.                 *tobufp++ = *utf8p++;
  394.         *tobufp = '\0';                /* NULL terminate, as a sentinel */
  395.     }
  396.  
  397.  
  398. #undef ucsbufsz     
  399. #undef ucsbuf         
  400. #undef ucsp         
  401. #undef ucsendp     
  402.                             
  403.  
  404.     return(tobuf);
  405. }
  406.  
  407.  
  408.  
  409. /*
  410.  * mz_utf82ucsswap
  411.  * ---------------
  412.  *
  413.  * mz_utf82ucs will convert the UTF-8 data to UCS-2 data of the same
  414.  * endian-ness of the platform the client is running on. Occasionally,
  415.  * this is not what is desired. mz_utf82ucsswap converts the UTF-8
  416.  * data to UCS-2 of the opposite endian-ness.
  417.  */
  418.  
  419.   
  420. MODULE_PRIVATE UNICVTAPI unsigned char *
  421. mz_utf82ucsswap(    CCCDataObject        obj,
  422.                 const unsigned char    *utf8buf,    /* UTF-8 buf for conv */
  423.                 int32                utf8bufsz)    /* UTF-8 buf size in bytes */
  424. {
  425.  
  426.     unsigned char *result;
  427.  
  428.     result = mz_utf82ucs(obj, utf8buf, utf8bufsz);
  429.     swap_ucs2_bytes(result, INTL_GetCCCLen(obj));
  430.     return(result);
  431.  
  432. }
  433.  
  434.  
  435. /* UTF-7 to UTF-8 conversion routines */
  436.  
  437.  
  438.  
  439.  
  440. /* mz_utf72utf8
  441.  * ------------
  442.  * 
  443.  * Takes a streams object, a buffer of UTF-7 data, and the size of
  444.  * that buffer.  Allocates, fills, and returns a buffer of UTF-8 
  445.  * data. (Its size is returned in the CCCDataObject.) The caller
  446.  * is responsible for freeing the returned buffer. 
  447.  * 
  448.  * Note: UTF-7 has the property that multiple characters of UTF-7 
  449.  * may make up a single character of UTF-8. Also, a single UTF-7 char 
  450.  * may contribute bits to more than one utf8 character. If such a
  451.  * UTF-7 character is involved at the end of the current chunk, it won't
  452.  * be save-able in uncvtbuf. For this reason, we also need to
  453.  * save the bit buffer. It turns out that we also need to save the
  454.  * fact that we are within a shifted sequence, because there is no
  455.  * other way for that information to persist between chunks of a 
  456.  * stream. If we save a buffer, then we are certainly in the middle
  457.  * of a shifted sequence, but even if there is no buffer to save, we
  458.  * may still be in a shifted sequence.
  459.  * 
  460.  * The streams module gives me one int32 - obj->cvtflag - in which
  461.  * to save my state.  This means that to save all my data, I'll need
  462.  * to do a few bit-wise operations.
  463.  *
  464.  * Arbitrarily, the top two bytes will hold the buffer, the next byte
  465.  * holds the count of relevant bits in the buffer, and the low order
  466.  * byte will hold 0 if we are not in a shiftSequence, 1 if we are.
  467.  *
  468.  * Since we will only save a buffer and bufferBitCount if we are
  469.  * in a shift sequence when this chunk terminates, obj->cvtflag == 0
  470.  * when we do not terminate in a shift sequence.
  471.  */
  472.  
  473.  
  474. /*
  475.     tables for RFC1642- UTF7
  476. */
  477.  
  478. PRIVATE    int16 rfc1642_fromb64[128] = 
  479. {
  480.     /*   0 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  481.     /*  10 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  482.     /*  20 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  483.     /*  30 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  484.     /*  40 */  -1,  -1,  -1,  62,  -1,  -1,  -1,  63,  52,  53,
  485.     /*  50 */  54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1,
  486.     /*  60 */  -1,  -1,  -1,  -1,  -1,   0,   1,   2,   3,   4,
  487.     /*  70 */   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
  488.     /*  80 */  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
  489.     /*  90 */  25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28,
  490.     /* 100 */  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
  491.     /* 110 */  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
  492.     /* 120 */  49,  50,  51,  -1,  -1,  -1,  -1,  -1
  493. };
  494. PRIVATE    unsigned char rfc1642_tob64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  495. PRIVATE    unsigned char rfc1642_shift[128] = {
  496. /*            0        1        2        3        4        5        6        7    */
  497. /*            8        9        A        B        C        D        E        F    */
  498. /* 0x00 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  499. /* 0x08 */    TRUE,    FALSE,    FALSE,    TRUE,    TRUE,    FALSE,    TRUE,    TRUE,
  500. /* 0x10 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  501. /* 0x18 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  502. /* 0x20 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  503. /* 0x28 */    FALSE,    FALSE,    FALSE,    TRUE,    FALSE,    FALSE,    FALSE,    FALSE,
  504. /* 0x30 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  505. /* 0x38 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  506. /* 0x40 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  507. /* 0x48 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  508. /* 0x50 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  509. /* 0x58 */    FALSE,    FALSE,    FALSE,    FALSE,    TRUE,    FALSE,    FALSE,    FALSE,
  510. /* 0x60 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  511. /* 0x68 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  512. /* 0x70 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  513. /* 0x78 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    TRUE,    TRUE
  514. };
  515.  
  516. PRIVATE utf7_encoding_method_data rfc1642_utf7 = {
  517.     rfc1642_fromb64,
  518.     rfc1642_tob64,
  519.     rfc1642_shift,
  520.     (unsigned char)'+',
  521.     (unsigned char)'-'
  522. };
  523.  
  524.  
  525. /*
  526.     tables for RFC2060- IMAP4rev1 Mail Box Name
  527. */
  528. PRIVATE    int16 rfc2060_fromb64[128] = 
  529. {
  530.     /*   0 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  531.     /*  10 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  532.     /*  20 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  533.     /*  30 */  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
  534.     /*  40 */  -1,  -1,  -1,  62,  63,  -1,  -1,  -1,  52,  53,
  535.     /*  50 */  54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1,
  536.     /*  60 */  -1,  -1,  -1,  -1,  -1,   0,   1,   2,   3,   4,
  537.     /*  70 */   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
  538.     /*  80 */  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
  539.     /*  90 */  25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28,
  540.     /* 100 */  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
  541.     /* 110 */  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
  542.     /* 120 */  49,  50,  51,  -1,  -1,  -1,  -1,  -1
  543. };
  544. PRIVATE    unsigned char rfc2060_tob64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
  545. PRIVATE    unsigned char rfc2060_shift[128] = {
  546. /*            0        1        2        3        4        5        6        7    */
  547. /*            8        9        A        B        C        D        E        F    */
  548. /* 0x00 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  549. /* 0x08 */    TRUE,    FALSE,    FALSE,    TRUE,    TRUE,    FALSE,    TRUE,    TRUE,
  550. /* 0x10 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  551. /* 0x18 */    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,    TRUE,
  552. /* 0x20 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    TRUE,    FALSE,
  553. /* 0x28 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  554. /* 0x30 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  555. /* 0x38 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  556. /* 0x40 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  557. /* 0x48 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  558. /* 0x50 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  559. /* 0x58 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  560. /* 0x60 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  561. /* 0x68 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  562. /* 0x70 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,
  563. /* 0x78 */    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    FALSE,    TRUE
  564. };
  565.  
  566. PRIVATE utf7_encoding_method_data rfc2060_utf7 = {
  567.     rfc2060_fromb64,
  568.     rfc2060_tob64,
  569.     rfc2060_shift,
  570.     (unsigned char)'&',
  571.     (unsigned char)'-'
  572. };
  573.  
  574. MODULE_PRIVATE UNICVTAPI unsigned char *
  575. mz_utf72utf8(    CCCDataObject        obj,
  576.                 const unsigned char    *utf7buf,    /* UTF-7 buf for conv */
  577.                 int32                utf7bufsz)    /* UTF-7 buf size in bytes */
  578. {
  579.     return intl_utf72utf8(obj,utf7buf, utf7bufsz, &rfc1642_utf7);
  580. }
  581. MODULE_PRIVATE UNICVTAPI unsigned char *
  582. mz_imap4utf72utf8(    CCCDataObject        obj,
  583.                 const unsigned char    *utf7buf,    /* UTF-7 buf for conv */
  584.                 int32                utf7bufsz)    /* UTF-7 buf size in bytes */
  585. {
  586.     return intl_utf72utf8(obj,utf7buf, utf7bufsz, &rfc2060_utf7);
  587. }
  588.  
  589. PRIVATE unsigned char *
  590. intl_utf72utf8(    CCCDataObject        obj,
  591.                 const unsigned char    *utf7buf,    /* UTF-7 buf for conv */
  592.                 int32                utf7bufsz,    /* UTF-7 buf size in bytes */
  593.                 utf7_encoding_method_data*    opt)
  594.  
  595. {
  596.  
  597.      unsigned char    *tobuf = NULL;
  598.      int32            tobufsz;
  599.      unsigned char    *tobufp, *utf7p;        /* current byte in bufs    */
  600.      unsigned char    *tobufendp, *utf7endp;    /* end of buffers        */
  601.      int32                    uncvtlen;
  602.     
  603.     uint16    oneUCS2char;
  604.     unsigned char    onechar;
  605.     int16            numoctets;
  606.     int16 mustnotshift = 0;
  607.     int16 inShiftSequence;
  608.  
  609.     uint32 buffer;
  610.     uint32 buffertemp = 0;
  611.     int16 bufferBitCount;
  612.     unsigned char *uncvtbuf = INTL_GetCCCUncvtbuf(obj);
  613.  
  614.     /* set up table to convert ASCII values of base64 chars to
  615.      * their base 64 value. If there is no conversion, use -1 as sentinel.
  616.      */
  617.  
  618.  
  619.     /* initialize data saved from previous stream */
  620.  
  621.     int32 flag = INTL_GetCCCCvtflag(obj);
  622.     inShiftSequence = flag & 1;
  623.     buffer = 0xFFFF0000 & flag;
  624.     bufferBitCount = (uint16) ((0x0000FF00 & flag) >> 8);
  625.  
  626. #define utf8bufsz    tobufsz
  627. #define utf8buf        tobuf
  628. #define utf8p        tobufp
  629. #define utf8endp    tobufendp
  630.                                                   /* Allocate a dest buffer:        */
  631.  
  632.  
  633.     /* UTF-7 characters that are directly encoded will be one octet UTF-8
  634.      * chars. Shifted chars will take 2.7 octets (plus shift in or out chars)
  635.      * to make 2 or 3 octet UTF-8 chars. So in the worst input, all the UTF-7
  636.      * data would convert to 3 octet UTF-8 data, and we would need 1/9th as
  637.      * many UTF-7 characters, plus 1 to round up, plus 1 for NULL termination.
  638.      */
  639.  
  640.     uncvtlen = strlen((char *)uncvtbuf);
  641.     tobufsz = (int32) (1.2*(utf7bufsz + uncvtlen) + 2); 
  642.  
  643.     if ((tobuf = (unsigned char *)XP_ALLOC_PRIV(tobufsz)) == (unsigned char *)NULL) 
  644.     {
  645.         INTL_SetCCCRetval(obj, MK_OUT_OF_MEMORY);
  646.         return(NULL);
  647.     }
  648.                                         /* Initialize pointers, etc.    */
  649.      utf7p = (unsigned char *)utf7buf;
  650.      utf7endp = utf7p + utf7bufsz - 1;
  651.  
  652. #define uncvtp    tobufp        /* use tobufp as temp index for uncvtbuf */                  
  653.                             /* If prev. unconverted chars, append unconverted
  654.                              * chars w/new chars and try to process.
  655.                              */
  656.  
  657.      if (uncvtbuf[0] != '\0') 
  658.     {
  659.          uncvtp = uncvtbuf + uncvtlen;
  660.          while (uncvtp < (uncvtbuf + UNCVTBUF_SIZE) &&
  661.                                                         utf7p <= utf7endp)
  662.              *uncvtp++ = *utf7p++; 
  663.  
  664.  
  665.          *uncvtp = '\0';                        /* nul terminate as sentinel */
  666.          utf7p = uncvtbuf;                /* process unconverted first */
  667.          utf7endp = uncvtp - 1;
  668.      }
  669. #undef uncvtp
  670.      
  671.      tobufp = tobuf;
  672.      tobufendp = tobufp + tobufsz - 2;        
  673.  
  674. WHILELOOP:
  675.  
  676.     while( (tobufp <= tobufendp) && (utf7p <= utf7endp) ) 
  677.     {
  678.  
  679.      
  680.         onechar = *utf7p++;
  681.         
  682.         
  683.         /* If I'm not in the shift sequence, and I have the start symbol,
  684.          * absorb it and loop again. Otherwise, if I have a legal character
  685.          * for a non-shifted sequence, (ASCII) write it directly. This is
  686.          * ok, because ASCII is just ASCII in UTF-8, so don't need to worry
  687.          * about UCS-2 conversion.
  688.          */
  689.  
  690.         if(!inShiftSequence) 
  691.         {
  692.  
  693.             if(onechar == opt->startshift) 
  694.             {
  695.                 if(*utf7p == opt->endshift) 
  696.                 {
  697.                     *tobufp++ = opt->startshift;
  698.                     utf7p++;
  699.                 } else inShiftSequence = TRUE;
  700.                 continue;
  701.             }
  702.                 
  703.             if(onechar <= MAX_ASCII) *tobufp++ = onechar;
  704.             else continue;
  705.             
  706.         } 
  707.         else 
  708.         {    /* inShiftSequence is TRUE */
  709.  
  710.             /* onechar is not a base64 allowable char if it is non-ASCII or
  711.              * if it is a non-base64 char from the ASCII set. 
  712.              */
  713.             mustnotshift = (onechar > MAX_ASCII || 
  714.                     (opt->fromb64[onechar] == NOT_BASE64));
  715.  
  716.             /* If I'm in the shift sequence, and get the opt->endshift character,
  717.              * I want to absorb it and turn off shifting. If I get another
  718.              * non-shiftable character, I want to write it and turn off shifting.
  719.              * If I get an illegal character, I discard it and keep looping. 
  720.              */
  721.  
  722.             if(mustnotshift) 
  723.             {
  724.  
  725.                 if(!(onechar == opt->endshift)) 
  726.                 {
  727.  
  728.                     if(onechar > MAX_ASCII) 
  729.                         continue;
  730.                     
  731.                     *tobufp++ = onechar;
  732.                 }
  733.  
  734.                 inShiftSequence = FALSE;
  735.                 buffer = 0;            /* flush buffer at end of shift sequence */
  736.                 bufferBitCount = 0;
  737.                 
  738.             
  739.             } 
  740.             else 
  741.             { 
  742.  
  743.                 buffertemp = opt->fromb64[onechar] & 0x0000003F;    /* grab 6-bit base64 char */
  744.                 buffer |= buffertemp << (26 - bufferBitCount); /* 26 is 32 - 6 bits */
  745.                 bufferBitCount += 6;
  746.  
  747.                 /* Flush the buffer of a UCS-2 character (won't be more than one)  */
  748.                 
  749.                 if(bufferBitCount > 15) 
  750.                 {
  751.  
  752.                     oneUCS2char = (int16) ((buffer & 0xFFFF0000) >> 16);
  753.                     numoctets = one_ucs2_to_utf8_char(tobufp, tobufendp, oneUCS2char);
  754.                     if(numoctets == -1) break; /* out of space in tobuf */
  755.                     tobufp += numoctets;
  756.                     bufferBitCount -= 16;
  757.                     buffer <<= 16;
  758.                 }
  759.  
  760.             }
  761.  
  762.         } /* end of inShiftSequence == TRUE */
  763.  
  764.     }       /* end of conversion while loop */
  765.  
  766.  
  767.  
  768.     if(uncvtbuf[0] != '\0') 
  769.     {                                        /* Just processed unconverted chars.
  770.                                              * ucsp points to 1st unprocessed char 
  771.                                              * in ucsbuf. Some may have been 
  772.                                              * processed while processing unconverted
  773.                                              * chars, so setup ptrs. not to process
  774.                                              * them twice.
  775.                                              */
  776.  
  777.                                             /* If nothing was converted, there wasn't
  778.                                              * enough UCS-2 data. Stop and get more
  779.                                              * data.
  780.                                              */
  781.  
  782.         if(utf7p == uncvtbuf) 
  783.         {    /* nothing was converted */
  784.             *tobufp = '\0';
  785.             INTL_SetCCCLen(obj, 0);
  786.             return(NULL);
  787.         }
  788.  
  789.         /* set up to read ucsbuf */
  790.         utf7endp = (unsigned char *) utf7buf + utf7bufsz - 1; 
  791.         utf7p =     (unsigned char *) utf7buf + (utf7p - uncvtbuf - uncvtlen);
  792.         uncvtbuf[0] = '\0';           /* No more unconverted chars.*/
  793.         goto WHILELOOP;                       /* Process new data */
  794.     }
  795.  
  796.  
  797.     *tobufp = '\0';                    /* NULL terminate dest. data */
  798.     INTL_SetCCCLen(obj, tobufp - tobuf);        /* length of processed data, in bytes */
  799.  
  800.     /* If we're in a shift sequence, we need to save away our buffer
  801.      * and the buffer bit count (although if all that's left in the buffer
  802.      * is padding 0's, we don't need to worry about it and should reset
  803.      * the bitCount to 0.)
  804.      */
  805.     
  806.     INTL_SetCCCCvtflag(obj,((inShiftSequence ? 1 : 0 ) |
  807.                             (buffer & 0xFFFF0000) |
  808.                             ((bufferBitCount << 8) & 0x0000FF00)));
  809.     
  810.     /* Now check for unconverted data from utf7p */
  811.     if(utf7p <= utf7endp) 
  812.     {        
  813.         int l = utf7endp - utf7p + 1;
  814.         memcpy(uncvtbuf, utf7p, l);
  815.         uncvtbuf[l] = '\0';
  816.     }
  817.  
  818. #undef utf8bufsz
  819. #undef utf8buf        
  820. #undef utf8p        
  821. #undef utf8endp    
  822.  
  823.     return(tobuf);
  824.  
  825. }
  826.  
  827.  
  828. /* UTF-8 to UTF-7 */
  829.  
  830.  
  831.  /*
  832.   * mz_utf82utf7
  833.   * ------------
  834.   *
  835.   * This function takes a CCCDataObject, a buffer of UTF-8 data, and the
  836.   * size of that buffer. It allocates and returns a buffer of the
  837.   * corresponding UTF-7 data (returning the size as a field in the
  838.   * CCCDataObject). The caller is responsible for freeing the returned
  839.   * data. If there are extra data at the end of the UTF-8 buffer which 
  840.   * cannot be translated into UTF-7 (ie, an incomplete character), it
  841.   * will be saved in the uncvtbuf of the CCCDataObject and used on the
  842.   * next call. 
  843.   *
  844.   * UTF-7 is a variant of base-64, and like base-64, it accumulates
  845.   * bits in a bit buffer, transforming them to UTF-7 chars when it
  846.   * has multiples of 6 bits. If the UTF-8 data being translated does
  847.   * not happen to terminate with a multiple of 6 bits, the final 
  848.   * char will be padded with 0's, and the shift sequence terminated.
  849.   * For this reason, we will *never* be inside a shift sequence in
  850.   * between chunks of data. This may mean that the final stream of
  851.   * data has sequences that look like +[some UTF-7 data]-+[more data]-,
  852.   * with a plus immediately following a -. Although unconventional,
  853.   * this is in fact legal UTF-7. 
  854.   *
  855.   * Finally, there are two formats of UTF-7, one extremely conservative
  856.   * fashion which shifts every character which could possibly be
  857.   * considered unsafe, and another which is somewhat more lax. Which
  858.   * of these is used is determined by obj->cvtflag. By default (cvtflag == 0)
  859.   * we employ the safer form of conversion. The differing characters
  860.   * are: !\"#$%&*;<=>@[]^_`{|}
  861.   */ 
  862. /* Tables */   
  863.  
  864.  
  865. MODULE_PRIVATE UNICVTAPI unsigned char *
  866. mz_utf82utf7(    CCCDataObject        obj,
  867.                 const unsigned char    *utf8buf,    /* UTF-8 buf for conv */
  868.                 int32                utf8bufsz)    /* UTF-8 buf size in bytes */
  869. {
  870.     return intl_utf82utf7(obj,utf8buf, utf8bufsz, &rfc1642_utf7);
  871. }
  872. MODULE_PRIVATE UNICVTAPI unsigned char *
  873. mz_utf82imap4utf7(    CCCDataObject        obj,
  874.                 const unsigned char    *utf8buf,    /* UTF-8 buf for conv */
  875.                 int32                utf8bufsz)    /* UTF-8 buf size in bytes */
  876. {
  877.     return intl_utf82utf7(obj,utf8buf, utf8bufsz, &rfc2060_utf7);
  878. }
  879. PRIVATE unsigned char *
  880. intl_utf82utf7(    CCCDataObject        obj,
  881.                 const unsigned char    *utf8buf,    /* UTF-8 buf for conv */
  882.                 int32                utf8bufsz,    /* UTF-8 buf size in bytes */
  883.                 utf7_encoding_method_data*    opt)
  884. {                                                                                    
  885.  
  886.  
  887.      unsigned char    *tobuf = NULL;
  888.     int32            tobufsz;
  889.     unsigned char    *tobufp, *utf8p;        /* current byte in bufs    */
  890.      unsigned char    *tobufendp, *utf8endp;    /* end of buffers        */
  891.      int32                    uncvtlen;
  892.     unsigned char *uncvtbuf = INTL_GetCCCUncvtbuf(obj);
  893.  
  894.  
  895.     uint16 onechar;
  896.     int16 numoctets;
  897.     int16 inShiftSequence = FALSE;
  898.     int16 needToShift = FALSE;
  899.     uint32 buffer = 0;
  900.     uint32 buffertemp = 0;
  901.     int16 bufferBitCount = 0;
  902.     unsigned char oneBase64char;
  903.  
  904.  
  905.  
  906. #define utf7bufsz    tobufsz
  907. #define utf7buf        tobuf
  908. #define utf7p        tobufp
  909. #define utf7endp    tobufendp
  910.  
  911.     
  912.     /* Allocate a dest buffer:        */
  913.  
  914.     uncvtlen = strlen((char *)uncvtbuf);
  915.     tobufsz = 3*(utf8bufsz + uncvtlen) +1;  
  916.     if (!tobufsz) {
  917.         return NULL;
  918.     }
  919.  
  920.     if ((tobuf = (unsigned char *)XP_ALLOC_PRIV(tobufsz)) == (unsigned char *)NULL) {
  921.         INTL_SetCCCRetval(obj, MK_OUT_OF_MEMORY);
  922.         return(NULL);
  923.     }
  924.                                         /* Initialize pointers, etc.    */
  925.      utf8p = (unsigned char *)utf8buf;
  926.      utf8endp = utf8p + utf8bufsz - 1; /* leave room for NULL termination (as sentinel?)*/
  927.  
  928. #define uncvtp    tobufp        /* use tobufp as temp index for uncvtbuf */                  
  929.                             /* If prev. unconverted chars, append unconverted
  930.                              * chars w/new chars and try to process.
  931.                              */
  932.  
  933.      if (uncvtbuf[0] != '\0') {
  934.          uncvtp = uncvtbuf + uncvtlen;
  935.         /* This is not leaving space for a NULL !!!!!!!!!!!! */
  936.          while (uncvtp < (uncvtbuf + UNCVTBUF_SIZE) &&
  937.                                                         utf8p <= utf8endp)
  938.              *uncvtp++ = *utf8p++;
  939.  
  940.          *uncvtp = '\0';                        /* nul terminate as sentinel */
  941.          utf8p = uncvtbuf;                /* process unconverted first */
  942.          utf8endp = uncvtp - 1;
  943.      }
  944. #undef uncvtp
  945.  
  946.  
  947.      tobufp = tobuf;
  948.      tobufendp = tobufp + tobufsz - 2;        /* save space for terminating null*/
  949.  
  950.         
  951.  
  952.  
  953.  WHILELOOP:
  954.  
  955.     while( (tobufp <= tobufendp) && (utf8p <= utf8endp) ) {
  956.  
  957.         /* convert one char's worth of utf8 to ucs2 */
  958.         numoctets = one_utf8_to_ucs2_char(utf8p, utf8endp, &onechar);
  959.         if(numoctets == -1) break; /* out of input*/
  960.         utf8p += numoctets;
  961.  
  962.         /* we need to be shifted if the character is non-ASCII or 
  963.          * is an ASCII character that should be shifted.
  964.          */
  965.         needToShift = (onechar > MAX_ASCII) || (opt->shift[onechar]);
  966.  
  967.  
  968.         if(!needToShift && inShiftSequence)      {
  969.  
  970.             if(bufferBitCount > 0) {
  971.                 if((tobufp+2) > tobufendp) break;
  972.                 bufferBitCount = pad_and_write(buffer, tobufp, bufferBitCount, opt);
  973.                 if (!bufferBitCount) {    /* buffer successfully flushed */
  974.                     tobufp+=2;
  975.                     buffer = 0;
  976.                 }
  977.             
  978.             } else {
  979.                 if((tobufp+1) > tobufendp) break;
  980.                 *tobufp++ = opt->endshift;
  981.             }
  982.             inShiftSequence = FALSE; /* now just fallthrough to next case*/
  983.         }
  984.  
  985.         if(!needToShift &&     !inShiftSequence) {
  986.             if((tobufp+1) > tobufendp) break;
  987.             *tobufp++ = (char) onechar;
  988.         } 
  989.  
  990.         if(needToShift && !inShiftSequence)     {
  991.             *tobufp++ = opt->startshift;
  992.             if(onechar == opt->startshift) { /* special-case behavior if onechar is a + */
  993.                 if((tobufp+1) > tobufendp) break;
  994.                 *tobufp++ = opt->endshift;
  995.             }
  996.             else inShiftSequence = TRUE; 
  997.         }
  998.  
  999.         if(needToShift && inShiftSequence) {
  1000.             
  1001.             buffertemp = onechar & 0x0000FFFF;
  1002.             buffer |= buffertemp << (16 - bufferBitCount);    
  1003.                                             /* ^--16 is the size of the int32 minus
  1004.                                              * the size of onechar */
  1005.             bufferBitCount += 16;
  1006.  
  1007.  
  1008.             /* Flush the buffer of as many base64 characters as we can form */
  1009.             while(bufferBitCount>5) {
  1010.                    if(tobufp > tobufendp) break;
  1011.                   oneBase64char = (char)  ((buffer & 0xFC000000) >> 26);
  1012.                   *tobufp++ =  opt->tob64[oneBase64char];
  1013.                   buffer <<= 6;
  1014.                   bufferBitCount -= 6;
  1015.             }
  1016.         }
  1017.  
  1018.  
  1019.     } /* end of while loop */
  1020.  
  1021.  
  1022.  
  1023.     if(uncvtbuf[0] != '\0') {            /* Just processed unconverted chars.
  1024.                                              * ucsp points to 1st unprocessed char 
  1025.                                              * in ucsbuf. Some may have been 
  1026.                                              * processed while processing unconverted
  1027.                                              * chars, so setup ptrs. not to process
  1028.                                              * them twice.
  1029.                                              */
  1030.  
  1031.                                             /* If nothing was converted, there wasn't
  1032.                                              * enough UTF-8 data. Stop and get more
  1033.                                              * data.
  1034.                                              */
  1035.  
  1036.         if(utf8p == uncvtbuf) {        /* nothing was converted */
  1037.             *tobufp = '\0';
  1038.             return(NULL);
  1039.         }
  1040.         utf8endp = (unsigned char *) utf8buf + utf8bufsz - 1; 
  1041.         utf8p =     (unsigned char *) utf8buf + (utf8p - uncvtbuf - uncvtlen);
  1042.         uncvtbuf[0] = '\0';           /* No more unconverted chars.*/
  1043.         goto WHILELOOP;                       /* Process new data */
  1044.     }
  1045.  
  1046.  
  1047.     /* Anything left in the buffer at this point should be padded with 0's
  1048.      * and appended to tobuf. */
  1049.  
  1050.     if(inShiftSequence) {
  1051.  
  1052.         if(bufferBitCount > 0) {
  1053.  
  1054.             if((tobufp+2) <= tobufendp) {
  1055.                 bufferBitCount = pad_and_write(buffer, tobufp, bufferBitCount,  opt);
  1056.                 if (!bufferBitCount) { /* buffer successfully flushed */
  1057.                     tobufp+=2;
  1058.                     buffer = 0;
  1059.                 }
  1060.             }
  1061.  
  1062.         }  else {
  1063.              if((tobufp+1) <= tobufendp) *tobufp++ = opt->endshift;
  1064.         }
  1065.  
  1066.         inShiftSequence = FALSE;
  1067.     }
  1068.  
  1069.  
  1070.      *tobufp = '\0';                /* NULL terminate dest. data */
  1071.  
  1072.  
  1073.     INTL_SetCCCLen(obj, tobufp - tobuf);        /* length of processed data, in bytes */
  1074.  
  1075.     if(utf8p <= utf8endp) {            /*  unconverted utf8 left? */
  1076.         tobufp = uncvtbuf;        /* just using tobufp as a temp index. */
  1077.         while (utf8p <= utf8endp)
  1078.                 *tobufp++ = *utf8p++;
  1079.         *tobufp = '\0';                /* NULL terminate, as a sentinel if nothing else.*/
  1080.     }
  1081.  
  1082.  
  1083. #undef utf7bufsz     
  1084. #undef utf7buf         
  1085. #undef utf7p         
  1086. #undef utf7endp     
  1087.                             
  1088.  
  1089.     return(tobuf);
  1090. }
  1091.  
  1092.  
  1093. /* Function: one_ucs2_to_utf8_char
  1094.  * 
  1095.  * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
  1096.  * We need a UTF-8 buffer because we don't know before this 
  1097.  * function how many bytes of utf-8 data will be written. It also
  1098.  * takes a pointer to the end of the UTF-8 buffer so that we don't
  1099.  * overwrite data. This function returns the number of UTF-8 bytes
  1100.  * of data written, or -1 if the buffer would have been overrun.
  1101.  */
  1102.  
  1103. #define LINE_SEPARATOR        0x2028
  1104. #define PARAGRAPH_SEPARATOR    0x2029
  1105. PRIVATE int16 one_ucs2_to_utf8_char(unsigned char *tobufp, 
  1106.         unsigned char *tobufendp, uint16 onechar)
  1107.  
  1108. {
  1109.  
  1110.      int16 numUTF8bytes = 0;
  1111.  
  1112.     if((onechar == LINE_SEPARATOR)||(onechar == PARAGRAPH_SEPARATOR))
  1113.     {
  1114.         strcpy((char*)tobufp, "\n");
  1115.         return strlen((char*)tobufp);;
  1116.     }
  1117.  
  1118.          if (onechar < 0x80) {                numUTF8bytes = 1;
  1119.         } else if (onechar < 0x800) {        numUTF8bytes = 2;
  1120.         } else if (onechar <= MAX_UCS2) {    numUTF8bytes = 3;
  1121.         } else { numUTF8bytes = 2;
  1122.                  onechar = DEFAULT_CHAR;
  1123.         } 
  1124.                 
  1125.         tobufp += numUTF8bytes;
  1126.  
  1127.         /* return error if we don't have space for the whole character */
  1128.         if (tobufp > tobufendp) {
  1129.             return(-1); 
  1130.         }
  1131.  
  1132.  
  1133.         switch(numUTF8bytes) {
  1134.  
  1135.             case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  1136.                     *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  1137.                     *--tobufp = onechar |  THREE_OCTET_BASE;
  1138.                     break;
  1139.  
  1140.             case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
  1141.                     *--tobufp = onechar | TWO_OCTET_BASE;
  1142.                     break;
  1143.             case 1: *--tobufp = (unsigned char)onechar;  break;
  1144.         }
  1145.  
  1146.         return(numUTF8bytes);
  1147. }
  1148.  
  1149.  
  1150. /*
  1151.  * utf8_to_ucs2_char
  1152.  *
  1153.  * Convert a utf8 multibyte character to ucs2
  1154.  *
  1155.  * inputs: pointer to utf8 character(s)
  1156.  *         length of utf8 buffer ("read" length limit)
  1157.  *         pointer to return ucs2 character
  1158.  *
  1159.  * outputs: number of bytes in the utf8 character
  1160.  *          -1 if not a valid utf8 character sequence
  1161.  *          -2 if the buffer is too short
  1162.  */
  1163. MODULE_PRIVATE UNICVTAPI int16
  1164. utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
  1165. {
  1166.     uint16 lead, cont1, cont2;
  1167.  
  1168.     /*
  1169.      * Check for minimum buffer length
  1170.      */
  1171.     if ((buflen < 1) || (utf8p == NULL)) {
  1172.         return -2;
  1173.     }
  1174.     lead = (uint16) (*utf8p);
  1175.  
  1176.     /*
  1177.      * Check for a one octet sequence
  1178.      */
  1179.     if (IS_UTF8_1ST_OF_1(lead)) {
  1180.         *ucs2p = lead & ONE_OCTET_MASK;
  1181.         return 1;
  1182.     }
  1183.  
  1184.     /*
  1185.      * Check for a two octet sequence
  1186.      */
  1187.     if (IS_UTF8_1ST_OF_2(*utf8p)) {
  1188.         if (buflen < 2)
  1189.             return -2;
  1190.         cont1 = (uint16) *(utf8p+1);
  1191.         if (!IS_UTF8_2ND_THRU_6TH(cont1))
  1192.             return -1;
  1193.         *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
  1194.         *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
  1195.         return 2;
  1196.     }
  1197.  
  1198.     /*
  1199.      * Check for a three octet sequence
  1200.      */
  1201.     else if (IS_UTF8_1ST_OF_3(lead)) {
  1202.         if (buflen < 3)
  1203.             return -2;
  1204.         cont1 = (uint16) *(utf8p+1);
  1205.         cont2 = (uint16) *(utf8p+2);
  1206.         if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
  1207.             || (!IS_UTF8_2ND_THRU_6TH(cont2)))
  1208.             return -1;
  1209.         *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
  1210.         *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
  1211.         *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
  1212.         return 3;
  1213.     }
  1214.     else { /* not a valid utf8/ucs2 character */
  1215.         return -1;
  1216.     }
  1217. }
  1218.  
  1219. UNICVTAPI int32
  1220. INTL_NumUTF8Chars(const unsigned char *utf8p)
  1221. {
  1222.     int num_chars = 0;
  1223.  
  1224.     while (*utf8p) {
  1225.         /*
  1226.          * Check for a one octet sequence
  1227.          */
  1228.         if (IS_UTF8_1ST_OF_1(*utf8p)) {
  1229.             num_chars += 1;
  1230.             utf8p += 1;
  1231.             continue;
  1232.         }
  1233.  
  1234.         /*
  1235.          * Check for a two octet sequence
  1236.          */
  1237.         else if (IS_UTF8_1ST_OF_2(*utf8p) 
  1238.             && IS_UTF8_2ND_THRU_6TH(*(utf8p+1))) {
  1239.             num_chars += 2;
  1240.             utf8p += 2;
  1241.             continue;
  1242.         }
  1243.  
  1244.         /*
  1245.          * Check for a three octet sequence
  1246.          */
  1247.         else if (IS_UTF8_1ST_OF_3(*utf8p) 
  1248.             && IS_UTF8_2ND_THRU_6TH(*(utf8p+1))
  1249.             && IS_UTF8_2ND_THRU_6TH(*(utf8p+2))) {
  1250.             num_chars += 3;
  1251.             utf8p += 3;
  1252.             continue;
  1253.         }
  1254.  
  1255.         /*
  1256.          * Not UTF8 : just muddle forward
  1257.          */
  1258.         else {
  1259.             num_chars += 1;
  1260.             utf8p += 1;
  1261.         }
  1262.  
  1263.     }
  1264.  
  1265.     return num_chars;
  1266. }
  1267.  
  1268. PUBLIC UNICVTAPI uint16 *
  1269. INTL_UTF8ToUCS2(const unsigned char *utf8p, int32 *num_chars)
  1270. {
  1271.     uint16 *ucs2_chars;
  1272.     int32 num_utf8_chars, ucs2_len, num_ucs2_chars;
  1273.     int parse_cnt, inval_cnt;
  1274.  
  1275.     /*
  1276.      * Figure the number of chars
  1277.      */
  1278.     num_utf8_chars = INTL_NumUTF8Chars(utf8p);
  1279.     ucs2_len = num_utf8_chars*2;
  1280.     ucs2_chars = (uint16 *)XP_ALLOC_PRIV(ucs2_len + 2);
  1281.     if (!ucs2_chars) return NULL;
  1282.     /*
  1283.  
  1284.      * Do the conversion
  1285.      */
  1286.     num_ucs2_chars = utf8_to_ucs2_buffer(utf8p, strlen((char*)utf8p), 
  1287.                                 &parse_cnt, &inval_cnt, ucs2_chars,  ucs2_len);
  1288.     ucs2_chars[num_ucs2_chars] = 0; /* null terminator */
  1289.  
  1290.     /*
  1291.      * return the result
  1292.      */
  1293.     if (num_ucs2_chars > 0)
  1294.         *num_chars = num_ucs2_chars;
  1295.     else
  1296.         *num_chars = 0;
  1297.     return ucs2_chars;
  1298. }
  1299.  
  1300. PUBLIC UNICVTAPI unsigned char *
  1301. INTL_UCS2ToUTF8(const uint16 *ucs2p, int32 num_chars)
  1302. {
  1303.     unsigned char *utf8_chars;
  1304.     int32 num_utf8_bytes, num_bytes_written, dummy;
  1305.     int i;
  1306.  
  1307.     /*
  1308.      * Figure the number of bytes for the utf8 string
  1309.      */
  1310.     num_utf8_bytes =0;
  1311.     for (i=0; i<num_chars; i++) {
  1312.         if (ucs2p[i] <= 0x7F) /* 0-0x7f only need one byte */
  1313.             num_utf8_bytes += 1;
  1314.         else if (ucs2p[i] <= 0x3FF) /* 0x80-0x3ff only need two bytes */
  1315.             num_utf8_bytes += 2;
  1316.         else /* 0x400-0xffff need three bytes */
  1317.             num_utf8_bytes += 3;
  1318.     }
  1319.     utf8_chars = (unsigned char *)XP_ALLOC_PRIV(num_utf8_bytes + 1);
  1320.     if (!utf8_chars) return NULL;
  1321.     XP_MEMSET(utf8_chars, 0, num_utf8_bytes + 1);
  1322.  
  1323.     /*
  1324.      * Do the conversion
  1325.      */
  1326.     num_bytes_written = ucs2_to_utf8_buffer(ucs2p, num_chars, utf8_chars,  
  1327.                                             num_utf8_bytes, &dummy);
  1328.     /*
  1329.      * return the result
  1330.      */
  1331.     return utf8_chars;
  1332. }
  1333.  
  1334. /*
  1335.  * ucs2_to_utf8_buffer
  1336.  *
  1337.  * Convert a ucs2 buffer to a utf8 multibyte character string
  1338.  *
  1339.  * inputs: 
  1340.  *         pointer to return ucs2 buffer
  1341.  *         length of ucs2 buffer ("read" length limit)
  1342.  *         pointer to utf8 character(s)
  1343.  *         length of utf8 buffer ("write" length limit)
  1344.  *
  1345.  * outputs: returns number of charecters "read" from the ucs2 string
  1346.  *          sets *num_bytes_written to # of utf8 characters "written"
  1347.  */
  1348. int32
  1349. ucs2_to_utf8_buffer(const uint16 *ucs2p, int32 num_chars, 
  1350.         unsigned char *utf8p, int32 num_utf8_bytes, int32 *utf8_bytes_written)
  1351. {
  1352.     int i;
  1353.  
  1354.     /*
  1355.      * Init values
  1356.      */
  1357.     *utf8_bytes_written = 0;
  1358.  
  1359.  
  1360.     /*
  1361.      * Convert the data
  1362.      */
  1363.     for (i=0; i<num_chars; i++) {
  1364.         if (ucs2p[i] <= 0x7F) { /* 0-0x7f only need one byte */
  1365.             if (num_utf8_bytes < 1)
  1366.                 break;
  1367.             utf8p[*utf8_bytes_written] = (unsigned char)ucs2p[i];
  1368.             num_utf8_bytes -= 1;
  1369.             *utf8_bytes_written += 1;
  1370.         }
  1371.         else if (ucs2p[i] <= 0x3FF) { /* 0x80-0x3ff only need two bytes */
  1372.             if (num_utf8_bytes < 2)
  1373.                 break;
  1374.             utf8p[*utf8_bytes_written+0] = (unsigned char)
  1375.                     (TWO_OCTET_BASE | ((ucs2p[i]>>6)&TWO_OCTET_MASK));
  1376.             utf8p[*utf8_bytes_written+1] = (unsigned char)
  1377.                     (CONTINUING_OCTET_BASE | (ucs2p[i]&CONTINUING_OCTET_MASK));
  1378.             num_utf8_bytes -= 2;
  1379.             *utf8_bytes_written += 2;
  1380.         }
  1381.         else { /* 0x400-0xffff need three bytes */
  1382.             if (num_utf8_bytes < 3)
  1383.                 break;
  1384.             utf8p[*utf8_bytes_written+0] = (unsigned char)
  1385.                     (THREE_OCTET_BASE | ((ucs2p[i]>>12)&THREE_OCTET_MASK));
  1386.             utf8p[*utf8_bytes_written+1] = (unsigned char)
  1387.                 (CONTINUING_OCTET_BASE | ((ucs2p[i]>>6)&CONTINUING_OCTET_MASK));
  1388.             utf8p[*utf8_bytes_written+2] = (unsigned char)
  1389.                     (CONTINUING_OCTET_BASE | (ucs2p[i]&CONTINUING_OCTET_MASK));
  1390.             num_utf8_bytes -= 3;
  1391.             *utf8_bytes_written += 3;
  1392.         }
  1393.     }
  1394.  
  1395.     return i;
  1396. }
  1397.  
  1398. /*
  1399.  * utf8_to_ucs2_buffer
  1400.  *
  1401.  * Convert a utf8 multibyte character string and place in a ucs2 buffer
  1402.  *
  1403.  * inputs: pointer to utf8 character(s)
  1404.  *         length of utf8 buffer ("read" length limit)
  1405.  *         pointer to return ucs2 buffer
  1406.  *         length of ucs2 buffer ("write" length limit)
  1407.  *         pointer to return count of invalid bytes
  1408.  *
  1409.  * outputs: returns number of bytes "read" from the utf8 string
  1410.  *          sets *invalid_cnt to # of invalid utf8 characters "read"
  1411.  */
  1412. UNICVTAPI int32
  1413. utf8_to_ucs2_buffer(const unsigned char *utf8p, int16 utf8len, 
  1414.                         int *parsed_cnt, int *invalid_cnt,
  1415.                         uint16 *ucs2p, int32 ucs2len)
  1416. {
  1417.     int read_len, write_len;
  1418.     int char_len;
  1419.  
  1420.     /*
  1421.      * Init the return values
  1422.      */
  1423.     *parsed_cnt = 0;
  1424.     *invalid_cnt = 0;
  1425.  
  1426.     /*
  1427.      * Check for minimum buffer lengths
  1428.      */
  1429.     if ((utf8len < 1) || (utf8p == NULL)
  1430.         || (ucs2len < 1) || (ucs2p == NULL)) {
  1431.         return 0;
  1432.     }
  1433.  
  1434.     /*
  1435.      * Do the conversion
  1436.      */
  1437.     for (read_len=0,write_len=0;
  1438.                     (read_len<utf8len) && (write_len<ucs2len);
  1439.                                 read_len +=char_len)
  1440.         {
  1441.         char_len = utf8_to_ucs2_char(utf8p+read_len, utf8len-read_len, 
  1442.                                                         (uint16*)ucs2p+write_len);
  1443.         if (char_len == -1) { /* invalid character */
  1444.             *invalid_cnt += 1;
  1445.             char_len = 1; /* try to resynchronize */
  1446.             *(ucs2p+write_len) = *(utf8p+read_len);
  1447.         }
  1448.         else if (char_len == -2) { /* buffer too short for last char */
  1449.             /* return with what we have so far */
  1450.             break;
  1451.         }
  1452.         /*
  1453.          * Note we converted one
  1454.          */
  1455.         *parsed_cnt += char_len;
  1456.         write_len += 1;
  1457.     }
  1458.     return write_len;
  1459. }
  1460.  
  1461. /* Function:  one_utf8_to_ucs2_char
  1462.  * 
  1463.  * Converts one UTF8 char to one UCS2 char. Needs to get UTF-8 from a
  1464.  * buffer of utf8 data, because we don't know how many octets it will 
  1465.  * be, not before this function is called. Take a pointer to the end of that
  1466.  * buffer to make sure we don't run past it. Put the resulting UCS-2
  1467.  * char into an int16 we're given a pointer to. Returns the number of
  1468.  * octets used in the utf-8 char we converted, and returns -1 if it
  1469.  * runs out of utf-8 data without a complete UCS-2 character.
  1470.  */
  1471. PRIVATE int16 one_utf8_to_ucs2_char(const unsigned char *utf8p, const unsigned char *utf8endp, 
  1472.                                uint16 *onecharp)
  1473. {
  1474.  
  1475.     int16 i, numoctets;
  1476.     uint32    ucs4 = 0;
  1477.     *onecharp = 0;
  1478.  
  1479.     if(*utf8p >= THREE_OCTET_BASE) numoctets = 3;
  1480.     else if (*utf8p >= TWO_OCTET_BASE) numoctets = 2;
  1481.     else numoctets = 1;
  1482.         
  1483.     /* See if all the data for the char is there */
  1484.     if ((utf8p + numoctets - 1) > utf8endp) {    
  1485.         return (-1);
  1486.     }
  1487.  
  1488.  
  1489.     for(i=numoctets; i>0; i--) {
  1490.         ucs4 += *utf8p++;
  1491.         if (i == 1) break;
  1492.         ucs4 <<= 6;
  1493.     }
  1494.  
  1495.     switch(numoctets) {
  1496.  
  1497.         case 3: ucs4 -= 0x000E2080UL; break;  /* truncating... */
  1498.         case 2: ucs4 -= 0x00003080UL; break;
  1499.     }
  1500.     *onecharp= (uint16)(ucs4 & 0x0000FFFFUL);
  1501.     return(numoctets);
  1502. }
  1503.  
  1504.  
  1505. /* 
  1506.  * Internal Function: pad_and_write
  1507.  * Checks to make sure there is less than one full base64 character in the
  1508.  * buffer, pad it with 0 to make up a full base64 character, write that
  1509.  * to tobuf, and write the shift termination character. (-)
  1510.  */
  1511.  
  1512. PRIVATE uint16  pad_and_write(uint32 buffer, unsigned char *tobufp, 
  1513.                             int16 bufferBitCount, utf7_encoding_method_data*    opt)
  1514.  
  1515.  
  1516. {
  1517.     int16 oneBase64char;
  1518.     
  1519.     if(bufferBitCount >= 6) return(bufferBitCount);
  1520.     oneBase64char = ((unsigned char) (buffer >> 26));
  1521.     *tobufp++ =     opt->tob64[oneBase64char];
  1522.     *tobufp = opt->endshift;
  1523.     return(0);
  1524. }
  1525.  
  1526.  
  1527. /* Function: swap_ucs2_bytes
  1528.  * 
  1529.  * Takes a buffer of ucs2 chars, and its size in *bytes*.
  1530.  *
  1531.  * This function is meant to cope with the problem that sometimes
  1532.  * UCS-2 data (because of the big-endian, little-endian problem?)
  1533.  * comes in in reversed order, and needs to be swapped to be
  1534.  * dealt with appropriately. 
  1535.  * 
  1536.  * This case can be detected at the very beginning of the stream,
  1537.  * because the first two bytes of any UCS-2 stream should be the
  1538.  * Byte Order Mark, or 0xFEFF. If instead you see 0xFFFE, you know
  1539.  * you need to swap. Neither of these are legal UCS-2 characters
  1540.  * otherwise, so you know that there is no danger of accidentally
  1541.  * triggering swapping with a legitimate UCS-2 stream.
  1542.  * Unfortunately, this marker is only present at the very beginning
  1543.  * of a stream; future chunks of the stream won't have the marker.
  1544.  * So if we ever detect that a stream needs to be swapped, we 
  1545.  * save that information by turning on the obj->cvtflag. If, on
  1546.  * future chunks, we see that that flag is turned on, we'll go
  1547.  * ahead and swap. 
  1548.  * Notice that if swapping is unnecessary, this function has 
  1549.  * no effect whatsoever.
  1550.  */     
  1551. PRIVATE void    swap_ucs2_bytes(unsigned char *ucsbuf, int32 ucsbufsz)
  1552. {
  1553.  
  1554.     int32 i;
  1555.     unsigned char swapTemp = 0;
  1556.  
  1557.           if(ucsbufsz%2) ucsbufsz--;
  1558.  
  1559.         for(i=0; i<ucsbufsz; i+=2) { 
  1560.  
  1561.               swapTemp = ucsbuf[i];
  1562.               ucsbuf[i] = ucsbuf[i+1];
  1563.               ucsbuf[i+1] = swapTemp;
  1564.              
  1565.         } 
  1566.     return; 
  1567. }
  1568.  
  1569.  
  1570.  
  1571.  
  1572.  
  1573.  
  1574.  
  1575. /* UCS-2 to UTF-7 jliu */
  1576.  
  1577.  
  1578.  /*
  1579.   * mz_ucs2utf7
  1580.   * ------------
  1581.   *
  1582.   * This function takes a CCCDataObject, a buffer of UCS-2 data, and the
  1583.   * size of that buffer. It allocates and returns a buffer of the
  1584.   * corresponding UTF-7 data (returning the size as a field in the
  1585.   * CCCDataObject). The caller is responsible for freeing the returned
  1586.   * data. If there are extra data at the end of the UTF-8 buffer which 
  1587.   * cannot be translated into UTF-7 (ie, an incomplete character), it
  1588.   * will be saved in the uncvtbuf of the CCCDataObject and used on the
  1589.   * next call. 
  1590.   *
  1591.   * UTF-7 is a variant of base-64, and like base-64, it accumulates
  1592.   * bits in a bit buffer, transforming them to UTF-7 chars when it
  1593.   * has multiples of 6 bits. If the UTF-8 data being translated does
  1594.   * not happen to terminate with a multiple of 6 bits, the final 
  1595.   * char will be padded with 0's, and the shift sequence terminated.
  1596.   * For this reason, we will *never* be inside a shift sequence in
  1597.   * between chunks of data. This may mean that the final stream of
  1598.   * data has sequences that look like +[some UTF-7 data]-+[more data]-,
  1599.   * with a plus immediately following a -. Although unconventional,
  1600.   * this is in fact legal UTF-7. 
  1601.   *
  1602.   * Finally, there are two formats of UTF-7, one extremely conservative
  1603.   * fashion which shifts every character which could possibly be
  1604.   * considered unsafe, and another which is somewhat more lax. Which
  1605.   * of these is used is determined by obj->cvtflag. By default (cvtflag == 0)
  1606.   * we employ the safer form of conversion. The differing characters
  1607.   * are: !\"#$%&*;<=>@[]^_`{|}
  1608.   */ 
  1609. /* Tables */   
  1610.  
  1611.  
  1612. MODULE_PRIVATE UNICVTAPI unsigned char *
  1613. mz_ucs2utf7(    CCCDataObject        obj,
  1614.                 const unsigned char    *ucs2buf,    /* UTF-8 buf for conv */
  1615.                 int32                ucs2bufsz)    /* UTF-8 buf size in bytes */
  1616. {
  1617.     utf7_encoding_method_data* opt = &rfc1642_utf7;
  1618.      unsigned char    *tobuf = NULL;
  1619.     int32            tobufsz;
  1620.     unsigned char    *tobufp, *ucs2p;        /* current byte in bufs    */
  1621.      unsigned char    *tobufendp, *ucs2endp;    /* end of buffers        */
  1622.      int32                    uncvtlen = 0;
  1623.     unsigned char *uncvtbuf = INTL_GetCCCUncvtbuf(obj);
  1624.  
  1625.  
  1626.     uint16 onechar;
  1627.     int16 inShiftSequence = FALSE;
  1628.     int16 needToShift = FALSE;
  1629.     uint32 buffer = 0;
  1630.     uint32 buffertemp = 0;
  1631.     int16 bufferBitCount = 0;
  1632.     unsigned char oneBase64char;
  1633.     XP_Bool needToSwap = FALSE;
  1634.  
  1635.  
  1636.     if( INTL_GetCCCFromCSID( obj ) == CS_UCS2_SWAP )
  1637.         needToSwap = TRUE;
  1638.  
  1639.     
  1640.     /* Allocate a dest buffer:
  1641.     ** in the worst case, every Unicode character will cost 2+4 = 6 octetes
  1642.     */
  1643.  
  1644.     uncvtlen = uncvtbuf[0];
  1645.     tobufsz = 6*( (ucs2bufsz + uncvtlen)/2 + 1 ) + 1;
  1646.     if (!tobufsz) {
  1647.         return NULL;
  1648.     }
  1649.  
  1650.     if ((tobuf = (unsigned char *)XP_ALLOC_PRIV(tobufsz)) == (unsigned char *)NULL) {
  1651.         INTL_SetCCCRetval(obj, MK_OUT_OF_MEMORY);
  1652.         return(NULL);
  1653.     }
  1654.                                         /* Initialize pointers, etc.    */
  1655.      ucs2p = (unsigned char *)ucs2buf;
  1656.      ucs2endp = ucs2p + ucs2bufsz - 1; /* leave room for NULL termination (as sentinel?)*/
  1657.  
  1658.      tobufp = tobuf;
  1659.      tobufendp = tobufp + tobufsz - 2;        /* save space for terminating null*/
  1660.  
  1661.         
  1662.     while( (tobufp <= tobufendp) && (ucs2p < ucs2endp) ) {
  1663.  
  1664.         if( uncvtbuf[0] != 0 ){
  1665.             onechar = uncvtbuf[1];
  1666.             uncvtbuf[0] = 0;
  1667.         } else
  1668.             onechar = *ucs2p++;
  1669.         onechar <<= 8;
  1670.         onechar |= *ucs2p++;
  1671.  
  1672.         /* do the swap stuff */
  1673.  
  1674.         if( onechar == NEEDS_SWAP_MARK ){
  1675.             INTL_SetCCCFromCSID( obj, CS_UCS2_SWAP );
  1676.             needToSwap = TRUE;
  1677.             continue;
  1678.         } else if( onechar == BYTE_ORDER_MARK ){
  1679.             INTL_SetCCCFromCSID( obj, CS_UCS2 );
  1680.             needToSwap = FALSE;
  1681.             continue;
  1682.         }
  1683.  
  1684.         if( needToSwap ){
  1685.             onechar = ( onechar << 8 ) | ( onechar >> 8 );
  1686.         }
  1687.  
  1688.         /* we need to be shifted if the character is non-ASCII or 
  1689.          * is an ASCII character that should be shifted.
  1690.          */
  1691.         needToShift = (onechar > MAX_ASCII) || (opt->shift[onechar]);
  1692.  
  1693.  
  1694.         if(!needToShift && inShiftSequence)      {
  1695.  
  1696.             if(bufferBitCount > 0) {
  1697.                 if((tobufp+2) > tobufendp) break;
  1698.                 bufferBitCount = pad_and_write(buffer, tobufp, bufferBitCount, opt);
  1699.                 if (!bufferBitCount) {    /* buffer successfully flushed */
  1700.                     tobufp+=2;
  1701.                     buffer = 0;
  1702.                 }
  1703.             
  1704.             } else {
  1705.                 if((tobufp+1) > tobufendp) break;
  1706.                 *tobufp++ = opt->endshift;
  1707.             }
  1708.             inShiftSequence = FALSE; /* now just fallthrough to next case*/
  1709.         }
  1710.  
  1711.         if(!needToShift &&     !inShiftSequence) {
  1712.             if((tobufp+1) > tobufendp) break;
  1713.             *tobufp++ = (char) onechar;
  1714.         } 
  1715.  
  1716.         if(needToShift && !inShiftSequence)     {
  1717.             *tobufp++ = opt->startshift;
  1718.             if(onechar == opt->startshift) { /* special-case behavior if onechar is a + */
  1719.                 if((tobufp+1) > tobufendp) break;
  1720.                 *tobufp++ = opt->endshift;
  1721.             }
  1722.             else inShiftSequence = TRUE; 
  1723.         }
  1724.  
  1725.         if(needToShift && inShiftSequence) {
  1726.             
  1727.             buffertemp = onechar & 0x0000FFFF;
  1728.             buffer |= buffertemp << (16 - bufferBitCount);    
  1729.                                             /* ^--16 is the size of the int32 minus
  1730.                                              * the size of onechar */
  1731.             bufferBitCount += 16;
  1732.  
  1733.  
  1734.             /* Flush the buffer of as many base64 characters as we can form */
  1735.             while(bufferBitCount>5) {
  1736.                    if(tobufp > tobufendp) break;
  1737.                   oneBase64char = (char)  ((buffer & 0xFC000000) >> 26);
  1738.                   *tobufp++ =  opt->tob64[oneBase64char];
  1739.                   buffer <<= 6;
  1740.                   bufferBitCount -= 6;
  1741.             }
  1742.         }
  1743.  
  1744.  
  1745.     } /* end of while loop */
  1746.  
  1747.  
  1748.  
  1749.     /* Anything left in the buffer at this point should be padded with 0's
  1750.      * and appended to tobuf. */
  1751.  
  1752.     if(inShiftSequence) {
  1753.  
  1754.         if(bufferBitCount > 0) {
  1755.  
  1756.             if((tobufp+2) <= tobufendp) {
  1757.                 bufferBitCount = pad_and_write(buffer, tobufp, bufferBitCount,  opt);
  1758.                 if (!bufferBitCount) { /* buffer successfully flushed */
  1759.                     tobufp+=2;
  1760.                     buffer = 0;
  1761.                 }
  1762.             }
  1763.  
  1764.         }  else {
  1765.              if((tobufp+1) <= tobufendp) *tobufp++ = opt->endshift;
  1766.         }
  1767.  
  1768.         inShiftSequence = FALSE;
  1769.     }
  1770.  
  1771.  
  1772.     *tobufp = '\0';                /* NULL terminate dest. data */
  1773.  
  1774.  
  1775.     INTL_SetCCCLen(obj, tobufp - tobuf);        /* length of processed data, in bytes */
  1776.  
  1777.     if(ucs2p <= ucs2endp) {            /*  unconverted ucs2 left? */
  1778.         uncvtbuf[0] = 1;
  1779.         uncvtbuf[1] = *ucs2endp;
  1780.     } else
  1781.         uncvtbuf[0] = 0;
  1782.  
  1783.  
  1784.     return(tobuf);
  1785. }
  1786.  
  1787.