home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / winsock / wvnsc926 / wvcoding.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  63.6 KB  |  2,102 lines

  1. /********************************************************************
  2.  *                                                                  *
  3.  *  MODULE    :  WVCODING.C                                         *
  4.  *                                                                  *
  5.  *  PURPOSE   : This file contains functions for en/decoding files  *
  6.  *                                                                  *
  7.  *  ENTRY POINTS:                                                   *
  8.  *            DecodeInit  - initializes decoding resources        *
  9.  *        DecodeDone  - frees decoding resources              *
  10.  *        InitCoded   - initializes a coded block             * 
  11.  *                  called for each new art to decode     *
  12.  *        DecodeLine  - Main decoding engine.  Called once    *
  13.  *                  for each input line                   *
  14.  *        DecodeFile  - Uses file instead of comm input as a  *
  15.  *                     line provider to the decoding engine  *
  16.  *        DecodeDoc   - Uses a Doc instead of comm input as a *
  17.  *                     line provider to the decoding engine  *
  18.  *    CompleteThisDecode  - Called at end of each article - adds  *
  19.  *                  block to appropriate decode thread,   *
  20.  *                  writes to disk if possible, etc       *
  21.  *                  Encode  - Encodes a file into a text block      *
  22.  *            EncodeToFile  - Calls Encode, then writes block to    * 
  23.  *                            an output file                        *
  24.  *                                                                  *
  25.  *  NOTES:                                                          *
  26.  *    Both the max # of batched decode files and the max #        * 
  27.  *    threads per file are fixed.  This should be made dynamic    *
  28.  *    at some point I guess.                                      *
  29.  *                                                                  *
  30.  * Author: John S. Cooper (jcooper@netcom.com)                      *
  31.  *   Date: Sept 13, 1993                                            *
  32.  *                                                                  *
  33.  * Let me know if you like this code, or if you re-use any piece of *
  34.  * it. I'm just curious to know what people think.                  *
  35.  ********************************************************************/
  36. /* 
  37.  * $Id: wvcoding.c 1.12 1994/09/18 22:44:20 jcooper Exp $
  38.  * $Log: wvcoding.c $
  39.  * Revision 1.12  1994/09/18  22:44:20  jcooper
  40.  * Better handling of status windows, using IsBusy flag, etc
  41.  *
  42.  * Revision 1.11  1994/09/16  00:58:48  jcooper
  43.  * new coding-status window style.  optimized info-header parser.
  44.  * general cleanup for 92.6
  45.  * 
  46.  * Revision 1.10  1994/08/24  18:00:29  jcooper
  47.  * misc encoding/decoding changes
  48.  *
  49.  * Revision 1.9  1994/08/11  21:03:19  rushing
  50.  * FindExecutable fix for NT
  51.  *
  52.  * Revision 1.8  1994/08/11  20:16:25  rushing
  53.  * bug fix
  54.  *
  55.  * Revision 1.6  1994/07/25  20:05:13  jcooper
  56.  * execution of decoded files
  57.  *
  58.  * Revision 1.5  1994/05/23  18:36:00  jcooper
  59.  * new attach code, session [dis]connect
  60.  *
  61.  * Revision 1.4  1994/03/01  19:14:22  rushing
  62.  * ifdef'd the DEBUG info
  63.  *
  64.  * Revision 1.3  1994/02/24  21:28:04  jcoop
  65.  * jcoop changes.
  66.  *
  67.  * Revision 1.2  1994/02/09  18:01:08  cnolan
  68.  * cnolan 90.2 changes
  69.  *
  70.  * Revision 1.1  1994/01/18  09:54:20  jcoop
  71.  * Initial revision
  72.  *
  73.  */ 
  74. #include <windows.h>
  75. #include <windowsx.h>
  76. #include "wvglob.h"
  77. #include "winvn.h"
  78. #pragma hdrstop
  79. #include <string.h>
  80. #include <stdio.h>
  81. #include <stdlib.h>
  82. #include <ctype.h>        /* for isspace, isalnum, etc */
  83.  
  84. #ifdef _DEBUG
  85. #define DEBUG_MAP
  86. #define DEBUG_MEM
  87. #define DEBUG_DECODE
  88. #endif
  89.  
  90. #define LOW        1    // sequence confidence levels
  91. #define HIGH            2
  92. #define UNUSED        127    // unused mapping slots
  93.  
  94. #define END_BLOCK    2
  95. #define CODE_LINE_LEN    45    
  96. /*
  97.  * Globals for this func
  98.  */
  99. char
  100.     xxTable[CODINGTABLESIZE] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  101. char
  102.     uuTable[CODINGTABLESIZE] = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
  103. char
  104.     base64Table[CODINGTABLESIZE] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  105. char    *codingTable;
  106. char     customTable[CODINGTABLESIZE];
  107.  
  108. TypDecodeThread *threadList[MAX_DECODE_THREADS];
  109. int    numDecodeThreads;        // number of active decode threads
  110. int    currentDecodeThread;
  111. int    numDumbDecoded;
  112. BOOL    bossanovaMIME;                  // = TRUE if found MIME Version header
  113. int      thisNumBlocks;             // MIME vars for current block
  114. char     thisContentType[80];
  115. char    thisContentDesc[MAXINTERNALLINE];
  116. char    thisBoundary[MAXINTERNALLINE];
  117. int    thisContentEncoding, prevContentEncoding;
  118. char     prevBlockIdent[MAXFILENAME];
  119. /*
  120.  * Private functions
  121.  */
  122. int     EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num);
  123. void    EncodeUnit (unsigned char *out, unsigned char *in, int num);
  124. int    GetThreadByName (char *name);
  125. int    AddToThreadList ();
  126. void     DestroyThread (int num);
  127. void    DestroyCodedBlock (TypCoded **thisCoded);
  128. void    InsertBlockInThread (int num, TypCoded *thisBlock, int index);
  129. void    DeleteBlockFromThread (int num, int index);
  130. BOOL    WriteDecodeThread(int num);
  131. BOOL    WriteBlockToFile(int num, TypCoded *thisDecode);
  132. BOOL    WriteTopBlockToFile (int num, int *endFlag);
  133. BOOL    AddDataToBlock (TypCoded *thisDecode, char *newData, unsigned int dataLen);
  134. void    ParseInfoLine (TypCoded *thisDecode, char *line, BOOL guessIdent);
  135. BOOL    ParseAppSpecificLine (TypCoded *thisDecode, char *line);
  136. char     *ReadSubjectToken (char *dest, char **ptr);
  137. BOOL    isnumber (char *str);
  138. BOOL    SearchThreadNames (char *name);
  139. int    ParseMimeHeaderLine (TypCoded *thisDecode, char *line);
  140. int    ParseMimeContentType (TypCoded *thisDecode, char *line);
  141.  
  142. void CreateCodingStatusWnd();
  143. int    DecodeDataLine (TypCoded *decode, char *line);
  144. void     CreateUUTable();
  145. BOOL    TestIgnoreLine (char *line);
  146. int    IsDataLine (char *line);
  147. BOOL     TestDataLine (char *line);
  148. int     ThreadTable (char *dest, char *name);
  149. void    UpdateThreadStatus (int num, char *str);
  150.  
  151. void    ExecuteDecodedFile (int num, char *fileName);
  152.  
  153. #if defined(DEBUG_DECODE) || defined(DEBUG_MEM)
  154. #define DEBUG_FILE "coding.log"
  155. void     DebugLog();
  156. void     InitDebugLog();
  157. char     debug[MAXCOMMLINE];
  158. OFSTRUCT debugFileStruct;
  159. #endif
  160.  
  161. #if defined(DEBUG_DECODE) || defined(DEBUG_MEM)
  162. /* ------------------------------------------------------------------------
  163.  * Debug log handler
  164.  * Open/close the file for every addition to log, so all info is safely
  165.  * captured in case of GPF
  166.  */
  167. void
  168. DebugLog ()
  169. {
  170.     HFILE hDebugFile;
  171.  
  172.         if ((hDebugFile = OpenFile ((char far *)NULL, &debugFileStruct, OF_REOPEN|OF_WRITE)) >= 0)
  173.     {       
  174.         _llseek(hDebugFile, 0L, 2);    /* go to end */
  175.             _lwrite(hDebugFile, debug, strlen(debug));
  176.             _lclose(hDebugFile);
  177.         }
  178. }
  179. void
  180. InitDebugLog ()
  181. {
  182.     HFILE hDebugFile;
  183.  
  184.         if ((hDebugFile = OpenFile ((char far *)DEBUG_FILE, &debugFileStruct, OF_CREATE)) >= 0)
  185.     {
  186.             sprintf(debug, "New log");
  187.             _lwrite(hDebugFile, debug, strlen(debug));
  188.             _lclose(hDebugFile);
  189.         }
  190. }
  191. #endif
  192. /* ------------------------------------------------------------------------
  193.  * Decode a file
  194.  * Uses a file instead of the comm article as a line-provider to the 
  195.  * decode routines
  196.  */
  197. void
  198. DecodeFile (HWND hParentWnd)
  199. {
  200.     FILE *DecodeFile;
  201.         char mybuf[MAXINTERNALLINE], fileName[MAXFILENAME], *ptr;
  202.         int result;
  203.  
  204.         if (AskForExistingFileName (hParentWnd, fileName, "Open encoded file") == FAIL)
  205.             return;
  206.  
  207.     DecodeInit();
  208.     if ((DecodeFile = fopen (fileName, "r")) == NULL)
  209.     {
  210.         MessageBox (hParentWnd, "Failure to read file", "File error", MB_OK);
  211.         return;
  212.     } 
  213.     if ((currentCoded = InitCoded(hParentWnd)) == NULL)
  214.     {
  215.         MessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
  216.         return;
  217.     }
  218.            while (1)
  219.            {
  220.                if ((ptr = fgets(mybuf, MAXINTERNALLINE, DecodeFile)) == NULL)
  221.                    strcpy (mybuf, "EOF");    // make sure wrap up occurs
  222.  
  223.         result = DecodeLine (currentCoded, mybuf);
  224.         if (result == FAIL)
  225.         {
  226.                  MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
  227.             goto abortDecodeFile;
  228.         }           
  229.         if (result == END_BLOCK)
  230.         {
  231.             if (CompleteThisDecode () == FAIL)
  232.             {
  233.                      MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
  234.                 goto abortDecodeFile;
  235.             }           
  236.                if ((currentCoded = InitCoded(hParentWnd)) == NULL)
  237.             {
  238.                 MessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
  239.                 goto abortDecodeFile;
  240.             }
  241.         }
  242.         if (ptr == NULL)    // EOF
  243.             break;
  244.     }
  245.  
  246.     if (currentCoded != NULL)    // finish final block
  247.         CompleteThisDecode ();
  248.    
  249.    abortDecodeFile:;
  250.           fclose(DecodeFile);
  251.     DecodeDone ();
  252. }
  253. /* ------------------------------------------------------------------------
  254.  * Decode a doc
  255.  * Uses a doc instead of the comm article as a line-provider to the 
  256.  * decode routines
  257.  */
  258. void
  259. DecodeDoc (HWND hParentWnd, TypDoc *Document)
  260. {
  261.         int result;
  262.     TypBlock far *BlockPtr;
  263.     TypLine far *LinePtr;
  264.             
  265.     DecodeInit();
  266.     if ((currentCoded = InitCoded(hParentWnd)) == NULL)
  267.     {
  268.         MessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
  269.         return;
  270.     }
  271.     
  272.     LockLine (Document->hFirstBlock, sizeof (TypBlock), (TypLineID) 0L, &BlockPtr, &LinePtr);
  273.     while (LinePtr->length != END_OF_BLOCK)
  274.     {
  275.         result = DecodeLine (currentCoded, 
  276.                 ((char far *) LinePtr) + sizeof (TypText) + sizeof (TypLine));
  277.  
  278.         if (result == FAIL)
  279.         {
  280.                  MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
  281.             goto abortDecodeDoc;
  282.         }           
  283.  
  284.         if (result == END_BLOCK)
  285.         {
  286.             if (CompleteThisDecode () == FAIL)
  287.             {
  288.                      MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
  289.                 goto abortDecodeDoc;
  290.             }           
  291.                if ((currentCoded = InitCoded(hParentWnd)) == NULL)
  292.             {
  293.                 MessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
  294.                 return;
  295.             }
  296.         }
  297.         NextLine (&BlockPtr, &LinePtr);
  298.     }
  299.     if (currentCoded != NULL)    // finish final block
  300.         CompleteThisDecode ();
  301.    
  302.    abortDecodeDoc:;
  303.     GlobalUnlock (BlockPtr->hCurBlock);
  304.     DecodeDone ();
  305. }
  306. /* ------------------------------------------------------------------------
  307.  *    Encode a file and ask the user for a file to name to store it in
  308.  */
  309. void
  310. EncodeToFile (HWND hParentWnd, char *inFile)
  311. {
  312.    register unsigned long i;
  313.    char fileName[MAXFILENAME];
  314.    OFSTRUCT outFileStruct;
  315.    HFILE hAttachFile;
  316.    TypTextBlock *encodeData;
  317.  
  318.    if ((encodeData = InitTextBlock (hParentWnd)) != NULL)
  319.    {
  320.       CreateStatusArea(hParentWnd);
  321.       strcpy (currentCoded->ident, inFile);    // status window info
  322.  
  323.       if (Encode (encodeData, inFile, ADD_TO_FILE) != FAIL)
  324.       {
  325.     fileName[0]='\0';        
  326.     if (AskForNewFileName (hParentWnd, fileName, "", FALSE) != FAIL)
  327.     {
  328.        if ((hAttachFile = OpenFile ((char far *) fileName, &outFileStruct, OF_CREATE)) < 0)
  329.         MessageBox(hParentWnd, "Unable to open output file", "File Error", MB_OK|MB_ICONSTOP);
  330.        else
  331.        {
  332.           for (i = 0; i < encodeData->numLines; i++)
  333.           _lwrite (hAttachFile, TextBlockLine (encodeData, i), lstrlen(TextBlockLine (encodeData, i)));
  334.           _lclose (hAttachFile);
  335.        }
  336.     }    
  337.       }     
  338.       FreeTextBlock (encodeData);
  339.       DestroyStatusArea ();
  340.    }   
  341. }
  342.  
  343. /* ------------------------------------------------------------------------
  344.  *    Create a status window a coding block used for status info
  345.  *    the currentCoded block is purely for status in this case
  346.  *    This is used by attach and EncodeFile and ReadFileToTextBlock
  347.  *    to show status in progress
  348.  *    Use UpdateBlockStatus to update/display status
  349.  *    Use DestroyStatusArea to clean up when done
  350.  */
  351. void
  352. CreateStatusArea (HWND hParentWnd)
  353. {
  354.     if ((currentCoded = InitCoded(hParentWnd)) == NULL)
  355.         return;
  356.     CreateCodingStatusWnd();
  357.     currentCoded->sequence = 1;        // status window info
  358.  
  359. }
  360.  
  361. void
  362. DestroyStatusArea ()
  363. {
  364.     DestroyCodedBlock (¤tCoded);
  365.     DestroyWindow (hCodedBlockWnd);
  366.     hCodedBlockWnd = (HWND)NULL;
  367. }
  368.  
  369. void
  370. CreateCodingStatusWnd()
  371. {
  372.     /* Create coding status window, top center */
  373.     hCodedBlockWnd = CreateWindowEx (WS_EX_DLGMODALFRAME, "WinVnBlockCoding", "Block Encoding Status",
  374.             WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_THICKFRAME,
  375.             (xScreen-STATUSWIDTH)>>1, 1, STATUSWIDTH, STATUSHEIGHT,
  376.             (HWND)NULL, (HMENU)NULL, hInst, (void far *)NULL);
  377.     
  378.     SetHandleBkBrush( hCodedBlockWnd, hStatusBackgroundBrush);
  379.     ShowWindow (hCodedBlockWnd, SW_SHOWNORMAL);
  380. }
  381.  
  382.  
  383. /* ------------------------------------------------------------------------
  384.  *    Adds text to a thread coding status block, and refreshes
  385.  *    the thread status window
  386.  */    
  387. void
  388. UpdateThreadStatus (int num, char *str)
  389. {
  390.     AddLineToTextBlock (threadList[num]->statusText, str);
  391.     InvalidateRect (threadList[num]->statusText->hTextWnd, NULL, FALSE);
  392.     UpdateWindow (threadList[num]->statusText->hTextWnd);
  393. }
  394.  
  395. /* ------------------------------------------------------------------------
  396.  *    Refreshes the block status window
  397.  */    
  398. void
  399. UpdateBlockStatus ()
  400. {
  401.     InvalidateRect (hCodedBlockWnd, NULL, FALSE);
  402.     UpdateWindow (hCodedBlockWnd);
  403. }
  404.  
  405. /* ------------------------------------------------------------------------
  406.  *    Encode a file 
  407.  *    Called _after_ the attach dialog, so all globals for this encode
  408.  *    are set  (sorry about the globals...)
  409.  *    Result is encoded file in TextBlock 
  410.  *    Assumes currentCoded is initialized
  411.  */
  412. int 
  413. Encode (TypTextBlock *textBlock, char *fileName, int mode)
  414. {
  415.     HFILE hFile;
  416.         int numRead, lineLen, start;
  417.     register int i;
  418.     unsigned char inBuf[CODE_LINE_LEN];
  419.     unsigned char outLine[MAXINTERNALLINE];
  420.     extern char *NameWithoutPath ();
  421.  
  422.     CodingState = ATTACH_PROCESSING;
  423.  
  424.     // Set up encoding map
  425.     switch (EncodingTypeNum)
  426.     {
  427.     case CODE_BASE64:    
  428.         codingTable = base64Table;
  429.         break;
  430.     case CODE_UU:
  431.         codingTable = uuTable;
  432.         break;
  433.     case CODE_XX:
  434. //    Note, XX table is hard coded in declarations for simplicity
  435.         codingTable = xxTable;
  436.         break;
  437.     case CODE_CUSTOM:
  438.         codingTable = UserCodingTable;
  439.         // add 2 table lines
  440.         if (AddEndedLineToTextBlock (textBlock, "table", mode))
  441.             return (FAIL);
  442.         memmove (str, UserCodingTable, 32);
  443.         str[32] = '\0';
  444.         if (AddEndedLineToTextBlock (textBlock, str, mode))
  445.             return (FAIL);
  446.         memmove (str, &UserCodingTable[32], 32);
  447.         if (AddEndedLineToTextBlock (textBlock, str, mode))
  448.             return (FAIL);
  449.         break;
  450.     }
  451.  
  452.     // Prepare for encoding
  453.     switch (EncodingTypeNum)
  454.     {
  455.     case CODE_UU:
  456.     case CODE_XX:
  457.     case CODE_CUSTOM:
  458.         // these are 3-to-4 encodings with 1st char indicating line len
  459.         // and block starts with 'begin' line and ends with 'end' line
  460.         // add 'begin' line
  461.         NameWithoutPath (str, fileName);
  462.         sprintf (outLine, "begin 755 %s", str);
  463.         if (AddEndedLineToTextBlock (textBlock, outLine, mode))
  464.             return (FAIL);
  465.         currentCoded->numLines++;
  466.             // set 1st char to appropriate line length value
  467.             outLine[0] = codingTable[CODE_LINE_LEN];
  468.             start = 1;    // count includes the count char itself
  469.         break;
  470.     case CODE_BASE64:
  471.         // base64 is a 3-to-4 encoding, with no line len indicator and 
  472.         // no 'begin' or 'end' lines
  473.         start = 0;
  474.     }
  475.     lineLen = CODE_LINE_LEN;
  476.  
  477.     // open the file and have at it    
  478.         hFile = _lopen (fileName, OF_READ);
  479.         while (1)
  480.         {
  481.             if ((numRead = _lread(hFile, inBuf, lineLen)) == 0)
  482.                 break;
  483.         if (numRead < lineLen)    // last line length value
  484.         {
  485.             if (EncodingTypeNum != CODE_BASE64)
  486.                 outLine[0] = codingTable[numRead];
  487.             
  488.             for (i = numRead; i < lineLen; i++)
  489.                 inBuf[i] = 0;
  490.         }
  491.             EncodeLine (outLine, inBuf, start, numRead);
  492.         if (AddEndedLineToTextBlock (textBlock, outLine, mode))
  493.             return (FAIL);
  494.         currentCoded->numLines++;
  495.         currentCoded->numBytes += numRead;
  496.             if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
  497.                 UpdateBlockStatus ();
  498.     }        
  499.     _lclose(hFile);
  500.     switch (EncodingTypeNum)
  501.     {
  502.     case CODE_UU:
  503.     case CODE_XX:
  504.     case CODE_CUSTOM:
  505.             // add zero length line
  506.             outLine[0] = codingTable[0];
  507.             outLine[1] = '\0';
  508.         if (AddEndedLineToTextBlock (textBlock, outLine, mode))
  509.             return (FAIL);
  510.         // add 'end' line
  511.         strcpy (outLine, "end");
  512.         if (AddEndedLineToTextBlock (textBlock, outLine, mode))
  513.             return (FAIL);
  514.         currentCoded->numLines+=2;
  515.         }
  516.     UpdateBlockStatus ();
  517.     
  518.     CodingState = INACTIVE;
  519.     return (SUCCESS);
  520. }
  521.     
  522. /* ------------------------------------------------------------------------
  523.  *    Encode num chars from line
  524.  *    Put data in outLine starting at start
  525.  */
  526. int 
  527. EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num)
  528. {
  529.     register int i, j;
  530.     
  531.     for (j = start, i = 0; i < num; j+=4, i+=3)
  532.         EncodeUnit (&outLine[j], &line[i], (i+3 > num) ? num - i : 3);
  533.  
  534.     outLine[j] = '\0';
  535.     return (j);
  536. }
  537.     
  538. void
  539. EncodeUnit (unsigned char *out, unsigned char *in, int num)
  540. {
  541.            out[0] = codingTable[((in[0]>>2) & 63)];
  542.     out[1] = codingTable[(((in[0]<<4)|(in[1]>>4)) & 63)];
  543.     if (num == 1)
  544.     {
  545.         if (EncodingTypeNum == CODE_BASE64)
  546.             strcpy (&out[2], "==");
  547.         return;
  548.     }
  549.  
  550.     out[2] = codingTable[(((in[1]<<2)|(in[2]>>6)) & 63)];
  551.     if (num == 2)
  552.     {
  553.         if (EncodingTypeNum == CODE_BASE64)
  554.             strcpy (&out[3], "=");
  555.         return;
  556.     }
  557.  
  558.     out[3] = codingTable[(in[2] & 63)];
  559. }
  560. /* ------------------------------------------------------------------------
  561.  *    Init for decoding
  562.  *     Initialize the coding tables
  563.  *    Init threadList:  list of pointers to Thread lists 
  564.  *    (one thread for each file being decoded)
  565.  *        
  566.  */
  567. void
  568. DecodeInit ()
  569. {
  570.     register int i;
  571.     
  572. #ifdef DEBUG_MEM
  573.     InitDebugLog();
  574. #endif
  575.     
  576.         for (i = 0; i < MAX_DECODE_THREADS; i++)
  577.             threadList[i] = NULL;
  578.  
  579.     currentCoded = (TypCoded *) NULL;
  580.     CodingState = DECODE_SKIPPING;
  581.     numDecodeThreads = 0;
  582.     numDumbDecoded = 0;
  583.     currentDecodeThread = -1;    
  584.     thisNumBlocks = -1;
  585.     prevBlockIdent[0] = '\0';
  586.     thisContentEncoding = CODE_UNKNOWN;
  587.     prevContentEncoding = CODE_UNKNOWN;
  588.         customTable[0] = '\0';
  589.  
  590. #ifdef DEBUG_MEM
  591.     sprintf(debug, "\nInitialized decoding, %d threads", MAX_DECODE_THREADS);
  592.     DebugLog();
  593. #endif
  594.     // Create coding status window (open for duration of coding only)
  595.     CreateCodingStatusWnd();
  596.     UpdateBlockStatus ();
  597. }
  598.  
  599. /* ------------------------------------------------------------------------
  600.  * At this point, all threads in sequence should have been written and freed,
  601.  * If any threads left, we either had an incomplete decode of some file,
  602.  * or the file was encoded w/out sequence info, and we were waiting to 
  603.  * hopefully receive it all.  If we had an endflag, then write it in order
  604.  * received
  605.  * Accepts handle of parent window so it can display any 'incomplete' messages
  606.  */
  607. void
  608. DecodeDone ()
  609. {
  610.     char mybuf[MAXINTERNALLINE], name[MAXINTERNALLINE];
  611.  
  612. #ifdef DEBUG_MEM
  613.     sprintf(debug, "\nDone coding,  Wrapping up stray threads");
  614.     DebugLog();
  615. #endif
  616.     name[0]='\0';            // only show name if not verbose mode
  617.     while (numDecodeThreads > 0)    // always work on top of list [0]
  618.     {
  619.         if (!CodingStatusVerbose)
  620.             if (threadList[0]->name[0] != '\0')
  621.                 sprintf (name, "%s   ", threadList[0]->name);
  622.             else
  623.                 sprintf (name, "%s   ", threadList[0]->ident);
  624.  
  625.         if (threadList[0]->numBlocks == 0)
  626.         {    // premature end of file
  627.                     WriteDecodeThread (0);
  628.             sprintf (mybuf, "%sDecode is missing parts, written total size %ld", name, threadList[0]->totalBytes);
  629.             UpdateThreadStatus (0, mybuf);
  630.         }
  631.         else if (threadList[0]->contentEncoding != CODE_BASE64 &&
  632.                 !threadList[0]->codedBlockList[threadList[0]->numBlocks-1]->endFlag)
  633.         {
  634.             sprintf (mybuf, "%sEnd never found.  Cancelling", name);
  635.             UpdateThreadStatus (0, mybuf);
  636.         }
  637.         else if (threadList[0]->expectedNumBlocks > 0)
  638.         {    // there IS sequencing info, so we should not be here
  639.                     WriteDecodeThread (0);
  640.             sprintf (mybuf, "%sDecode is missing parts, written total size %ld", name, threadList[0]->totalBytes);
  641.             UpdateThreadStatus (0, mybuf);
  642.         }
  643.         else
  644.                 {
  645.             sprintf (mybuf, "%sCompleteness confidence is medium, written total size %ld", name, threadList[0]->totalBytes);
  646.             UpdateThreadStatus (0, mybuf);
  647.                     WriteDecodeThread (0);
  648.         }            
  649.         DestroyThread (0);    // decrements numDecodeThreads
  650.     }
  651.     currentCoded = NULL;
  652.     CodingState = INACTIVE;
  653.         CommDecoding = FALSE;
  654.     DestroyWindow (hCodedBlockWnd);
  655.     hCodedBlockWnd = (HWND)NULL;
  656. }    
  657.  
  658. /* ------------------------------------------------------------------------
  659.  *     Initialize a coded object, and initial data space
  660.  *    Returns ptr to object or NULL if failed
  661.  */
  662. TypCoded *
  663. InitCoded (HWND hParentWnd)
  664. {      
  665.     TypCoded *thisCoded = NULL;
  666.     
  667. #ifdef DEBUG_MEM
  668.     sprintf(debug, "\nInitializing a coded object");
  669.     DebugLog();
  670. #endif
  671.     if ((thisCoded = (TypCoded *) GlobalAllocPtr ( GMEM_MOVEABLE, sizeof (TypCoded))) == NULL)
  672.         return ((TypCoded *) NULL);
  673.     
  674. #ifdef DEBUG_MEM
  675.     sprintf(debug, "\nAllocing data size %d", BASE_BLOCK_SIZE);
  676.     DebugLog();
  677. #endif
  678.     if ((thisCoded->data = (char huge *) GlobalAllocPtr (GMEM_MOVEABLE, BASE_BLOCK_SIZE * sizeof (char))) == NULL)
  679.         return ((TypCoded *) NULL);
  680.  
  681.     thisCoded->maxBytes = BASE_BLOCK_SIZE;
  682.     thisCoded->numBytes = 0;
  683.     thisCoded->numLines = 0;
  684.     thisCoded->sequence = -1;
  685.     thisCoded->seqConfidence = 0;
  686.     thisCoded->name[0] = '\0';
  687.     thisCoded->ident[0] = '\0';
  688.     thisCoded->endFlag = thisCoded->beginFlag = FALSE;
  689.     thisCoded->hParentWnd = hParentWnd;
  690.         bossanovaMIME = FALSE;
  691.     
  692.     return (thisCoded);
  693. }    
  694.  
  695. /* ------------------------------------------------------------------------
  696.  *     Destroy a thread
  697.  *    Destroy all blocks in the thread
  698.  *    Remove the thread from the threadList
  699.  */
  700. void
  701. DestroyThread (int num)
  702.     register int i;
  703.     
  704. #ifdef DEBUG_MEM
  705.     sprintf(debug, "\nDestroying thread %d", num);
  706.     DebugLog();
  707. #endif
  708.     if (CodingStatusVerbose)
  709.         threadList[num]->statusText->IsBusy = FALSE;
  710.     
  711.     for (i = 0; i < threadList[num]->numBlocks; i++)
  712.         DestroyCodedBlock (&(threadList[num]->codedBlockList[i]));
  713.         
  714.     GlobalFreePtr (threadList[num]);
  715.  
  716.     for (i = num; i < numDecodeThreads; i++)
  717.         threadList[i] = threadList[i + 1];
  718.     
  719.     numDecodeThreads--;
  720.     if (currentDecodeThread == num)
  721.         currentDecodeThread = max (0, currentDecodeThread - 1);
  722. }
  723. /* ------------------------------------------------------------------------
  724.  *     Destroy a coded block structure
  725.  *    Free the huge data.  Takes a ptr to a ptr so it can NULL it when done
  726.  */
  727. void
  728. DestroyCodedBlock (TypCoded **thisCoded)
  729. {        
  730. #ifdef DEBUG_MEM
  731.     sprintf(debug, "\nDestroying block sequence %d", (*thisCoded)->sequence);
  732.     DebugLog();
  733. #endif
  734.     GlobalFreePtr ((*thisCoded)->data);
  735.     GlobalFreePtr (*thisCoded);
  736.         *thisCoded = NULL;
  737. }
  738.  
  739. /* ------------------------------------------------------------------------
  740.  *     Given a file name, find the associated thread number  
  741.  *    Returns -1 if no thread by that name exists
  742.  */
  743. int
  744. GetThreadByName (char *name)
  745. {
  746.     int i, result;
  747.     
  748.     for (result = -1, i = 0; i < numDecodeThreads && result == -1; i++)
  749.         if (!_stricmp (name, threadList[i]->name))
  750.             result = i;
  751.  
  752.     return (result);
  753. }
  754. /* ------------------------------------------------------------------------
  755.  *     Determine if a thread has already been started for this ident
  756.  *    if yes, add decode object to that thread
  757.  *    otherwise, alloc a new thread list structure, add this block to it
  758.  *    At end, currentDecodeThread is set to handle of thread
  759.  */
  760. BOOL                                                           
  761. AddToThreadList ()
  762. {
  763.     register int i;
  764.     int num;
  765.     char mybuf[MAXINTERNALLINE];
  766.     int x, y, width, height, maxX, maxY;
  767.     
  768.      if (DumbDecode)
  769.      {    
  770.         if (numDecodeThreads == 0)
  771.              num = -1;
  772.         else 
  773.              num = 0;
  774.     } else
  775.      /* Search existing threads for an ident match.  If
  776.       * no match found, or no threads exist yet, add new thread.
  777.       */
  778.         for (num = -1, i = 0; i < numDecodeThreads; i++)
  779.             if (!_stricmp (currentCoded->ident, threadList[i]->ident))
  780.                 num = i;
  781.  
  782.     if (num == -1)        /* add a new thread */
  783.     {
  784.         if ((numDecodeThreads + 1) > MAX_DECODE_THREADS)    
  785.         {
  786.             return (FAIL);
  787. /*            GlobalUnlock(decodeObjectHandles);
  788.               decodeObjectHandles = GlobalRealloc (decodeFileHandles,
  789.                   (numDecodeThreads + LIST_SIZE_INC) * sizeof (HGLOBAL), 
  790.                   GMEM_MOVEABLE);
  791.             threadList = (HGLOBAL *) GlobalLock (decodeFileHandles);
  792. */
  793.         }
  794.  
  795. // create a new DecodeThreadStruct object, store its handle in threadList,
  796. #ifdef DEBUG_MEM
  797.         sprintf(debug, "\nCreating new thread ident %s", currentCoded->ident);
  798.         DebugLog();
  799. #endif
  800.          num = numDecodeThreads;
  801.         numDecodeThreads++;
  802.  
  803.         threadList[num] = (TypDecodeThread *) GlobalAllocPtr (GMEM_MOVEABLE, sizeof (TypDecodeThread));
  804.         threadList[num]->expectedNumBlocks = 0;
  805.         threadList[num]->numBlocksWritten = 0;
  806.         threadList[num]->numBlocks = 0;
  807.         threadList[num]->totalBytes = 0;
  808.         threadList[num]->dosFileName[0] = '\0';
  809.         if (thisContentEncoding == CODE_CUSTOM)
  810.             strncpy (threadList[num]->customTable, customTable, CODINGTABLESIZE);
  811.         threadList[num]->contentEncoding = thisContentEncoding;
  812.         strcpy (threadList[num]->ident, currentCoded->ident);
  813.                 if (currentCoded->name[0] != '\0')
  814.             strcpy (threadList[num]->name, currentCoded->name);
  815.         else
  816.             threadList[num]->name[0] = '\0';
  817.  
  818.             if (CodingStatusVerbose || NumStatusTexts == 0)
  819.         {
  820.            if ((threadList[num]->statusText = InitTextBlock(hCodedBlockWnd)) == NULL)
  821.             MessageBox (hCodedBlockWnd, "Memory allocation error in text block", "Memory Error", MB_OK);
  822.                 
  823.                    if (NumStatusTexts + 1 > MAX_DECODE_THREADS);
  824.                        // ?????
  825.                 
  826.                    CodingStatusText[NumStatusTexts++] = threadList[num]->statusText;
  827.  
  828.            if (CodingStatusVerbose)
  829.                sprintf(mybuf, "Decoding Status for file %s", (threadList[num]->name[0]!='\0')?threadList[num]->name:threadList[num]->ident);
  830.            else
  831.                strcpy(mybuf, "Decoding Status");
  832.            
  833.            width = xScreen>>1;
  834.            height = (yScreen>>1) - CaptionHeight;
  835.            maxX = 3 * (width>>1);    /* 3/4 screen width  */
  836.            maxY = 3 * (height>>1);    /* 3/4 screen height */
  837.  
  838.            x = (NumStatusTexts * 10) % maxX;
  839.            y = (yScreen>>2) + (NumStatusTexts * CaptionHeight) % maxY;
  840.  
  841.            threadList[num]->statusText->hTextWnd =
  842.             CreateWindow ("WinVnCoding", mybuf,
  843.                 WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
  844.                 x, y, width, height,
  845.                 (HWND)NULL, (HMENU)NULL, hInst, (void far *)NULL);
  846.  
  847.            if (threadList[num]->statusText->hTextWnd == (HWND)NULL)
  848.            {
  849.             FreeTextBlock(threadList[num]->statusText);    
  850.             MessageBox (hCodedBlockWnd, "Couldn't create status text window","Window Creation Error", MB_OK);
  851.             return (FAIL);            
  852.            }
  853.            SetHandleBkBrush (threadList[num]->statusText->hTextWnd,
  854.                       hStatusBackgroundBrush);
  855.            ShowWindow (threadList[num]->statusText->hTextWnd, SW_SHOWNORMAL);
  856.                UpdateWindow (threadList[num]->statusText->hTextWnd);
  857.         }
  858.         else
  859.            threadList[num]->statusText = CodingStatusText[0];
  860.  
  861.     }
  862.     
  863. /* If this new block enlightens us with any useful thread info, update
  864.  * the thread
  865.  */
  866.     if (currentCoded->name[0] != '\0')
  867.         strcpy (threadList[num]->name, currentCoded->name);
  868. //    if (threadList[num]->contentType[0] == '\0')
  869. //        strcpy (threadList[num]->contentType, thisContentType);
  870.     if (threadList[num]->expectedNumBlocks == 0 && thisNumBlocks > 0)
  871.         threadList[num]->expectedNumBlocks = thisNumBlocks;
  872.     
  873.     threadList[num]->totalBytes += currentCoded->numBytes;
  874. /*
  875.  * Now, add this decode object to the thread codedBlockList
  876.  * Insert in correct sequence. 
  877.  * If no sequence info available, add to end of list, but before any end block
  878.  */
  879.     if (DumbDecode)
  880.         InsertBlockInThread(num, currentCoded, threadList[num]->numBlocks);
  881.     else
  882.     {    
  883.       if (threadList[num]->numBlocks != 0 && currentCoded->sequence == -1)
  884.       {    /* sequence unknown, add at end or before end block */
  885.         if (currentCoded->beginFlag)
  886.             InsertBlockInThread(num, currentCoded, 0);
  887.         else 
  888.         {
  889.             if (threadList[num]->codedBlockList[threadList[num]->numBlocks - 1]->endFlag)
  890.                 InsertBlockInThread(num, currentCoded, threadList[num]->numBlocks - 1);
  891.             else            
  892.                 InsertBlockInThread(num, currentCoded, threadList[num]->numBlocks);
  893.         }
  894.       }
  895.       else
  896.       {
  897.         for (i = 0; i < threadList[num]->numBlocks; i++)
  898.         {
  899.             if (threadList[num]->codedBlockList[i]->sequence == -1 || 
  900.                 threadList[num]->codedBlockList[i]->sequence >= currentCoded->sequence)
  901.                 break;
  902.         }
  903.         InsertBlockInThread (num, currentCoded, i);
  904.       }                                                  
  905.     }
  906.     currentDecodeThread = num;         
  907.     if (!CodingStatusVerbose)
  908.         return (SUCCESS);
  909.     
  910.     sprintf (mybuf, "Decoded block");
  911.     if (currentCoded->sequence == -1)
  912.     {
  913.         if (currentCoded->beginFlag || currentCoded->endFlag)
  914.         {
  915.             if (currentCoded->beginFlag)
  916.                 strcat (mybuf, ", begin block");
  917.             if (currentCoded->endFlag)
  918.                 strcat (mybuf, ", end block");
  919.         } else
  920.             strcat (mybuf, " sequence unknown");
  921.     }        
  922.     else
  923.     {
  924.         sprintf (str, " %d", currentCoded->sequence);
  925.         strcat (mybuf, str);
  926.         if (threadList[num]->expectedNumBlocks > 0)
  927.         {
  928.             sprintf (str, " of %d.", threadList[num]->expectedNumBlocks);
  929.             strcat (mybuf, str);
  930.         }
  931.         if (currentCoded->sequence == threadList[num]->numBlocksWritten + 1)
  932.             strcat (mybuf, " In sequence");
  933.         else
  934.             strcat (mybuf, " Out of sequence");
  935.     }
  936.     UpdateThreadStatus (num, mybuf);
  937.     thisNumBlocks = -1;
  938.  
  939.     return (SUCCESS);
  940. }
  941.  
  942. /* ------------------------------------------------------------------------
  943.  *     Insert/delete a block in a thread list
  944.  *    Inserts and deletes will be O(n).  Since n will never be big,
  945.  *    it's not worth the trouble (and space usage) of using a linked list
  946.  *
  947.  *    index should never be -1 (case of unkown sequence handled by caller)
  948.  *    to insert at front of list, index = 0
  949.  *    to insert at end of list, index = threadList[num]->numBlocks
  950.  */
  951. void
  952. InsertBlockInThread (int num, TypCoded *block, int index)
  953. {
  954.     register int i;
  955.     
  956.     for (i = threadList[num]->numBlocks; i > index && i > 0 ; i--)
  957.         threadList[num]->codedBlockList[i] = threadList[num]->codedBlockList[i-1];
  958.      
  959. #ifdef DEBUG_DECODE
  960.     sprintf(debug, "\nInserting into thread %s at posn %d", threadList[num]->ident, i);
  961.     DebugLog(); 
  962. #endif
  963.      threadList[num]->codedBlockList[i] = block;
  964.             
  965.     threadList[num]->numBlocks++;        
  966.  
  967.     return;
  968. }
  969.  
  970. void
  971. DeleteBlockFromThread (int num, int index)
  972. {
  973.     register int i;
  974.     
  975.     for (i = index; i < threadList[num]->numBlocks; i++)
  976.         threadList[num]->codedBlockList[i] = threadList[num]->codedBlockList[i+1];
  977.      
  978. #ifdef DEBUG_DECODE
  979.     sprintf(debug, "\nRemoved from thread %s at posn %d", threadList[num]->name, index);
  980.     DebugLog();
  981. #endif
  982.     threadList[num]->numBlocks--;        
  983.  
  984.     return;
  985. }
  986.  
  987. /* ------------------------------------------------------------------------
  988.  *     Complete a decode block
  989.  *    Adds to thread list
  990.  *    Writes block to disk if possible (and any others now in sequence)
  991.  *    If it ends a thread, make sure everything is written, and
  992.  *    free up the thread
  993.  */
  994. BOOL
  995. CompleteThisDecode ()
  996. {
  997.     char mybuf[MAXINTERNALLINE];
  998.     int num, singleBlockDone, foundEnd;
  999.  
  1000.     UpdateBlockStatus ();    // final size displayed
  1001. #ifdef DEBUG_DECODE
  1002.     sprintf(debug, "\nCompleteThisDecode for file %s, block %d",
  1003.             currentCoded->ident, currentCoded->sequence);
  1004.     DebugLog();
  1005. #endif
  1006.     if (currentCoded->numBytes == 0)
  1007.     {
  1008. #ifdef DEBUG_DECODE
  1009.         sprintf(debug, "\nNon-data block.  Discarding it.");
  1010.         DebugLog();
  1011. #endif
  1012.         DestroyCodedBlock (¤tCoded);
  1013.         return (SUCCESS);
  1014.     }
  1015.     
  1016.     if (DumbDecode)
  1017.     {        
  1018.         if (numDumbDecoded == 0 && !currentCoded->beginFlag)
  1019.             return (SUCCESS); // Dumb skipping a data block
  1020.         if (numDumbDecoded > 0 && currentCoded->beginFlag)
  1021.         {        // found next begin without an end
  1022.             if (CodingStatusVerbose)
  1023.                 sprintf (mybuf, "Decode missing end, total size %ld, wrote to file %s", threadList[num]->totalBytes, threadList[num]->dosFileName);
  1024.             else
  1025.                 sprintf (mybuf, "%s   Decode missing end, total size %ld, wrote to file %s", 
  1026.             threadList[num]->name, threadList[num]->totalBytes, threadList[num]->dosFileName);
  1027.             UpdateThreadStatus (num, mybuf);
  1028.             DestroyThread(num);
  1029.             numDumbDecoded = 0;
  1030.         }
  1031.     }                    
  1032.         else
  1033.     // if for some reason (?) we didn't get a begin, but this is block 1
  1034.     if (currentCoded->sequence == 1 && 
  1035.         currentCoded->seqConfidence == HIGH && 
  1036.         !currentCoded->beginFlag)
  1037.     {
  1038.         currentCoded->beginFlag;
  1039.         strcpy (currentCoded->name, currentCoded->ident);
  1040.     }    
  1041.     
  1042.     if (AddToThreadList () == FAIL)
  1043.         return (FAIL);
  1044.     
  1045.     prevContentEncoding = thisContentEncoding;
  1046.     thisContentEncoding = CODE_UNKNOWN;
  1047.     
  1048.     num = currentDecodeThread;    
  1049.     singleBlockDone = currentCoded->beginFlag && currentCoded->endFlag;
  1050.  
  1051.     if (!CodingStatusVerbose && 
  1052.        (threadList[num]->numBlocksWritten == 0 && threadList[num]->numBlocks == 1))
  1053.     {
  1054.         sprintf (mybuf, "%s   Decode in progress", (threadList[num]->name[0]!='\0')?threadList[num]->name:threadList[num]->ident);
  1055.         UpdateThreadStatus (num, mybuf);
  1056.     }
  1057.     
  1058.     if (DumbDecode)
  1059.     {
  1060.         if (WriteTopBlockToFile (num, &foundEnd) == FAIL)
  1061.             return (FAIL);
  1062.         numDumbDecoded++;
  1063.     }
  1064.         else
  1065.         {
  1066. /* If this block is both begin and end, then go straight to WriteDecodeThread
  1067.  * If currentCoded is in sequence, then it was added as block 0 in the 
  1068.  * thread's block list, and may have caused other blocks to now be in 
  1069.  * sequence as well.  Write all blocks which are in sequence now.
  1070.  */
  1071.       if (!singleBlockDone && currentCoded->sequence != -1 && 
  1072.           currentCoded->seqConfidence != 0)
  1073.         while (threadList[num]->numBlocks != 0)
  1074.         {
  1075.             if (threadList[num]->codedBlockList[0]->sequence == threadList[num]->numBlocksWritten)
  1076.             {    // skip duplicate block sequence
  1077.                 DestroyCodedBlock (¤tCoded);    // threadList[num]->codedBlockList[0]
  1078.                             DeleteBlockFromThread (num, 0);
  1079.                             break;
  1080.                     }
  1081.             if (threadList[num]->codedBlockList[0]->sequence != threadList[num]->numBlocksWritten + 1)
  1082.                 break;
  1083. #ifdef DEBUG_DECODE
  1084.             sprintf(debug, "\nBlock in sequence, writing it, file %s, block %d",
  1085.                 threadList[num]->name, threadList[num]->codedBlockList[0]->sequence);
  1086.             DebugLog();
  1087. #endif
  1088.             if (WriteTopBlockToFile (num, &foundEnd) == FAIL)
  1089.                 return (FAIL);
  1090.          }
  1091.      }    
  1092.     if (singleBlockDone || (foundEnd && DumbDecode) ||
  1093.         (foundEnd && threadList[num]->expectedNumBlocks > 0 && 
  1094.         threadList[num]->numBlocksWritten >= threadList[num]->expectedNumBlocks))
  1095.     {
  1096. #ifdef DEBUG_DECODE
  1097.         sprintf(debug, "\nThread complete: file %s", threadList[num]->name);
  1098.         DebugLog();
  1099. #endif
  1100.            if (WriteDecodeThread(num) == FAIL)
  1101.         {
  1102.             DestroyThread(num);
  1103.             currentCoded = NULL;
  1104.                return (FAIL);
  1105.            }
  1106.  
  1107.         if (CodingStatusVerbose)
  1108.             sprintf (mybuf, "Decode complete, total size %ld, wrote to file %s", threadList[num]->totalBytes, threadList[num]->dosFileName);
  1109.         else
  1110.             sprintf (mybuf, "%s   Decode complete, total size %ld, wrote to file %s", 
  1111.         threadList[num]->name, threadList[num]->totalBytes, threadList[num]->dosFileName);
  1112.         UpdateThreadStatus (num, mybuf);
  1113.  
  1114.         DestroyThread(num);
  1115.         numDumbDecoded = 0;
  1116.     }
  1117.     currentCoded = NULL;
  1118.     return (SUCCESS);
  1119. }            
  1120.             
  1121. /* ------------------------------------------------------------------------
  1122.  *     Write data for the top block of a thread to a file, and remove
  1123.  *    the top block from the thread
  1124.  */
  1125. BOOL
  1126. WriteTopBlockToFile (int num, int *endFlag)
  1127. {
  1128.     char mybuf[MAXINTERNALLINE];
  1129.  
  1130.     if (WriteBlockToFile (num, threadList[num]->codedBlockList[0]) == FAIL)
  1131.     {
  1132.         sprintf (mybuf, "Could not write to file %s", threadList[num]->dosFileName);
  1133.         UpdateThreadStatus (num, mybuf);
  1134.         DestroyThread(num);
  1135.         return (FAIL);
  1136.     }
  1137.     
  1138.     *endFlag = threadList[num]->codedBlockList[0]->endFlag;
  1139.     DestroyCodedBlock (&(threadList[num]->codedBlockList[0]));
  1140.     DeleteBlockFromThread (num, 0);
  1141.     threadList[num]->numBlocksWritten++; 
  1142.     return (SUCCESS);
  1143. }
  1144.     
  1145. /* ------------------------------------------------------------------------
  1146.  *     Write data for a thread to a file
  1147.  */
  1148. BOOL
  1149. WriteDecodeThread (int num)
  1150. {
  1151.     register int i;
  1152.     char mybuf[MAXINTERNALLINE];
  1153.         
  1154.     for (i = 0; i < threadList[num]->numBlocks; i++)
  1155.     {
  1156. #ifdef DEBUG_DECODE
  1157.         sprintf(debug, "\nSaving sequence %d", threadList[num]->codedBlockList[i]->sequence);
  1158.         DebugLog();
  1159. #endif
  1160.         if (WriteBlockToFile (num, threadList[num]->codedBlockList[i]) == FAIL)
  1161.         {
  1162.             sprintf (mybuf, "Could not write to file %s", threadList[num]->dosFileName);
  1163.             UpdateThreadStatus (num, mybuf);
  1164.             return (FAIL);
  1165.         }
  1166.     }
  1167.     if (ExecuteDecodedFiles)
  1168.         ExecuteDecodedFile(num, threadList[num]->dosFileName);
  1169.  
  1170.     return (SUCCESS);
  1171. }
  1172.  
  1173. /* ------------------------------------------------------------------------
  1174.  *     Write block data to a file
  1175.  *    Verify thread file name will work in DOS, and request a new file
  1176.  *    name if necessary
  1177.  *     Accepts handle of parent window to allow any necessary dialogs
  1178.  */
  1179. BOOL
  1180. WriteBlockToFile(int num, TypCoded *thisDecode)
  1181.     register unsigned long i;
  1182.     unsigned int chunkSize;
  1183.     char    actualFileName[MAXFILENAME], mybuf[MAXINTERNALLINE];
  1184.         OFSTRUCT outFileStruct;
  1185.     HFILE     hWriteFile;
  1186.     UINT    fileMode;
  1187.  
  1188. #define    CHUNK_SIZE 65500    /* must not exceed UINT (65534) */        
  1189.     
  1190.     if (threadList[num]->dosFileName[0] != '\0') {
  1191.         fileMode = OF_WRITE;
  1192.         if (strchr (threadList[num]->dosFileName, '\\') == NULL)
  1193.         {
  1194.             strcpy (actualFileName, DecodePathName);
  1195.             if (actualFileName[strlen(actualFileName)-1] != '\\')
  1196.                 strcat(actualFileName, "\\");
  1197.             strcat(actualFileName, threadList[num]->dosFileName);
  1198.             }
  1199.         else
  1200.            strcpy (actualFileName, threadList[num]->dosFileName);
  1201.     }
  1202.     else 
  1203.     {
  1204.     /* First time write to file
  1205.      */
  1206.         fileMode = OF_CREATE;
  1207.                 
  1208.                 actualFileName[0] = '\0';
  1209.         if (threadList[num]->name[0] != '\0')
  1210.             strcpy (threadList[num]->dosFileName, threadList[num]->name);
  1211.         else if (threadList[num]->ident[0] != '\0')
  1212.             strcpy (threadList[num]->dosFileName, threadList[num]->ident);
  1213.         if (threadList[num]->dosFileName[0] != '\0')
  1214.         {
  1215.             strcpy (actualFileName, DecodePathName);
  1216.             if (actualFileName[strlen(actualFileName-1)] != '\\')
  1217.                 strcat(actualFileName, "\\");
  1218.             strcat(actualFileName, threadList[num]->dosFileName);
  1219.         }
  1220.  
  1221.         /* smart filer work */
  1222.         if (SmartFile(hCodedBlockWnd, actualFileName) == FAIL)
  1223.             return (FAIL);
  1224.  
  1225.         strcpy (threadList[num]->dosFileName, actualFileName);
  1226.     }
  1227.         
  1228.         if ((hWriteFile = OpenFile ((char far *) actualFileName, &outFileStruct, fileMode)) < 0)
  1229.     {
  1230.         MessageBox(hCodedBlockWnd, "Unable to open output file", "File Error", MB_OK|MB_ICONSTOP);
  1231.         return (FAIL);
  1232.         }
  1233.         if (fileMode == OF_WRITE)
  1234.             _llseek (hWriteFile, 0L, 2);    /* append */
  1235.             
  1236.     for (i = 0L; i < thisDecode->numBytes; i += chunkSize)
  1237.     {
  1238.                if (i + CHUNK_SIZE > thisDecode->numBytes)
  1239.                    chunkSize = (unsigned int) (thisDecode->numBytes - i);
  1240.                else
  1241.                    chunkSize = CHUNK_SIZE;
  1242.  
  1243.                if (_lwrite(hWriteFile, &(thisDecode->data[i]), chunkSize) != chunkSize)
  1244.         {
  1245.             MessageBox(hCodedBlockWnd, "Error writing to file", "File Error", MB_OK|MB_ICONSTOP);
  1246.                    _lclose(hWriteFile);
  1247.             return (FAIL);
  1248.             }
  1249.         }
  1250.  
  1251.     if (CodingStatusVerbose)
  1252.     {
  1253.         strcpy (mybuf, "     Wrote block ");
  1254.         if (thisDecode->sequence > 0)
  1255.         {
  1256.             sprintf (str, "%d ", thisDecode->sequence);
  1257.             strcat (mybuf, str);
  1258.         }
  1259. //        sprintf (str, "to file %s", threadList[num]->dosFileName);
  1260. //        strcat (mybuf, str);
  1261.         UpdateThreadStatus (num, mybuf);
  1262.     }
  1263.            _lclose(hWriteFile);
  1264.     return (SUCCESS);
  1265. }
  1266.  
  1267. /* ------------------------------------------------------------------------
  1268.  *     Decode a line in article.  Store in thisDecode
  1269.  */
  1270. int  
  1271. DecodeLine(TypCoded *thisDecode, char *line)
  1272. {
  1273.     register int i;
  1274.     static int table_count;
  1275.     char thisBlockName[MAXINTERNALLINE];
  1276.          int mode;
  1277.  
  1278.     if (strlen(line) == 0)    
  1279.         return (SUCCESS);
  1280.  
  1281.     thisDecode->numLines++;
  1282.     if (thisDecode->numLines % STATUS_UPDATE_FREQ == 0)
  1283.         UpdateBlockStatus ();
  1284.     
  1285.     switch (CodingState)
  1286.     {
  1287.     case DECODE_SKIPPING: 
  1288.         if (strlen(line) < 3)    // if we're skipping, ignore any really short line
  1289.             return (SUCCESS);
  1290.  
  1291.         // encoded UU/XX/Custom data always starts with line 
  1292.         // like 'begin <mode> <file-name>'
  1293.         if (!strncmp (line, "begin", 5) &&
  1294.             sscanf (line, "%*s %d %s", &mode, thisBlockName) == 2)
  1295.                 {
  1296. #ifdef DEBUG_DECODE
  1297.             sprintf(debug, "\nStart of new encoded file %s, mode %d", thisBlockName, mode);
  1298.             DebugLog();
  1299. #endif
  1300.             if (thisBlockName[0] != '\0')
  1301.                 strcpy (thisDecode->name, strlwr(thisBlockName));
  1302.                         else
  1303.                 strcpy (thisDecode->name, thisDecode->ident);
  1304.                                     
  1305.             thisDecode->sequence = 1;
  1306.             thisDecode->beginFlag = TRUE;
  1307. //            thisDecode->mode = mode;
  1308.  
  1309.             CodingState = DECODE_PROCESSING;
  1310.             return (SUCCESS);
  1311.         }     
  1312. /* Check for keywords in the non-data lines which may aid in describing
  1313.  * the name for this data, the sequence, a custom table, etc
  1314.  */
  1315.          else if (!DumbDecode && !_strnicmp(line, "subject:", 8))
  1316.              ParseInfoLine (thisDecode, &line[8], TRUE);
  1317.          else if (!DumbDecode && !_strnicmp(line, "summary:", 8))
  1318.             ParseInfoLine (thisDecode, &line[8], FALSE);
  1319.          else if (!DumbDecode && ParseAppSpecificLine (thisDecode, line))
  1320.             return (SUCCESS);
  1321.          else if (!strncmp(line, "table", 5))
  1322.          {    // next two lines are table
  1323.              CodingState=DECODE_GET_TABLE;
  1324.             table_count = 0;
  1325.              return (SUCCESS);
  1326.          }
  1327.                      
  1328. /*        else if (ParseMimeHeaderLine(thisDecode, line) == SUCCESS)
  1329.             return (SUCCESS);
  1330. */
  1331. /* Skipping to find new block of data.
  1332.  * Skip until a data line (a line with the correct length) is found
  1333.  */        
  1334.          else if (IsDataLine(line))
  1335.         {   
  1336. #ifdef DEBUG_DECODE
  1337.             sprintf(debug, "\nFound start of next data section", line);
  1338.             DebugLog();
  1339. #endif
  1340.                          CodingState = DECODE_PROCESSING;
  1341.                            // found good line, process it and continue processing
  1342.                            return (DecodeDataLine(thisDecode, line));
  1343.         }
  1344. #ifdef DEBUG_DECODE
  1345.         sprintf(debug, "\nskipped: %s", line);
  1346.         DebugLog();
  1347. #endif
  1348.         return (SUCCESS);        // continue skipping 
  1349.  
  1350.     case DECODE_GET_TABLE:
  1351.     // This is one of two lines containing table info
  1352.         memmove ((customTable + table_count * 32), line, 32);
  1353.  
  1354.         if (++table_count == 2)
  1355.         {
  1356. #ifdef DEBUG_MAP
  1357.             strncpy (str, customTable, 64);
  1358.             str[64]='\0';
  1359.             sprintf(debug, "\nfound table: %s", str);
  1360.             DebugLog();
  1361. #endif
  1362.             CodingState = DECODE_SKIPPING;
  1363.             if (i = CreateCodingMap (CodingMap[CODE_CUSTOM], customTable) != -1)
  1364.             {
  1365. #ifdef DEBUG_MAP
  1366.                 sprintf (debug, "Invalid decoding table in block.  Duplicate character %c.",  i);
  1367.                 DebugLog();
  1368. #endif
  1369.                 customTable[0]='\0';    // ditch the table
  1370.             } 
  1371.             else
  1372.                 thisContentEncoding = CODE_CUSTOM;
  1373.         }
  1374.         return (SUCCESS);
  1375.             
  1376.     case DECODE_PROCESSING:
  1377. /* Some encoders place an END line at end of a section, and length it like 
  1378.  * a proper encoded line
  1379.  * Make sure we catch it, don't decode it as data, and switch back to skipping
  1380.  * Note the only End statement which means End of entire file is lower case
  1381.  * "end".  The other tags are ignored, i.e. "END", "End_of_section", etc
  1382.  */
  1383.         if (!_strnicmp (line, "end", 3) || !IsDataLine(line) ||
  1384.            (bossanovaMIME && !strcmp (&line[2], thisBoundary))) // skip '--'
  1385.         {
  1386. #ifdef DEBUG_DECODE    
  1387.             sprintf(debug, "\nSwitching back to skipping\nskipped: %s", line);
  1388.             DebugLog();
  1389. #endif
  1390.             CodingState = DECODE_SKIPPING;
  1391.  
  1392. /* If we are switching back to skipping and haven't really received any
  1393.  * data yet (< about a line's worth), then the line which gave us the coding type
  1394.  * was bogus (regular text line where first char happened to = encoded line 
  1395.  * length, Here's one: 'From: marnold@cwis.unomaha.edu (Matthew Eldon Arnold)'
  1396.  * Ditch all data so far and the current table  and reprocess
  1397.  * this line.  Note if we have a custom table, then we trust it.
  1398.  */
  1399.             if (thisContentEncoding != CODE_CUSTOM && thisDecode->numBytes < 80)
  1400.             {
  1401.                 thisDecode->numBytes = 0;
  1402.                 thisContentEncoding = CODE_UNKNOWN;
  1403. #ifdef DEBUG_DECODE    
  1404.                 sprintf(debug, "\nBogus table!  Scrapping so far, reprocessing line: %s", line);
  1405.                 DebugLog();
  1406. #endif
  1407.                 thisDecode->numLines--;
  1408.                 return (DecodeLine (thisDecode, line));
  1409.             }
  1410.             strcpy (prevBlockIdent, thisDecode->ident);
  1411.         
  1412.                    if (!strncmp (line, "end", 3))
  1413.             {
  1414.                 thisDecode->endFlag = TRUE;
  1415.                 if (thisNumBlocks == -1 && thisDecode->sequence != -1)
  1416.                     thisNumBlocks = thisDecode->sequence;
  1417. #ifdef DEBUG_DECODE
  1418.                 sprintf(debug, "\nFound end of file %s", thisDecode->ident);
  1419.                 DebugLog();
  1420. #endif
  1421.             }                    
  1422.                 return (END_BLOCK);
  1423.         }
  1424.                   return (DecodeDataLine(thisDecode, line));
  1425.     }
  1426. }
  1427.  
  1428. /* ------------------------------------------------------------------------
  1429.  *  4 to 3 line decoder
  1430.  */
  1431. BOOL    DecodeDataLine (TypCoded *thisDecode, char *line)
  1432. {
  1433.     register unsigned int i, j;
  1434.     unsigned int decodedCount, numEncoded, numDecoded, checkSum, startNum, stop;
  1435.     unsigned char buf[4], outLine[120];
  1436.     char *thisMap = CodingMap[thisContentEncoding];
  1437.     
  1438.     switch (thisContentEncoding)
  1439.     {
  1440.     case CODE_QP:
  1441.        // not implemented yet
  1442.     return (FAIL);
  1443.     
  1444.     case CODE_UU:
  1445.     case CODE_XX:
  1446.     case CODE_CUSTOM:
  1447.     // these are 4-to-3 decodings which have the line length encoded
  1448.     // as the first char (count is # of chars after decoding)
  1449.     checkSum = decodedCount = thisMap[line[0]];
  1450.     numEncoded = 4 * ((thisMap[line[0]] + 2) / 3);    // don't include count char
  1451.     startNum = 1;
  1452.     break;
  1453.     case CODE_BASE64:
  1454.     // base64 is 4-to-3 decoding with nonexplicit line lengths 
  1455.     numEncoded = strlen(line);
  1456.     decodedCount = 3 * (numEncoded / 4);
  1457.     startNum = 0;
  1458.     break;
  1459.     }    
  1460.     for (i = startNum, numDecoded=0; numDecoded < decodedCount;)
  1461.     {
  1462.     // Get the next group of four characters 
  1463.     // Handle last group of characters in a base64 encoding specially -
  1464.     // padding '=' at end means we have fewer than 24 bits after decoding
  1465.     // Base64 end scenarios:
  1466.     //    'xx==' decodes to 8 bits
  1467.     //    'xxx=' decodes to 16 bits
  1468.     //    'xxxx' decodes to 24 bits
  1469.     //    'x===' can't happen
  1470.     stop = min(4, numEncoded - i + 1);
  1471.     for (j = 0; j < stop; j++, i++)
  1472.     {       
  1473.         if (thisContentEncoding == CODE_BASE64 && line[i] == '=')
  1474.         {
  1475.             buf[j] = 0;
  1476.             j--;
  1477.             break;
  1478.         }
  1479.         else
  1480.             buf[j] = thisMap[line[i]];
  1481.         checkSum += buf[j]; 
  1482.     }
  1483.     outLine[numDecoded++] = buf[0]<<2|buf[1]>>4;
  1484.     if (j == 1) break;
  1485.     outLine[numDecoded++] = buf[1]<<4|buf[2]>>2;
  1486.     if (j == 2) break;
  1487.     outLine[numDecoded++] = buf[2]<<6|buf[3];
  1488.     }
  1489.     if (numDecoded && AddDataToBlock (currentCoded, outLine, numDecoded) == FAIL)
  1490.     return (FAIL);
  1491.  
  1492. /*    if (checkSum%64 != line[i])
  1493.     {
  1494.         printf("\nChecksum error: %d vs. %d", checkSum%64, line[i]);
  1495.         break;
  1496.     } else
  1497.         printf("\nChecksum ok: %c vs. %c", checkSum%64, line[i]);
  1498. */
  1499.     return (SUCCESS);
  1500. }
  1501.  
  1502. /* ------------------------------------------------------------------------
  1503.  * AddDataToBlock is called on a line-by-line basis.
  1504.  * dataLen should rarely be > 100 bytes
  1505.  * The data is type huge, but it's just pointing to a string of chars, so
  1506.  * the size does not have to be a power of 2
  1507.  */
  1508. BOOL
  1509. AddDataToBlock (TypCoded *thisDecode, char *newData, unsigned int dataLen)
  1510. {
  1511.     if (thisDecode->numBytes + (unsigned long) dataLen > thisDecode->maxBytes)
  1512.     { 
  1513.             thisDecode->maxBytes += BASE_BLOCK_SIZE;
  1514. #ifdef DEBUG_MEM
  1515.         sprintf(debug, "\nReallocing data to %ld", thisDecode->maxBytes);
  1516.         DebugLog();
  1517. #endif
  1518.         if ((thisDecode->data = (char huge *) GlobalReAllocPtr (thisDecode->data, thisDecode->maxBytes, GMEM_MOVEABLE)) == NULL)    
  1519.             return (FALSE);
  1520.     }
  1521.     memmove (thisDecode->data + thisDecode->numBytes, newData, dataLen);
  1522.     thisDecode->numBytes += dataLen;
  1523.         return (SUCCESS);
  1524. }     
  1525.         
  1526. /* ------------------------------------------------------------------------
  1527.  *     Determine if the line is a data line
  1528.  */
  1529. BOOL
  1530. IsDataLine (char *line)
  1531. {
  1532.     if (thisContentEncoding == CODE_UNKNOWN)
  1533.     {
  1534. /* If this block has same ident as prev block, use same decode table
  1535.  * If this block has same ident as any existing threads, use same decode table
  1536.  * as matching thread
  1537.  * Otherwise, test line for UU, XX, or Base64, returning if find a fit
  1538.  */            
  1539.         if (currentCoded->ident[0] != '\0' && 
  1540.             !strcmp (currentCoded->ident, prevBlockIdent))
  1541.         {
  1542.             thisContentEncoding = prevContentEncoding;
  1543. #ifdef DEBUG_MAP
  1544.             strcpy (debug, "\nUsing same table as prev decode block");
  1545.             DebugLog();
  1546. #endif
  1547.         }
  1548.         else
  1549.             if ((thisContentEncoding = ThreadTable (customTable, currentCoded->ident)) != CODE_UNKNOWN)
  1550.             {
  1551.                if (thisContentEncoding == CODE_CUSTOM)
  1552.                 CreateCodingMap (CodingMap[CODE_CUSTOM], customTable);
  1553. #ifdef DEBUG_MAP
  1554.                 strcpy (debug, "\nUsing stored thread table");
  1555.                 DebugLog();
  1556. #endif
  1557.             }
  1558.             else
  1559.             {                
  1560.                 thisContentEncoding = CODE_UU;
  1561.                 if (TestDataLine (line))
  1562.                 {
  1563. #ifdef DEBUG_MAP
  1564.                     sprintf (debug, "\nUsing UU table: %s", line);
  1565.                     DebugLog();
  1566. #endif
  1567.                     return (TRUE);
  1568.                 }
  1569.                 thisContentEncoding = CODE_XX;
  1570.                 if (TestDataLine (line))
  1571.                 {
  1572. #ifdef DEBUG_MAP
  1573.                     sprintf (debug, "\nUsing XX table: %s", line);
  1574.                     DebugLog();
  1575. #endif
  1576.                     return (TRUE);
  1577.                 }
  1578.                 thisContentEncoding = CODE_BASE64;
  1579.                 if (TestDataLine (line))
  1580.                 {
  1581. #ifdef DEBUG_MAP
  1582.                     sprintf (debug, "\nUsing Base64 table: %s", line);
  1583.                     DebugLog();
  1584. #endif
  1585.                     return (TRUE);
  1586.                 }
  1587.                 thisContentEncoding = CODE_UNKNOWN;
  1588.                 return (FALSE);
  1589.             }
  1590.     }
  1591.     return (TestDataLine(line));
  1592. }
  1593.         
  1594. BOOL
  1595. TestDataLine (char *line)
  1596. {        
  1597.    unsigned int expectedLineLen, dataLen, lineLen;
  1598.    register unsigned int i;
  1599.    char *thisMap;
  1600.    
  1601.    thisMap = CodingMap[thisContentEncoding];
  1602.    lineLen = strlen(line);
  1603.    switch (thisContentEncoding)
  1604.    {
  1605.    case CODE_UU:
  1606.    case CODE_XX:
  1607.    case CODE_CUSTOM:
  1608. /*
  1609.  * The first char of a good data UU/XX/Custom line is the encoded character
  1610.  * count for the line.  
  1611.  * example M = ascii 77, decodes to 45.  This line will decode to 45 characters,
  1612.  * so the # encoded chars should be (int)(4 * ((n+2) / 3)) - (round up and re-encode)
  1613.  * So if line[0] is 'M', there should be 60 characters on the line
  1614.  * The count does not include the first count char, so the expected line 
  1615.  * length is count+1 (i.e. 61). 
  1616.  * If the line length is actually 2 + count, then we have a checksum (?)
  1617.  */ 
  1618.     expectedLineLen = 4 * ((thisMap[line[0]] + 2) / 3) + 1;
  1619.  
  1620.     if (lineLen < expectedLineLen)
  1621.          return (FALSE);
  1622. /*
  1623.  * Count the number of encoded data characters on the line (up to any whitespace)
  1624.  * (Some encoders pad with spaces at the end).  Don't forget, a space may be
  1625.  * a valid encoded char, so a proper line may very well end with a space.
  1626.  * Don't blindly remove all trailing white space!
  1627.  */        
  1628.      for (dataLen = lineLen; 
  1629.           isspace (line[dataLen-1]) && dataLen > 0 && dataLen != expectedLineLen; 
  1630.           --dataLen);
  1631.      
  1632.      // May have a checksum character, so allow for one extra char
  1633.      if (expectedLineLen != dataLen && expectedLineLen+1 != dataLen)
  1634.          return (FALSE);
  1635.      
  1636.      line[dataLen] = '\0';    // permanently chop off the white space
  1637.      // It's the right length, now check content for match w/ table
  1638.     for (i = 0; i < expectedLineLen; i++)
  1639.         if (thisMap[line[i]] == UNUSED)
  1640.             return (FALSE);
  1641.     return (TRUE);
  1642.  
  1643.     case CODE_BASE64:
  1644.     // permanently remove all trailing space
  1645.     while (lineLen > 0 && isspace (line[lineLen-1]))
  1646.         line[--lineLen] = '\0';
  1647.      
  1648.      // for base 64, just check if all chars in coding map (allow pad '=')
  1649.     for (i = 0; i < lineLen; i++)
  1650.         if (thisMap[line[i]] == UNUSED && line[i] != '=')
  1651.             return (FALSE);
  1652.     return (TRUE);
  1653.  
  1654.     case CODE_QP:
  1655.     // not implemented yet
  1656.     default:
  1657.         return (FALSE);
  1658.     }        
  1659. }                               
  1660.  
  1661. /* ------------------------------------------------------------------------
  1662.  *     Mappings for encoding/decoding
  1663.  */
  1664. int
  1665. CreateCodingMap(char *map, char *table)
  1666. {
  1667.     register unsigned int i;
  1668.     
  1669.     for (i=0; i < 128; i++)
  1670.         map[i] = UNUSED;
  1671.     
  1672.     // if you see a table[i] character, decode it to i
  1673.     // i.e. UU, table[1] is a '!'.  So a '!' should decode to 1
  1674.     
  1675.     for (i = 0; i < CODINGTABLESIZE; i++)
  1676.             if (map[table[i]] != UNUSED)
  1677.                 return (table[i]);    // table[i] is duplicate 
  1678.         else                
  1679.             map[table[i]]=i;
  1680.     
  1681. #ifdef DEBUG_MAP2
  1682.     sprintf(debug, "\nCoding Map:\n"); DebugLog();
  1683.     for (i=0; i < 128; i++)
  1684.     {
  1685.         sprintf(debug, "%d ",map[i]);
  1686.         DebugLog();
  1687.     } 
  1688. #endif
  1689.     return (-1);            
  1690. }
  1691.  
  1692. int   
  1693. ThreadTable (char *dest, char *ident)
  1694. {
  1695.     int num;
  1696.     register int i;
  1697.     
  1698.     for (num = -1, i = 0; i < numDecodeThreads; i++)
  1699.         if (!_stricmp (ident, threadList[i]->ident))
  1700.             num = i;
  1701.  
  1702.     if (num == -1)        // no thread by that name 
  1703.         return (CODE_UNKNOWN);
  1704.     else
  1705.     {
  1706.         if (threadList[num]->contentEncoding == CODE_CUSTOM)
  1707.             strncpy (dest, threadList[num]->customTable, CODINGTABLESIZE);
  1708.         return (threadList[num]->contentEncoding);
  1709.     }
  1710. }
  1711. #if 0
  1712. void
  1713. CreateUUTable()
  1714. {
  1715.     register unsigned int i;
  1716.  
  1717.     for (i=0; i < CODINGTABLESIZE; i++)
  1718.         uuTable[i] = i + 32;
  1719.     
  1720.     uuTable[0] = 96;        /* set space to map to back-quote */    
  1721. }
  1722. #endif
  1723.  
  1724. /* ------------------------------------------------------------------------
  1725.  *    Search through a subject: or BEGIN line 
  1726.  *    (for a Subject: line,  "Subject:" has already been removed).
  1727.  *
  1728.  *    Attempt to obtain a block number, and a total # of blocks
  1729.  *    Generate an identifier based on the subject, preferably based on the
  1730.  *    actual file name.  If guessIdent is TRUE, choose an ident even if
  1731.  *    it doesn't look like a fileName.  If guessIdent is FALSE, only fill
  1732.  *    ident if we see a fileName (word containing a dot).
  1733.  *
  1734.  *    Note: at this point, we haven't seen any MIME header, and we're not
  1735.  *    really expecting one.  So now is the time to get what we can
  1736.  *
  1737.  *    Common Subject styles which this function deals with:
  1738.  *    filename.ext 1/2 comment
  1739.  *    filename.ext(1/2)comment
  1740.  *    filename.ext (1/2) comment
  1741.  *    filename.ext [1/2] comment
  1742.  *    filename.ext 1 of 2 comment
  1743.  *    filename.ext part 1 of 2 comment
  1744.  *    filename.ext 1 of 2 comment
  1745.  *    This is part 1/2 of filename.ext
  1746.  *
  1747.  *    Also handles the following common BEGIN lines:
  1748.  *    BEGIN --- CUT HERE --- Cut Here --- cut here --- abcd.efg n/N
  1749.  *     BEGIN --- CUT HERE --- Cut Here --- cut here --- abcd.efg
  1750.  *    BEGIN-------cut here-------CUT HERE-------PART n               // This is TIN
  1751.  *    BEGIN -- CUT HERE -- cut here -- abcd.efg part n of N
  1752.  *    BEGIN---CUT HERE---BEGIN---CUT HERE---BEGIN PART n/N
  1753.  *
  1754.  *    For subject lines beginning with the comment, or free text, the
  1755.  *    name is harder to guess.  In these cases, prefer a word containing
  1756.  *    a dot (filename.ext), or if none, just take the first word.  Scan
  1757.  *    any existing threads to see if any of their identifiers matches.
  1758.  *    For example,
  1759.  *    another encoded file: filename.ext (1/2)
  1760.  *    testing encoded files (1/2)    i.e. ident would be testing
  1761.  *
  1762.  *     Known to be incorrectly handled by this function:
  1763.  *    filename.ext 3.4 (1/2)        i.e. name w/ version number
  1764.  *    filename.ext 001        i.e. sequence w/ no # parts
  1765.  *    filename.ext1            i.e. part number appended to file name
  1766.  */ 
  1767. #define SEEK_PARTNUM    1
  1768. #define SEEK_NUMPARTS    2
  1769. #define IGNORE_NUMBERS    3
  1770. void
  1771. ParseInfoLine (TypCoded *thisDecode, char *line, BOOL guessIdent)
  1772. {
  1773.     char tok[MAXINTERNALLINE], next[MAXINTERNALLINE];
  1774.     char guessName[MAXINTERNALLINE];
  1775.     char *ptr, *thisTok, *tmp;
  1776.     int  numberMode;
  1777.     
  1778.     numberMode = SEEK_PARTNUM;
  1779.     ptr = line;
  1780.         
  1781.     guessName[0] = '\0';
  1782.         while (*ptr)
  1783.         {
  1784.             if (ReadSubjectToken (thisTok = tok, &ptr) == NULL)
  1785.                 break;
  1786.         
  1787. //    NUMBERS
  1788.         if (numberMode != IGNORE_NUMBERS)
  1789.            if (isnumber (thisTok))
  1790.            {
  1791.             if (numberMode == SEEK_PARTNUM)
  1792.             {
  1793.                 thisDecode->sequence = atoi (thisTok);
  1794.                 thisDecode->seqConfidence = LOW;
  1795.  
  1796.                 if (ReadSubjectToken (next, &ptr) == NULL)
  1797.                     break;
  1798.                 if (!_stricmp (next, "/") ||
  1799.                     !_stricmp (next, "of"))
  1800.                 {
  1801.                     numberMode = SEEK_NUMPARTS;        
  1802.                     continue;
  1803.                 }
  1804.                 else    // no numParts immediately following
  1805.                     thisTok = next;    // process the next tok
  1806.             }
  1807.             else    // numberMode == SEEK_NUMPARTS
  1808.             {
  1809.                 thisNumBlocks = atoi (tok);
  1810.                 numberMode = IGNORE_NUMBERS;
  1811.                 thisDecode->seqConfidence = HIGH;
  1812.                     continue;
  1813.             }
  1814.            }
  1815.            else if (numberMode == SEEK_NUMPARTS)
  1816. //           We had '# of non#', a red herring, seek again
  1817.             numberMode == SEEK_PARTNUM;
  1818.             
  1819. //    WORDS
  1820.         // Prefer a word containing a dot for the ident (but not a period
  1821.         // at the end of a sentence
  1822.             if ((tmp = strchr (thisTok, '.')) && isalpha(*(tmp+1)))
  1823.             strcpy (thisDecode->ident, thisTok);
  1824.             else if (guessIdent)
  1825.             {
  1826.                 // skip if already have name w/ confidence
  1827.                 if (thisDecode->ident[0] == '\0')
  1828.             {
  1829.             // check if any threads have this token as name
  1830.                    if (SearchThreadNames (thisTok))
  1831.                     strcpy (thisDecode->ident, thisTok);
  1832.  
  1833.             // Save first word found for thread name
  1834.             // prefer ident not starting with 're'
  1835.                    else if (guessName[0] == '\0' || !_strnicmp(guessName, "re", 2))
  1836.                     strcpy (guessName, thisTok);
  1837.                 }
  1838.             }
  1839.     }
  1840.     if (guessIdent && thisDecode->ident[0] == '\0')
  1841.         strcpy (thisDecode->ident, guessName);
  1842.  
  1843. #ifdef DEBUG_DECODE
  1844.     sprintf(debug, "\nSubject header: name %s, part number %d (confidence %d), num parts %d", 
  1845.         thisDecode->ident, thisDecode->sequence, thisDecode->seqConfidence, thisNumBlocks);
  1846.     DebugLog();
  1847. #endif
  1848. }
  1849.  
  1850. char *
  1851. ReadSubjectToken (char *dest, char **ptr)
  1852. {
  1853.     int len;
  1854.     register int i;
  1855.     char *str = *ptr;
  1856.     
  1857.     if (*str == '\0')
  1858.         return ('\0');
  1859.         
  1860.     len = strcspn (str, " ()[]\\/:,\"'`{};\n\r");
  1861.  
  1862. /*    Ignore (skip) all of these delimiters except '/'
  1863.  *    Return '/' as a token of length 1
  1864.  */                      
  1865.     if (len == 0)
  1866.         if (*str == '/')
  1867.             len = 1;
  1868.         else
  1869.         {    // skip this delimiter
  1870.             (*ptr)++;
  1871.             return (ReadSubjectToken (dest, ptr));
  1872.         }
  1873.  
  1874.         for (i = 0; i < len; i++)
  1875.             dest[i] = tolower(str[i]);
  1876.         dest[len] = '\0';
  1877.  
  1878.     *ptr = str + len;    // increment line ptr    
  1879. }
  1880.  
  1881. BOOL
  1882. SearchThreadNames (char *name)
  1883. {
  1884.     register int i;
  1885.     int len;
  1886.     
  1887.     len = strlen (name);
  1888.  
  1889.     for (i = 0; i < numDecodeThreads; i++)
  1890.         if (!_strnicmp (name, threadList[i]->ident, len))
  1891.             return (TRUE);
  1892.  
  1893.     return (FALSE);
  1894. }
  1895.  
  1896. /* ParseAppSpecificLine
  1897.  * returns TRUE if it handles the line, else FALSE
  1898.  *
  1899.  * Example info lines handled by this function:
  1900.  *    part=n
  1901.  *    file=abc.def
  1902.  *    pfile=xyz.abc 
  1903.  *    Archive-name: fileident/part0n        // no extension included for filename
  1904.  *
  1905.  *     section N of uuencode 5.10 of file abcd.efg   by R.E.M.
  1906.  *    section n/N   file abcd.efg   [ Wincode v2.3 ]
  1907.  *    [ Section: n/N  File: abcd.efg  Encoder: Wincode v1.4 ]
  1908.  *    section n/N abcd.efg  [EnUU 2.1]
  1909.  *  abcd.efg    section  n/N   UUXFER ver 2.0 by David M. Read
  1910.  *    POST V2.0.0 abcd.efg (Part n/N)
  1911.  */
  1912. BOOL
  1913. ParseAppSpecificLine (TypCoded *thisDecode, char *line)
  1914. {
  1915.     char *orig, *ptr;
  1916.     int seq, num, len;
  1917.     float ver;
  1918.     char name[MAXINTERNALLINE];
  1919.     char copy[MAXINTERNALLINE];
  1920.     
  1921.        for (orig = line; *orig && isspace(*orig); orig++);    // skip leading spaces
  1922.     
  1923.     if (*orig == '\0')            // blank line (or all spaces)
  1924.             return (SUCCESS);
  1925.  
  1926.     len = min(MAXINTERNALLINE-1, strlen(orig));
  1927.     strncpy (copy, orig, len);    // make a lower-case copy
  1928.     copy[len] = '\0';
  1929.     strlwr(copy);
  1930.     
  1931.     if (!strncmp(orig, "BEGIN", 5))    // specifically comparing case-sensitive
  1932.     {
  1933.         ParseInfoLine(thisDecode, copy, FALSE);
  1934.         return (TRUE);
  1935.     }
  1936.  
  1937.     if (sscanf(copy, "file=%s", name) == 1)
  1938.     {
  1939.         strcpy (thisDecode->ident, name);
  1940.         return (TRUE);
  1941.     }
  1942.  
  1943.     if (*copy == 'p')    // info headers starting with 'p'
  1944.     {
  1945.         if (sscanf(copy, "part=%d", &seq) == 1)
  1946.         {
  1947.             thisDecode->sequence = seq;
  1948.             if (!thisDecode->seqConfidence)
  1949.                 thisDecode->seqConfidence = LOW; // still don't have number of parts
  1950.             return (TRUE);
  1951.         }
  1952.         if (sscanf(copy, "pfile=%s", name) == 1)
  1953.         {
  1954.             strcpy (thisDecode->ident, name);
  1955.             return (TRUE);
  1956.         }
  1957.         /* POST */
  1958.         if (sscanf (copy, "POST V%*s %s (Part %d/%d)", name, &seq, &num) == 3)
  1959.         {
  1960.             thisDecode->sequence = seq;
  1961.             thisNumBlocks = num;
  1962.             thisDecode->seqConfidence = HIGH;
  1963.             strcpy (thisDecode->ident, name);
  1964.             return (TRUE);
  1965.         }
  1966.         return (FALSE);    // started with 'p' but we didn't have a template
  1967.     }
  1968.  
  1969.     /* note some of these look for a version number, then don't use it.  the
  1970.        version number is scanned simply to add to our confidence that this is
  1971.        a valid info line.  we want it to be included in the count - so we know
  1972.        if the line fits the template (hence don't use %*f)
  1973.     */
  1974.  
  1975.     /* R.E.M., EnUU, and WinCode v2.x all start with "section " */
  1976.     if (!strncmp(copy, "section", 7))
  1977.     {
  1978.         ptr = ©[7];
  1979.         /* R.E.M. */
  1980.         if (sscanf (ptr, " %d of uuencode %f of file %s", &seq, &ver, name) == 3)
  1981.         {    
  1982.             thisDecode->sequence = seq;
  1983.             if (!thisDecode->seqConfidence)
  1984.                 thisDecode->seqConfidence = LOW; // still don't have number of parts
  1985.                     
  1986.             strcpy (thisDecode->ident, name);
  1987.             thisContentEncoding = CODE_UU;
  1988.             return (TRUE);
  1989.         }
  1990.          /* EnUU */
  1991.          if (sscanf (ptr, " %d/%d %s [EnUU %f]", &seq, &num, name, &ver) == 4)
  1992.         {
  1993.             thisDecode->sequence = seq;
  1994.             thisNumBlocks = num;
  1995.             thisDecode->seqConfidence = HIGH; 
  1996.             strcpy (thisDecode->ident, name);
  1997.             thisContentEncoding = CODE_UU;
  1998.             return (TRUE);
  1999.         }
  2000.         /* Wincode v2.x */
  2001.         if (sscanf (ptr, " %d/%d  file %s [ Wincode v%f ]", &seq, &num, name, &ver) == 4)
  2002.         {
  2003.             thisDecode->sequence = seq;
  2004.             thisNumBlocks = num;
  2005.             thisDecode->seqConfidence = HIGH; 
  2006.             strcpy (thisDecode->ident, name);
  2007.             return (TRUE);
  2008.         }
  2009.         return (FALSE);    // started with 'section' but we didn't have a template
  2010.     }
  2011.     /* Wincode v1.x */
  2012.     if (sscanf (copy, "[ section: %d/%d  File: %s  Encoder: Wincode v%f", &seq, &num, &ver) == 4)
  2013.     {
  2014.         thisDecode->sequence = seq;
  2015.         thisNumBlocks = num;
  2016.         thisDecode->seqConfidence = HIGH; 
  2017.         strcpy (thisDecode->ident, name);
  2018.         return (TRUE);
  2019.     }
  2020.  
  2021.     /* UUXFER */
  2022.     if (sscanf (copy, "%s section %d/%d  UUXFER ver %f", name, &seq, &num, &ver) == 4)
  2023.     {    
  2024.         thisDecode->sequence = seq;
  2025.         thisNumBlocks = num;
  2026.         thisDecode->seqConfidence = HIGH; 
  2027.         strcpy (thisDecode->ident, name);
  2028.         thisContentEncoding = CODE_UU;
  2029.         return (TRUE);
  2030.     }
  2031.  
  2032.     if (sscanf(copy, "archive-name: %[^/]/part%d", name, &seq) == 2)        // %[^/] reads a string up to a slash
  2033.     {
  2034.         strcpy (thisDecode->ident, name);
  2035.         thisDecode->sequence = seq;
  2036.         if (!thisDecode->seqConfidence)
  2037.             thisDecode->seqConfidence = LOW; // still don't have number of parts
  2038.         return (TRUE);
  2039.     }
  2040.  
  2041.     return (FALSE);
  2042. }
  2043. //-------------------------------------------------------------------------
  2044. void
  2045. ExecuteDecodedFile(int num, char *fileName)
  2046. {
  2047.     char ext[4];        // these are DOS specific...
  2048. //    char name[9];
  2049.     char association[MAXINTERNALLINE];
  2050.     char execute[MAXINTERNALLINE+MAXFILENAME];
  2051.     int len;
  2052.     unsigned int err;
  2053.     char *src, *dest, *src2;
  2054.     char mybuf[MAXINTERNALLINE];
  2055.  
  2056. #ifndef WIN32
  2057.     GetFileExtension (ext, fileName);
  2058.  
  2059.         if (!_stricmp (ext, "exe"))
  2060.             strcpy (execute, fileName);
  2061.     else
  2062.     // example association: doc=C:\WIN\WINWORD\winword.exe ^.doc
  2063.  
  2064.     if ((len = GetProfileString ("Extensions", (LPCSTR)ext, "", association, MAXINTERNALLINE)) != 0)
  2065.     {
  2066.       for (src = association, dest = execute; *src; src++)
  2067.       {
  2068.         if (*src == '^')
  2069.         {
  2070.             for (src2 = fileName; *src2 && *src2 != '.'; src2++)
  2071.                *(dest++) = *src2;
  2072.         }
  2073.         else
  2074.            *(dest++) = *src;
  2075.       }
  2076.       *dest = '\0';
  2077.         }
  2078.         else
  2079.         {
  2080.         sprintf(mybuf, "Cannot execute - no association for file type %s", ext);
  2081.         UpdateThreadStatus (num, mybuf);
  2082.         return;
  2083.     }
  2084.  
  2085. #else
  2086.         FindExecutable (fileName, ".", association);
  2087.     wsprintf (execute, "%s %s", association, fileName);
  2088. #endif
  2089.     if ((err = WinExec (execute, SW_SHOWNORMAL)) < 32)
  2090.     {
  2091.         sprintf(mybuf, "Error executing %s, error %u", execute, err);
  2092.         UpdateThreadStatus (num, mybuf);
  2093.     }
  2094.     else if (CodingStatusVerbose)
  2095.     {
  2096.         sprintf(mybuf, "Executed %s", execute);
  2097.         UpdateThreadStatus (num, mybuf);
  2098.     }
  2099. }
  2100.