/********************************************************************
 *                                                                  *
 *  MODULE    :  WVCODING.CPP                                       *
 *                                                                  *
 *  PURPOSE   : This file contains functions for en/decoding files  *
 *                                                                  *
 *  ENTRY POINTS:                                                   *
 *              DecodeInit  - initializes decoding resources        *
 *              DecodeDone  - frees decoding resources              *
 *              InitCoded   - initializes a coded block             * 
 *                            called for each new art to decode     *
 *              DecodeLine  - Main decoding engine.  Called once    *
 *                            for each input line                   *
 *              DecodeFile  - Uses file instead of comm input as a  *
 *                            line provider to the decoding engine  *
 *              DecodeDoc   - Uses a Doc instead of comm input as a *
 *                            line provider to the decoding engine  *
 *      CompleteThisDecode  - Called at end of each article - adds  *
 *                            block to appropriate decode thread,   *
 *                            writes to disk if possible, etc       *
 *           EncodeAndSend  - Encodes a file and sends to net       *
 *       EncodeToTextBlock  - Encodes a file into a text block      *
 *            EncodeToFile  - Encodes a file and stores in new file *
 *                                                                  *
 *  NOTES:                                                          *
 *    Both the max # of batched decode files and the max #          * 
 *    threads per file are fixed.  This should be made dynamic      *
 *    at some point I guess.                                        *
 *                                                                  *
 * Author: John S. Cooper (jcooper@planetz.com)                      *
 *   Date: Sept 13, 1993                                            *
 ********************************************************************/

/* 
 * $Id: wvcoding.cpp 1.4 1997/04/05 00:26:27 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#include "defaults.h"
#pragma hdrstop

#define WVCODING
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>                              /* for isspace, isalnum, etc */
#if 1 //S.Yoshida
#ifndef __GNUC__
#include <shellapi.h>                   /* for FindExecutable */
#endif
#else
#include <shellapi.h>                   /* for FindExecutable */
#endif

#include "WVClass.h"

#ifdef _DEBUG
#undef DEBUG_MAP
#undef DEBUG_MEM
#undef DEBUG_DECODE
#endif

#define LOW             1                               // sequence confidence levels
#define HIGH            2
#define UNUSED          127                     // unused mapping slots

/*
 * Globals for this func
 */

char *codingTable;
char customTable[CODINGTABLESIZE+1];
TypDecodeThread *threadList[MAX_DECODE_THREADS] = {0};
int numDecodeThreads;                   // number of active decode threads

#if 1 /* S.Yoshida */
  #define SUPPORT_PARTIAL_MULTIPART  1
  #define PARTIALID      contentType        // reuse old member :-p
  static char thisPartId[ MAXHEADERLINE ];// partial id for current art
  static int  thisPartNum;                  // partial number for current art
  static int  lastNumParts;
#else
  #define SUPPORT_PARTIAL_MULTIPART 0
#endif

int currentDecodeThread;
int numDumbDecoded;
int thisNumBlocks;                              // MIME vars for current block

static char thisBoundary[MAXINTERNALLINE], thisBoundaryEnded[MAXINTERNALLINE];
int thisContentEncoding, prevContentEncoding;
char prevBlockIdent[MAXINTERNALLINE];
char subjWrapLine[MAXHEADERLINE];

TypTextBlock *EncodeTextBlock;
HANDLE hEncodeFile;

/*
 * Private functions
 */
unsigned long Encode (TypAttachment * thisAttach, unsigned long startByte, unsigned long maxBytes, BOOL (*AddLine) (char *, unsigned long));
unsigned long EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num, int encodingType);
void EncodeUnit (unsigned char *out, unsigned char *in, int num, int encodingType);
int GetThreadByName (char *name);
int AddToThreadList ();
void DestroyThread (int num);
void DestroyCodedBlock (TypCoded ** thisCoded);
void InsertBlockInThread (int num, TypCoded * thisBlock, int index);
void DeleteBlockFromThread (int num, int index);
int WriteDecodeThread (int num);
int WriteBlockToFile (int num, TypCoded * thisDecode);
int WriteTopBlockToFile (int num, int *endFlag, BOOL bPreview = FALSE);
int AddDataToBlock (TypCoded * thisDecode, char *newData, unsigned int dataLen);
void PreAllocDataByLines (TypCoded * thisDecode);
void ParseInfoLine (TypCoded * thisDecode, char *line, BOOL guessIdent);
BOOL ParseAppSpecificLine (TypCoded * thisDecode, char *line);
BOOL ReadSubjectToken (char *dest, char **ptr);
BOOL isnumber (char *str);
BOOL SearchThreadNames (char *name);
int ParseMimeHeaderLine (TypCoded * thisDecode, char *line);
int ParseMimeContentType (TypCoded * thisDecode, char *line);
char GetLastChar(char *szLine);
void CrcInit(DWORD* pcrc_val );
void CrcAdd(int c, DWORD* crc_val );
BOOL ReadHex32( LPCSTR szHex, DWORD* pdwHex);

void CreateCodingStatusWnd (char *title, int show);
int DecodeDataLine (TypCoded * decode, char *line);
void CreateUUTable ();
BOOL TestIgnoreLine (char *line);
int IsDataLine (char *line, BOOL bFirst = FALSE);
BOOL IsHeaderLine(LPCSTR pbuf, int nLength);
BOOL TestDataLine (char *line, BOOL bFirst = FALSE);
int ThreadTable (char *dest, char *name);
void UpdateThreadStatus (int num, char *str);

HANDLE ExecuteDecodedFile (int num, char *fileName);

#if defined(DEBUG_DECODE) || defined(DEBUG_MEM)
#define DEBUG_FILE "coding.log"
void DebugLog ();
void InitDebugLog ();
char debug[MAXCOMMLINE];
OFSTRUCT debugFileStruct;
#endif

static long crc_tab[256] =
{
	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}
;


void CrcInit(DWORD* pcrc_val )
{
	if (pcrc_val)
		*pcrc_val = (DWORD) -1;
}


void CrcAdd(int c, DWORD* crc_val )
{
	DWORD ch1,ch2,cc;
	if (!crc_val)
		return;

	 cc = (c & 0xFF);
	 ch1 =(*crc_val ^ cc) & 0xffL;
	 ch1 =crc_tab[ch1];
	 ch2 =(*crc_val>>8L) & 0xffffffL;  // Correct version
	 *crc_val = ch1 ^ ch2;
}

int dehex( int c)
{
    
    if (c >= 'a' && c <= 'f')
        c -=32;
        
    if (c > '9')
        c -= 7;
    c = c & 0x0F;
    return c;
    
}

//read up to 8 hex digits
BOOL ReadHEX32( LPCSTR szHex, DWORD* pdwHex)
{

	if (!pdwHex || !szHex || !*szHex)
		return FALSE;

	BOOL bOK = TRUE;
	BOOL bInHex = FALSE;

	for (int i = 0; i < 8 && *szHex; i++)  //allow for blanked zeros
	{

		if (isxdigit( (unsigned char) *szHex))
		{
			*pdwHex = *pdwHex << 4 | dehex(*szHex++);
			bInHex = TRUE;
		}
		else
		{		
			if (!isspace((unsigned char)*szHex) || bInHex)
			{
				bOK = FALSE;
				break;
			}
			*szHex++;

		}
	}

	return bOK;
}

const char * ident_with_frob(const char * source)
{
	size_t pos;
	unsigned long sum;
	const char * p;
	static char sRetval[MAXFILENAME + 1];

	sRetval[0] = '\0';
	if(source)
	{
		//  if('<' == *source) // skip any leading '<'
		//      source++;
		// fix a bug where a bad references line causes a GPF
		// JD 2/10/97
		//
		while (source &&  (' ' == *source))
			source++;
		if(source)
		{
			strntcpy(sRetval, source, MAXFILENAME);
			sRetval[MAXFILENAME] = '\0';
			
			if(pos = strlen(source)) // chop trailing whitespace and '>'
			{
				if (pos < MAXFILENAME)
					sRetval[pos] = 0;
			}
			// only frob if we need to
			if(pos >= MAXFILENAME)
			{
				sum = 0;
				for (p = source; *p; p++)
				{
					sum = (sum << 1) + *p;
				}
				_sprintf(sRetval+MAXFILENAME-8, "%08x", sum);
			}
		}
	}
	return (sRetval);
}

#if defined(DEBUG_DECODE) || defined(DEBUG_MEM)
/* ------------------------------------------------------------------------
 * Debug log handler
 * Open/close the file for every addition to log, so all info is safely
 * captured in case of GPF
 */
void
DebugLog ()
{
  HFILE hDebugFile;

  if ((hDebugFile = OpenFile ((char far *) NULL, &debugFileStruct, OF_REOPEN | OF_WRITE)) >= 0) {
        _llseek (hDebugFile, 0L, 2);    /* go to end */
        _lwrite (hDebugFile, debug, strlen (debug));
        _lclose (hDebugFile);
  }
  OutputDebugString(debug);
}
void
InitDebugLog ()
{
  HFILE hDebugFile;

  if ((hDebugFile = OpenFile ((char far *) DEBUG_FILE, &debugFileStruct, OF_CREATE)) >= 0) {
        sprintf (debug, "New log");
        _lwrite (hDebugFile, debug, strlen (debug));
        _lclose (hDebugFile);
  }
}

#endif

/* return the last non-whitespace character on a line */
/* string must be null terminated */
char GetLastChar(char *szLine)
{

    char c = '\0';
    
    if (!szLine)
        return 0;
    while(*szLine)
    {
        if (!isspace((unsigned char)*szLine))
            c = *szLine;
        szLine++;
    }
    
    return c;
}

/* ------------------------------------------------------------------------
 * Decode a file
 * Uses a file instead of the comm article as a line-provider to the 
 * decode routines
 */
void
DecodeFile (HWND hParentWnd)
{
  FILE *DecodeFile;
  char mybuf[MAXINTERNALLINE], fileName[MAXFILENAME], *ptr;
  int result;

  if (AskForExistingFileName (hParentWnd, fileName, "Open Encoded File") == FAIL)
        return;

  DecodeInit (FALSE);
  if ((DecodeFile = fopen (fileName, "rb")) == NULL) { //yENC need binary
        WVMessageBox(hParentWnd, IDS_WVCODING_FILE_FAIL, IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONERROR);
        //WVMessageBox (hParentWnd, "Failure to read file", "File error", MB_OK | MB_ICONERROR);
        return;
  }
  if ((currentCoded = InitCoded (hParentWnd)) == NULL) {
        WVMessageBox(hParentWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK | MB_ICONERROR);
        //WVMessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
        return;
  }
  hSaveCursor = SetCursor (hHourGlass);
  
  *thisBoundary = '\0';  //MDS initialize each time
  *thisBoundaryEnded = '\0';

  while (1) {
        if ((ptr = fgets (mybuf, MAXINTERNALLINE, DecodeFile)) == NULL)
          strcpy (mybuf, "~EOF~");      // make sure wrap up occurs ('~' is not in any char map)

        result = DecodeLine (currentCoded, mybuf);
        if (result == FAIL) {
          WVMessageBox(hParentWnd, IDS_WVCODING_DECODE_ABORT, IDS_WVCODING_PROBLEMS, MB_OK | MB_ICONEXCLAMATION);
          //WVMessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
          goto abortDecodeFile;
        }
        if (result == END_BLOCK) {
          if (CompleteThisDecode () == FAIL) {
                WVMessageBox(hParentWnd, IDS_WVCODING_DECODE_ABORT, IDS_WVCODING_PROBLEMS, MB_OK | MB_ICONEXCLAMATION);
                //WVMessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
                goto abortDecodeFile;
          }
          if ((currentCoded = InitCoded (hParentWnd)) == NULL) {
                WVMessageBox(hParentWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK | MB_ICONERROR);
                //WVMessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
                goto abortDecodeFile;
          }
        }
        if (ptr == NULL)                        // EOF

          break;
  }

  if (currentCoded != NULL)             // finish final block

        CompleteThisDecode ();

abortDecodeFile:;
  SetCursor (hSaveCursor);

  fclose (DecodeFile);
  DecodeDone ();
}

/* ------------------------------------------------------------------------
 * DecodeOpenArticles and DecodeDoc
 * Uses a doc instead of the comm article as a line-provider to the 
 * decode routines
 * Assumes DecodeInit is already called, and expects caller to call DecodeDone
 */
void DecodeOpenArticles (HWND hParentWnd, BOOL bMonitor /*=FALSE*/)
{
  register int i;
  // the following two functions are in wvgroup.c
  // extern BOOL article_operation (TypDoc far * Doc, long artindex,
  //       BOOL (*art_fun) (header_p headers, TypGroup * group, long artindex));
  // extern BOOL selected_false (header_p headers, TypGroup * group, long artindex);

  for (i = 0; i < MAXARTICLEWNDS; i++) 
  {
        if (ArticleDocs[i].InUse && (!CommBusy || CommDoc != &ArticleDocs[i])) 
		{
          // ensure the article is deselected now
          if (ArticleDocs[i].ParentDoc) 
		  {
                article_operation (ArticleDocs[i].ParentDoc, ArticleDocs[i].LastSeenLineID, selected_false);
                InvalidateRect (ArticleDocs[i].ParentDoc->hDocWnd, NULL, FALSE);
          }
          DecodeDoc (hParentWnd, &ArticleDocs[i], bMonitor);
        }
  }
}

int DecodeDoc (HWND hParentWnd, TypDoc * Document, BOOL bMonitor /*=FALSE*/)
{
  int result = FAIL;
  TypBlock far *BlockPtr;
  TypLine far *LinePtr;

  if ((currentCoded = InitCoded (hParentWnd)) == NULL) 
  {
        WVMessageBox(hParentWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK | MB_ICONERROR);
        //WVMessageBox (hParentWnd, "Unable to continue due to memory constraints.  Aborted", "Init Coded Object Error", MB_OK);
        return FAIL;
  }


  hSaveCursor = SetCursor (hHourGlass);
  LockLine (Document->hFirstBlock, sizeof (TypBlock), (TypLineID) 0L, &BlockPtr, &LinePtr);
  while (LinePtr->length != END_OF_BLOCK && LinePtr->length ) 
  {
        result = DecodeLine (currentCoded,GetTextPtr(LinePtr), bMonitor);

        if (result == FAIL) 
		{
          WVMessageBox(hParentWnd, IDS_WVCODING_DECODE_ABORT, IDS_WVCODING_PROBLEMS, MB_OK | MB_ICONEXCLAMATION);
          
          goto abortDecodeDoc;
        }

        if (result == END_BLOCK) 
		{
          if (CompleteThisDecode (bMonitor) == FAIL) 
		  {
                WVMessageBox(hParentWnd, IDS_WVCODING_DECODE_ABORT, IDS_WVCODING_PROBLEMS, MB_OK | MB_ICONEXCLAMATION);
				result = FAIL;
                goto abortDecodeDoc;
          }
          if ((currentCoded = InitCoded (hParentWnd)) == NULL) 
		  {
                WVMessageBox(hParentWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK | MB_ICONERROR);          
                result = FAIL;
				goto abortDecodeDoc;
          }

        }
        NextLine (&BlockPtr, &LinePtr);
  }
  if (currentCoded != NULL)             // finish final block
        CompleteThisDecode (bMonitor);

abortDecodeDoc:;
  SetCursor (hSaveCursor);
  GlobalUnlock (BlockPtr->hCurBlock);

  return result;
}
/* ------------------------------------------------------------------------
 *    Encode a file and ask the user for a file to name to store it in
 */
void EncodeToFile (HWND hParentWnd, TypAttachment * thisAttach)
{
  char fileName[MAXFILENAME];
  HANDLE hFile = INVALID_HANDLE_VALUE;

  fileName[0] = '\0';
  if (AskForNewFileName (hParentWnd, fileName, "", FALSE) != FAIL) 
  {
        if ((hFile = CreateFile ((char*) fileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
				FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) 
		{
          WVMessageBox(hParentWnd, IDS_WVCODING_OPEN_FAILURE, IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP);
          //WVMessageBox (hParentWnd, "Unable to open output file", "File Error", MB_OK | MB_ICONSTOP);
        }
        else 
		{
          SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
		  SetEndOfFile(hFile); //truncate existing file

		  CreateStatusArea (hParentWnd, "Encoding To File", SW_SHOWNORMAL);
          hSaveCursor = SetCursor (hHourGlass);
          //SetCapture (hParentWnd);
          strntcpy (currentCoded->ident, thisAttach->fileName, MAXINTERNALLINE-1);   // status window info

          if (EncodeIntoFile (hFile, thisAttach, 0, 0) == 0) 
		  {
                WVMessageBox(hParentWnd, IDS_WVCODING_ENCODE_FAILURE, IDS_WVCODING_ENCODE_ERROR,  MB_OK | MB_ICONSTOP);
                //WVMessageBox (hParentWnd, "Encode to file failed", "Encode Error", MB_OK | MB_ICONSTOP);
          }
      SetCursor (hSaveCursor);
      DestroyStatusArea ();
      CloseHandle (hFile);
    }
  }
}

/* ------------------------------------------------------------------------
 *    Create a status window a coding block used for status info
 *  the currentCoded block is purely for status in this case
 *  This is used by attach and EncodeFile, SendComposition, etc
 *  to show status in progress
 *  Use UpdateBlockStatus to update/display status
 *  Use DestroyStatusArea to clean up when done
 */
void CreateStatusArea (HWND hParentWnd, char *title, int show)
{
  if ((currentCoded = InitCoded (hParentWnd)) == NULL)
        return;
  CreateCodingStatusWnd (title, show);
  currentCoded->sequence = 1;   // status window info
}

void DestroyStatusArea ()
{
  if (currentCoded) {
        DestroyCodedBlock (&currentCoded);
  }
  if (hCodedBlockWnd) {
        DestroyWindow (hCodedBlockWnd);
  }
  hCodedBlockWnd = (HWND) NULL;
}

void AddAbortStatus(HWND hAbortNotify /*=0*/)
{
  if (hCodedBlockWnd) 
        SendMessage (hCodedBlockWnd, WM_ABORTBUTTON, 1,(LPARAM) hAbortNotify);

}

void CreateCodingStatusWnd (char *title, int show)
{
  char temp[MAXINTERNALLINE];
  int x, y;

  /* Create coding status window, default top center */
  x = (xScreen - STATUSWIDTH) >> 1;
  y = 1;

  /* If the screen position has been saved, use that instead. */
  GetPrivateProfileString (CODING, "BlockStatusWindowPos", "!",
                                                 temp, MAXINTERNALLINE, szAppProFile);
  RECT rPos = {x,y,x+STATUSWIDTH,y+STATUSHEIGHT};
  if (*temp != '!') 
  {
        if (sscanf (temp, "%d,%d", &rPos.left, &rPos.top) == 2)
        {
			rPos.bottom = rPos.top + STATUSHEIGHT;
			rPos.right = rPos.left + STATUSWIDTH;
			MakeRectVisible( rPos);
		}
  }

  hCodedBlockWnd = CreateWindowEx (WS_EX_DLGMODALFRAME, "WinVnBlockCoding", title,
                                   WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN,
                                   rPos.left, rPos.top, STATUSWIDTH, STATUSHEIGHT,
                                           (HWND) NULL, (HMENU) NULL, hInst, (void far *) NULL);

  SetHandleBkBrush (hCodedBlockWnd, hStatusBackgroundBrush);
  ShowWindow (hCodedBlockWnd, show);
}


/* ------------------------------------------------------------------------
 *    Adds text to a thread coding status block, and refreshes
 *  the thread status window
 */
void UpdateThreadStatus (int num, char *str)
{
  if (threadList[num]->statusText->hTextWnd)
  {
	AddLineToTextBlock (threadList[num]->statusText, str);
	InvalidateRect (threadList[num]->statusText->hTextWnd, NULL, FALSE);
	UpdateWindow (threadList[num]->statusText->hTextWnd);
  }
}

/* ------------------------------------------------------------------------
 *    Refreshes the block status window
 */
void UpdateBlockStatus (BOOL bShowAbort /*= FALSE*/, HWND hAbortWnd /*= NULL*/)
{
  if (hCodedBlockWnd) 
  {

		SendMessage (hCodedBlockWnd, WM_ABORTBUTTON, bShowAbort ? 1 : 0, (LPARAM) hAbortWnd);
		InvalidateRect (hCodedBlockWnd, NULL, FALSE);
        UpdateWindow (hCodedBlockWnd);
  }
}

/* ------------------------------------------------------------------------
 *    Encode a file to file, net, or textblock
 *  Assumes currentCoded is initialized
 *    startByte is file byte to start at (0 == beginning)
 *    maxBytes is # encoded bytes to generate before stopping (0 == no restriction)
 *    AddLine is a pointer to a function which will be used to deal with each
 *      encoded line appropriately (i.e. add to file, send to net...)
 * returns # bytes read from file or 0 on failure
 */
BOOL 
EncodeIntoFileHelper (char *str, long nLen)
{
  DWORD dwWrote, dwToWrite = nLen;
  return (WriteFile (hEncodeFile, str, dwToWrite, &dwWrote, NULL) && dwWrote == dwToWrite ) ? SUCCESS : FAIL;
}

unsigned long EncodeIntoFile (HANDLE hFile, TypAttachment * thisAttach,
                                unsigned long startByte, unsigned long maxBytes)
{
  unsigned long result;
  hEncodeFile = hFile;
  CodingState = ATTACH_PROCESSING;
  result = Encode (thisAttach, startByte, maxBytes, (BOOL (*)(char *, unsigned long)) EncodeIntoFileHelper);
  CodingState = INACTIVE;
  return result;
}

unsigned long           // assumes appropriate sockets are opened and initialized
EncodeAndSend (TypAttachment * thisAttach, unsigned long startByte,
                           unsigned long maxBytes)
{
  unsigned long result;
  int oldState;
//  extern BOOL SendOneLine (char *str);        // in wvattach.c

  oldState = CodingState;
  CodingState = ATTACH_SENDING;
  UpdateBlockStatus ();
  result = Encode (thisAttach, startByte, maxBytes, SendOneLineWithLen);

  CodingState = oldState;
  UpdateBlockStatus ();
  return result;
}


unsigned long Encode (TypAttachment * thisAttach, unsigned long startByte,
                unsigned long maxBytes, BOOL (*AddLine) (char *, unsigned long))

{
  HANDLE hFile;
  int lineLen, start = 0;
  register int i;
  unsigned char inBuf[MAXINTERNALLINE];	 //yEnc needs 129+
  unsigned char outLine[MAXINTERNALLINE];
  unsigned long totalNumAdded, totalNumRead;
  DWORD numRead;
  BOOL done;
  int len, encodingType;
#ifndef __GNUC__
  extern char *NameWithoutPath (char *tempName, char *fileName);
#endif

  totalNumAdded = totalNumRead = 0;
  encodingType = thisAttach->encodingType;

  // Set up encoding map
  switch (encodingType) {
  case CODE_BASE64:
        codingTable = base64Table;
        break;
  case CODE_UU:
        codingTable = uuTable;
        break;
  case CODE_XX:
//  Note, XX table is hard coded in declarations for simplicity
        codingTable = xxTable;
        break;
  case CODE_YENC:
		break;
  case CODE_CUSTOM:
        codingTable = UserCodingTable;
        // add 2 table lines
        if ((*AddLine) ("table\r\n",7))
          return (0);
        memmove (str, UserCodingTable, 32);
        str[32] = '\r';
        str[33] = '\n';
        str[34] = '\0';
        if ((*AddLine) (str, strlen(str)) )
          return (0);
        memmove (str, &UserCodingTable[32], 32);
        if ((*AddLine) (str, strlen(str)) )
          return (0);

        totalNumAdded += 75;            // table + \r\n + 32 + \r\n + 32 + \r\n

        currentCoded->numBytes += 75;
        currentCoded->numLines += 3;
        break;
  }
  lineLen = ENCODE_LINE_LEN;


  // Prepare for encoding
  switch (encodingType) 
  {
	  case CODE_UU:
	  case CODE_XX:
	  case CODE_CUSTOM:
			// these are 3-to-4 encodings with 1st char indicating line len
			// and block starts with 'begin' line and ends with 'end' line
			// add 'begin' line
			if (startByte == 0) 
			{
			  NameWithoutPath (str, thisAttach->fileName);
			  len = _snprintf ((char*)outLine, MAXINTERNALLINE, "\r\nbegin 755 %s\r\n", str);
			  if ((*AddLine) ((char*)outLine, strlen( (char*)outLine) ) )
					return (0);

			  totalNumAdded += len;
			  currentCoded->numBytes += len;
			  currentCoded->numLines++;
			  thisAttach->nCurPart = 0;
			}
			thisAttach->nCurPart++;
			// set 1st char to appropriate line length value
			outLine[0] = codingTable[ENCODE_LINE_LEN];
			start = 1;                                      // count includes the count char itself
			break;
	  case CODE_YENC:
			//yEnc is a binary tranmission of up to 255+ bytes per line usually 128/129
			CrcInit(&thisAttach->dwCRCPart);
			if (startByte == 0)
			{
				CrcInit(&thisAttach->dwCRCFull);
				thisAttach->nCurPart = 0;
			}

			thisAttach->nCurPart++;
			currentCoded->dwPartSize = 0;
			start = 0;
			lineLen = YENCODE_LINE_LEN; //hard coded for now
			break;
	  case CODE_BASE64:
			// base64 is a 3-to-4 encoding, with no line len indicator and 
			// no 'begin' or 'end' lines

			if (startByte == 0)
			{
				thisAttach->nCurPart = 0;

				if ( !*thisAttach->contentType)  //only for file encoder
				{
				  NameWithoutPath (str, thisAttach->fileName);
				  len = _snprintf ((char*)outLine, MAXINTERNALLINE, "\r\nContent-Type: application/octet-stream; name=\"%s\"\r\n"
					"Content-Transfer-Encoding:base64\r\n\r\n", str);
				  if ((*AddLine) ((char*)outLine, strlen( (char*)outLine) ) )
						return (0);

				  totalNumAdded += len;
				  currentCoded->numBytes += len;
				  currentCoded->numLines+=2;

				}
			}

			start = 0;
			thisAttach->nCurPart++;
			break;
  }
  

  // open the file and have at it 
  if ((hFile = CreateFile ((char*) thisAttach->fileName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
			FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) 
  {
        return (0);
  }
  if (startByte && SetFilePointer (hFile, startByte, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) 
  {
        CloseHandle (hFile);
        return (0);
  }
  done = FALSE;

  if (encodingType == CODE_YENC)
  {
	  	int remain = 0;
		
		if (maxBytes > thisAttach->nAttachBytes)
			maxBytes = thisAttach->nAttachBytes;
		DWORD dwSize = maxBytes;


		NameWithoutPath (str, thisAttach->fileName);
		if (startByte == 0) 
		{
			
			currentCoded->dwExpectedBytes = GetFileSize(hFile, NULL);
						
			if (thisAttach->nAttachParts > 1)
			{
				len = _snprintf ((char*)outLine, MAXINTERNALLINE, "\r\n=ybegin part=1 total=%ld size=%ld line=%d name=%s\r\n", thisAttach->nAttachParts, currentCoded->dwExpectedBytes, YENCODE_LINE_LEN, str);
			}
			else
				len = _snprintf ((char*)outLine, MAXINTERNALLINE, "\r\n=ybegin size=%ld line=%d name=%s\r\n", currentCoded->dwExpectedBytes,YENCODE_LINE_LEN, str);


		}
		else
		{
			if (maxBytes == 0)
			{
				dwSize = currentCoded->dwExpectedBytes  - startByte;
			}
			len = _snprintf ((char*)outLine, MAXINTERNALLINE, "\r\n=ybegin part=%ld total=%ld line=%ld size=%ld name=%s\r\n", thisAttach->nCurPart, thisAttach->nAttachParts, YENCODE_LINE_LEN, currentCoded->dwExpectedBytes,  str);

		}
		if ((*AddLine) ((char*)outLine, strlen( (char*)outLine) ) )
			return (0);
		//totalNumAdded += len;
		currentCoded->numBytes += len;
		currentCoded->numLines++;


		if (thisAttach->nAttachParts > 1)
		{
			
			if ( startByte +1 + thisAttach->nAttachBytes > currentCoded->dwExpectedBytes)
				dwSize = currentCoded->dwExpectedBytes - (startByte);
			
			len = _snprintf ((char*)outLine, MAXINTERNALLINE, "=ypart begin=%ld end=%ld\r\n", startByte+1, startByte+dwSize );
			if ((*AddLine) ((char*)outLine, strlen( (char*)outLine) ) )
				return (0);
			//totalNumAdded += len;
			currentCoded->numBytes += len;
			currentCoded->numLines++;
		}

		start = 0;
	  
		while (!done && (!maxBytes || currentCoded->dwPartSize < maxBytes)) 
		{
			int i,c, len=0; 
			if (!ReadFile (hFile, inBuf+remain, lineLen-remain, &numRead, NULL)) 
			{
			  CloseHandle (hFile);
			  return (0);
			}
			numRead += remain;
			remain = 0;
			if (numRead < lineLen) 
			{        // change last line length value
				done = TRUE;
			}
			i = 0;
			while (i < numRead && len < YENCODE_LINE_LEN && (!maxBytes || currentCoded->dwPartSize < maxBytes))
			{
				c = inBuf[i++];
				currentCoded->dwPartSize++;
				CrcAdd(c, &thisAttach->dwCRCPart);
				CrcAdd(c, &thisAttach->dwCRCFull);
				c = ((c+42) & 0xFF); //add the secret number
				//handle problem characters, or dot in first column	or tab in 1st or last column
				if (c == 0x00 || (c == 0x09 && (len == 0 || len+1 == YENCODE_LINE_LEN)) || c == 0x0A || c == 0x0D || c == '=' || (len == 0 && c == '.') )
				{
					outLine[len++] = '=';
					outLine[len++] = c+64;
				}
				else
				{
					outLine[len++] = c;
				}	
			
			}
			outLine[len++] = '\r';
			outLine[len++] = '\n';
			outLine[len] = '\0';
			
			if ((*AddLine) ((char*)outLine, len)) 
			{
			  CloseHandle (hFile);
			  return (0);
			}
			if (i < numRead)
			{
				remain = numRead - i;
				memmove(inBuf, inBuf+i, remain);
			}

					
			totalNumAdded += len;
			//currentCoded->numLines++; //! updated in AddLine already! MDS 20000407
			currentCoded->numBytes += i;
			totalNumRead += i;


		}//while !done

		if (thisAttach->nAttachParts > 1)
		{
			if (done)
			{
				_snprintf( (char*)outLine, MAXINTERNALLINE,"=yend size=%ld part=%d pcrc32=%08lx crc32=%08lx\r\n",currentCoded->dwPartSize,thisAttach->nCurPart,(thisAttach->dwCRCPart ^ 0xFFFFFFFF), (thisAttach->dwCRCFull ^ 0xFFFFFFFF));
				totalNumRead =  thisAttach->nAttachBytes;
			}
			else
				_snprintf( (char*)outLine, MAXINTERNALLINE,"=yend size=%ld part=%d pcrc32=%08lx \r\n",currentCoded->dwPartSize,thisAttach->nCurPart,(thisAttach->dwCRCPart ^ 0xFFFFFFFF));

		}
		else
		{
			//single part
			_snprintf( (char*)outLine, MAXINTERNALLINE, "=yend size=%ld crc32=%08lx \r\n",currentCoded->dwPartSize, (thisAttach->dwCRCPart ^ 0xFFFFFFFF));
		}
		len = strlen( (char *) outLine);
		if ((*AddLine) ((char*)outLine, len)) 
		{
		  CloseHandle (hFile);
		  return (0);
		}

  }
  else
  {

	  while (!done && (!maxBytes || totalNumAdded < maxBytes)) 
	  {
			if (!ReadFile (hFile, inBuf, lineLen, &numRead, NULL))
			{
			  CloseHandle (hFile);
			  return (0);
			}
			if (numRead < lineLen) {        // change last line length value

			  if (encodingType != CODE_BASE64) 
			  {
					outLine[0] = codingTable[numRead];
			  }
			  for (i = numRead; i < lineLen; i++) 
			  {
					inBuf[i] = 0;
			  }
			  done = TRUE;
			}
			EncodeLine (outLine, inBuf, start, numRead, encodingType);
			strcat ((char*)outLine, "\r\n");
			if ((*AddLine) ((char*)outLine, strlen( (char*)outLine))) 
			{
			  CloseHandle (hFile);
			  return (0);
			}
			totalNumAdded += strlen ((char*)outLine);
			currentCoded->numBytes += numRead;
			totalNumRead += numRead;

	  }
  }
  CloseHandle (hFile);

  if (done) {
        switch (encodingType) {
        case CODE_UU:
        case CODE_XX:
        case CODE_CUSTOM:
          // add zero length line
          outLine[0] = codingTable[0];
          outLine[1] = '\r';
          outLine[2] = '\n';
          outLine[3] = '\0';
          if ((*AddLine) ((char*)outLine, strlen((char*)outLine)) )
                return (0);
          if ((*AddLine) ("end\r\n", 5))
                return (0);
          currentCoded->numLines += 2;
        }
  }
  UpdateBlockStatus ();

  return (totalNumRead);
}

/* ------------------------------------------------------------------------
 *    Encode num chars from line
 *  Put data in outLine starting at start
 */
unsigned long
EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num, int encodingType)
{
  register int i, j;

  if (encodingType == CODE_YENC)
	return 0;

  if (encodingType == CODE_NONE)
  {
	memcpy(outLine, line+start, num);
	outLine[num] = 0;
	return num; 
  }

  for (j = start, i = 0; i < num; j += 4, i += 3)
        EncodeUnit (&outLine[j], &line[i], (i + 3 > num) ? num - i : 3, encodingType);

  outLine[j] = '\0';
  return (j);
}

void EncodeUnit (unsigned char *out, unsigned char *in, int num, int encodingType)
{
  
  if (encodingType ==  CODE_YENC)
	return;
  


  out[0] = codingTable[((in[0] >> 2) & 63)];
  out[1] = codingTable[(((in[0] << 4) | (in[1] >> 4)) & 63)];
  if (num == 1) 
  {
        if (encodingType == CODE_BASE64) 
		{
          strcpy ((char*)&out[2], "==");
        }
        else {
          out[2] = 0;
        }
        return;
  }

  out[2] = codingTable[(((in[1] << 2) | (in[2] >> 6)) & 63)];
  if (num == 2) 
  {
        if (encodingType == CODE_BASE64) 
		{
          strcpy ((char*)&out[3], "=");
        }
        else {
          out[3] = 0;
        }
        return;
  }

  out[3] = codingTable[(in[2] & 63)];
}
/* ------------------------------------------------------------------------
 *    Init for decoding
 *  Initialize the coding tables
 *  Init threadList:  list of pointers to Thread lists 
 *  (one thread for each file being decoded)
 *      
 */
void DecodeInit (BOOL bRestart /*=FALSE*/)
{
  register int i;

	if (bRestart)
	{
#if SUPPORT_PARTIAL_MULTIPART
		thisPartNum = -1;
		thisContentEncoding = CODE_UNKNOWN;
		CodingState = DECODE_SKIPPING;

#endif
	return;

	}
#ifdef DEBUG_MEM
  InitDebugLog ();
#endif

	for (i = 0; i < MAX_DECODE_THREADS; i++)
	{

		if (threadList[i] && !IsBadReadPtr(threadList[i], sizeof(TypDecodeThread)))
		{
			DestroyThread(i);

		}
		threadList[i] = NULL;
	}

  currentCoded = (TypCoded *) NULL;
  CodingState = DECODE_SKIPPING;
  numDecodeThreads = 0;
  numDumbDecoded = 0;
  currentDecodeThread = -1;
  thisNumBlocks = -1;
  prevBlockIdent[0] = '\0';
  thisContentEncoding = CODE_UNKNOWN;
  prevContentEncoding = CODE_UNKNOWN;
  *thisBoundary = '\0';  //MDS initialize each time
  *thisBoundaryEnded = '\0';
#if SUPPORT_PARTIAL_MULTIPART
  thisPartId[0] = '\0';
  thisPartNum = -1;
  lastNumParts = -1;
#endif
  customTable[0] = '\0';

#ifdef DEBUG_MEM
  sprintf (debug, "\r\nInitialized decoding, %d threads", MAX_DECODE_THREADS);
  DebugLog ();
#endif
  // Create coding status window (open for duration of coding only)
  CreateCodingStatusWnd ("Block Decoding Status",
                                MinimizeStatusWindows ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL);
  UpdateBlockStatus ();

}

/* ------------------------------------------------------------------------
 * At this point, all threads in sequence should have been written and freed,
 * If any threads left, we either had an incomplete decode of some file,
 * or the file was encoded w/out sequence info, and we were waiting to 
 * hopefully receive it all.  If we had an endflag, then write it in order
 * received
 * Accepts handle of parent window so it can display any 'incomplete' messages
 */
void DecodeDone (BOOL bMonitor /*= FALSE*/)
{
  char mybuf[MAXINTERNALLINE], name[MAXINTERNALLINE];
  int i;
#ifdef DEBUG_MEM
  sprintf (debug, "\r\nDone coding,  Wrapping up stray threads");
  DebugLog ();
#endif
  name[0] = '\0';                               // only show name if not verbose mode

  while (numDecodeThreads > 0)  // always work on top of list [0]
   {
        if (bMonitor)
			threadList[0]->bMonitor = bMonitor;

#ifdef MULTISTATUSWND

		if (!CodingStatusVerbose) 
#endif
		{
          if (threadList[0]->name[0] != '\0')
                _snprintf (name, MAXINTERNALLINE-1, "%s   ", threadList[0]->name);
          else
                _snprintf (name, MAXINTERNALLINE-1, "%s   ", threadList[0]->ident);
        }

        if (threadList[0]->numBlocks == 0) {    // premature end of file

          WriteDecodeThread (0);
          _snprintf (mybuf, MAXINTERNALLINE,
                                 "%sDecode is missing parts, written total size %ld", name, threadList[0]->totalBytes);
          UpdateThreadStatus (0, mybuf);
        }
        else if (threadList[0]->contentEncoding != CODE_BASE64 &&
        !threadList[0]->codedBlockList[threadList[0]->numBlocks - 1]->endFlag) 
        {
          _snprintf (mybuf, MAXINTERNALLINE, "%sEnd never found.  Cancelling", name);
          UpdateThreadStatus (0, mybuf);
        }
        else if (threadList[0]->expectedNumBlocks > 0) {        // there IS sequencing info, so we should not be here

          WriteDecodeThread (0);
          _snprintf (mybuf, MAXINTERNALLINE,
                                 "%sDecode is missing parts, written total size %ld", name, threadList[0]->totalBytes);
          UpdateThreadStatus (0, mybuf);
        }
        else {
          _snprintf (mybuf, MAXINTERNALLINE,
                                 "%sCompleteness confidence is medium, written total size %ld", name, threadList[0]->totalBytes);
          UpdateThreadStatus (0, mybuf);
          WriteDecodeThread (0);
        }
        DestroyThread (0);                      // decrements numDecodeThreads

  }
  for (i = 0; i < NumStatusTexts; i++)
        CodingStatusText[i]->IsBusy = FALSE;

  currentCoded = NULL;
  CodingState = INACTIVE;
  CommDecoding = FALSE;
  if (hCodedBlockWnd && IsWindow(hCodedBlockWnd))
  {
		WINDOWPLACEMENT wpl = {0};
		wpl.length =  sizeof(WINDOWPLACEMENT);
		if  (GetWindowPlacement(hCodedBlockWnd, &wpl))
		{
          sprintf (mybuf, "%d,%d", wpl.rcNormalPosition.left, wpl.rcNormalPosition.top);
          WritePrivateProfileString (CODING, "BlockStatusWindowPos", mybuf, szAppProFile);
        }

  }

  DestroyWindow (hCodedBlockWnd);
  hCodedBlockWnd = (HWND) NULL;
  AutoDecode = 0;
  AbortDownload = 0;
}

/* ------------------------------------------------------------------------
 *    Initialize a coded object, and initial data space
 *  Returns ptr to object or NULL if failed
 */
TypCoded * InitCoded (HWND hParentWnd)
{
  TypCoded *thisCoded = NULL;

#ifdef DEBUG_MEM
  sprintf (debug, "\r\nInitializing a coded object");
  DebugLog ();
#endif
  if ((thisCoded = (TypCoded *) GlobalAllocPtr (GHND, sizeof (TypCoded))) == NULL)
        return ((TypCoded *) NULL);

#ifdef DEBUG_MEM
  sprintf (debug, "\r\nAllocing data size %d", BASE_BLOCK_SIZE);
  DebugLog ();
#endif
  if ((thisCoded->data = (char huge *) GlobalAllocPtr (GHND, BASE_BLOCK_SIZE * sizeof (char))) == NULL)
          return ((TypCoded *) NULL);

  thisCoded->maxBytes = BASE_BLOCK_SIZE;
  thisCoded->numBytes = 0;
  thisCoded->numLines = 0;
  thisCoded->sequence = thisPartNum;
  thisCoded->seqConfidence = 0;
  thisCoded->estNumLines = 0;
  thisCoded->name[0] = '\0';
  thisCoded->ident[0] = '\0';
  thisCoded->MimeID[0] = '\0';
  thisCoded->endFlag = thisCoded->beginFlag = FALSE;
  thisCoded->hParentWnd = hParentWnd;
  thisCoded->nMimeState = 0;
  thisCoded->nAppType = 0;
  thisCoded->nTotal = -1;
  thisCoded->dwCRC = 0xFFFFFFFFL;
  thisCoded->dwExpectedBytes = 0;
  thisCoded->dwPartSize = 0;
  thisCoded->bsubjWrap = FALSE;

  return (thisCoded);
}

/* ------------------------------------------------------------------------
 *    Destroy a thread
 *  Destroy all blocks in the thread
 *  Remove the thread from the threadList
 */
void DestroyThread (int num)
{
  register int i;

#ifdef DEBUG_MEM
  sprintf (debug, "\r\nDestroying thread %d", num);
  DebugLog ();
#endif
#ifdef MULTISTATUSWND
  if (CodingStatusVerbose)
  {
        threadList[num]->statusText->IsBusy = FALSE;

		//if (IsWindow(threadList[num]->statusText->hTextWnd)	)
		//	DestroyWindow(threadList[num]->statusText->hTextWnd);
  }
#endif

  for (i = 0; i < threadList[num]->numBlocks; i++)
        DestroyCodedBlock (&(threadList[num]->codedBlockList[i]));

  GlobalFreePtr (threadList[num]);

  for (i = num; i < numDecodeThreads; i++)
        threadList[i] = threadList[i + 1];

  numDecodeThreads--;
  if (currentDecodeThread == num)
        currentDecodeThread = max (0, currentDecodeThread - 1);
}
/* ------------------------------------------------------------------------
 *    Destroy a coded block structure
 *  Free the huge data.  Takes a ptr to a ptr so it can NULL it when done
 */
void
DestroyCodedBlock (TypCoded ** thisCoded)
{
#ifdef DEBUG_MEM
  sprintf (debug, "\r\nDestroying block sequence %d", (*thisCoded)->sequence);
  DebugLog ();
#endif
  GlobalFreePtr ((*thisCoded)->data);
  GlobalFreePtr (*thisCoded);
  *thisCoded = NULL;
}

/* ------------------------------------------------------------------------
 *    Given a file name, find the associated thread number  
 *  Returns -1 if no thread by that name exists
 */
int
GetThreadByName (char *name)
{
  int i, result;

  for (result = -1, i = 0; i < numDecodeThreads && result == -1; i++)
        if (!_stricmp (name, threadList[i]->name))
          result = i;

  return (result);
}
/* ------------------------------------------------------------------------
 *    Determine if a thread has already been started for this ident
 *  if yes, add decode object to that thread
 *  otherwise, alloc a new thread list structure, add this block to it
 *  At end, currentDecodeThread is set to handle of thread
 */
BOOL
AddToThreadList ()
{
  register int i;
  int num = -1;
  char mybuf[MAXINTERNALLINE];
  int x, y, width, height, maxX, maxY;

  if (DumbDecode) 
  {
        if (numDecodeThreads == 0)
          num = -1;
        else
          num = 0;
  }
#if SUPPORT_PARTIAL_MULTIPART //OBSOLUTE S.Yoshida
  else if( currentCoded->endFlag && currentCoded->beginFlag ) 
  {
        num = -1;
  }
#endif
  else 
  {
        /* Search existing threads for an ident match.  If
         * no match found, or no threads exist yet, add new thread.
         */
#if SUPPORT_PARTIAL_MULTIPART
        i = 0;
        if ( thisPartId[0] ) 
		{
          for (i = 0; i < numDecodeThreads; i ++ ) 
		  {
            if( !strcmp( thisPartId, threadList[i]->PARTIALID ) && 
			 	(thisNumBlocks > 1 ?  thisNumBlocks  == threadList[i]->expectedNumBlocks : 1) ) 
			{
              num = i;
              i = -1;
              break;
            }
          }
        } if ( i != -1 )
#endif
        for (num = -1, i = 0; i < numDecodeThreads; i++) 
		{
#if SUPPORT_PARTIAL_MULTIPART

          if (currentCoded->bMime && threadList[i]->bMime) 
		  {
            if ( currentCoded->MimeID[0] && (!_stricmp (currentCoded->MimeID, threadList[i]->PARTIALID)) &&
			 	 (thisNumBlocks > 1 ?  thisNumBlocks  == threadList[i]->expectedNumBlocks : 1) ) 
			{
              num = i;
              break;
            }
          } 
          else 
#endif
          if (!currentCoded->bMime && !threadList[i]->bMime) 
		  {
            if (!_stricmp (currentCoded->ident, threadList[i]->ident))
                num = i;
          }
        }
  }

  if (num == -1) 
  {                              /* add a new thread */
        if ((numDecodeThreads + 1) > MAX_DECODE_THREADS) 
		{
          return (FAIL);
/*          GlobalUnlock(decodeObjectHandles);
   decodeObjectHandles = GlobalRealloc (decodeFileHandles,
   (numDecodeThreads + LIST_SIZE_INC) * sizeof (HGLOBAL), 
   GMEM_MOVEABLE);
   threadList = (HGLOBAL *) GlobalLock (decodeFileHandles);
 */
        }

// create a new DecodeThreadStruct object, store its handle in threadList,
#ifdef DEBUG_MEM
        sprintf (debug, "\r\nCreating new thread ident %.200s", currentCoded->ident);
        DebugLog ();
#endif
        num = numDecodeThreads;

        threadList[num] = (TypDecodeThread *) GlobalAllocPtr (GHND, sizeof (TypDecodeThread));
		if (!threadList[num])
			return (FAIL);
        numDecodeThreads++;

        threadList[num]->expectedNumBlocks = 0;
        threadList[num]->numBlocksWritten = 0;
        threadList[num]->numBlocks = 0;
        threadList[num]->totalBytes = 0;
        threadList[num]->dosFileName[0] = '\0';
        threadList[num]->bMime = FALSE;
        threadList[num]->bStart = currentCoded->beginFlag;
		threadList[num]->previewFlag = 0;
		threadList[num]->bMonitor = 0;

        if (thisContentEncoding == CODE_CUSTOM)
          strntcpy (threadList[num]->customTable, customTable, CODINGTABLESIZE);
        threadList[num]->contentEncoding = thisContentEncoding;
        strntcpy (threadList[num]->ident, currentCoded->ident, MAXINTERNALLINE-1);
#if SUPPORT_PARTIAL_MULTIPART
        strntcpy (threadList[num]->PARTIALID, thisPartId, MAXINTERNALLINE-1);
#endif
        if (currentCoded->name[0] != '\0')
          strntcpy (threadList[num]->name, currentCoded->name, MAXFILENAME-1);
        else
          threadList[num]->name[0] = '\0';

#if SUPPORT_PARTIAL_MULTIPART
        //MDS use real MIME id matching when present 20004011
        if (currentCoded->MimeID[0] != '\0')
          strntcpy (threadList[num]->PARTIALID, currentCoded->MimeID, MAXINTERNALLINE-1);
        threadList[num]->bMime = currentCoded->bMime;
#endif
        if (
#ifdef MULTISTATUSWND
CodingStatusVerbose || 
#endif
			NumStatusTexts == 0) 
		{
          if ((threadList[num]->statusText = InitTextBlock (hCodedBlockWnd)) == NULL)
                WVMessageBox(hCodedBlockWnd, IDS_WVCODING_TEXT_MEM, IDS_WVCODING_MEM_ERROR, MB_OK | MB_ICONSTOP);
                //WVMessageBox (hCodedBlockWnd, "Memory allocation error in text block", "Memory Error", MB_OK);

          if (NumStatusTexts + 1 < MAX_DECODE_THREADS)
          CodingStatusText[NumStatusTexts++] = threadList[num]->statusText;

          width = xScreen >> 1;
          height = (yScreen >> 1) - CaptionHeight;
          maxX = 3 * (xScreen >> 1);      /* 3/4 screen width  */
          maxY = 3 * (yScreen >> 1);     /* 3/4 screen height */

          x = 1;
          y = yScreen >> 2;

          /* If the screen position has been saved, use that instead. */
          GetPrivateProfileString (CODING, "DecodeStatusWindowPos", "!",
                                                         mybuf, MAXINTERNALLINE, szAppProFile);
          if (*mybuf != '!') 
		  {
                sscanf (mybuf, "%d,%d,%d,%d", &x, &y, &width, &height);
          }

#ifdef MULTISTATUSWND
          if (CodingStatusVerbose)
                _snprintf (mybuf, MAXINTERNALLINE, "Decoding Status for file %s", (threadList[num]->name[0] != '\0') ? threadList[num]->name : threadList[num]->ident);
          else
#endif
                strcpy (mybuf, "Decoding Status");

          threadList[num]->statusText->hTextWnd =  
		  
                CreateWindow ("WinVnCoding", mybuf,
                                          WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
                                          (x + (NumStatusTexts * StatusCharWidth)) % maxX,
                                          (y + (NumStatusTexts * StatusLineHeight)) % maxY, 
                                          width, height,
                                          (HWND) NULL, (HMENU) NULL, hInst, (void far *) NULL);

          if (threadList[num]->statusText->hTextWnd == (HWND) NULL) 
		  {
                FreeTextBlock (threadList[num]->statusText);
                WVMessageBox(hCodedBlockWnd, IDS_WVCODING_NO_STATUS,IDS_WVCODING_WINDOW_FAIL, MB_OK | MB_ICONSTOP); 
                //MessageBox (hCodedBlockWnd, "Couldn't create status text window", "Window Creation Error", MB_OK | MB_ICONSTOP);
                return (FAIL);
          }
		  
          SetHandleBkBrush (threadList[num]->statusText->hTextWnd,
                                                hStatusBackgroundBrush);
          ShowWindow (threadList[num]->statusText->hTextWnd,
                                MinimizeStatusWindows ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL);
          UpdateWindow (threadList[num]->statusText->hTextWnd);
        }
        else
          threadList[num]->statusText = CodingStatusText[0];


        threadList[num]->statusText->IsBusy = TRUE;

  }

/* If this new block enlightens us with any useful thread info, update
 * the thread
 */
  if (currentCoded->name[0] != '\0')
  {
    if ( threadList[num]->name[0] == '\0' )
        strntcpy (threadList[num]->name, currentCoded->name, MAXFILENAME-1);
  }

  if (threadList[num]->expectedNumBlocks == 0 && thisNumBlocks > 0)
        threadList[num]->expectedNumBlocks = thisNumBlocks;

  threadList[num]->totalBytes += currentCoded->numBytes;
/*
 * Now, add this decode object to the thread codedBlockList
 * Insert in correct sequence. 
 * If no sequence info available, add to end of list, but before any end block
 */
  if (DumbDecode)
  {
        InsertBlockInThread (num, currentCoded, threadList[num]->numBlocks);
  }
  else 
  {
        if (threadList[num]->numBlocks != 0 && currentCoded->sequence == -1) 
		{  /* sequence unknown, add at end or before end block */
          if (currentCoded->beginFlag)
                InsertBlockInThread (num, currentCoded, 0);
          else 
		  {
                if (threadList[num]->codedBlockList[threadList[num]->numBlocks - 1]->endFlag)
                  InsertBlockInThread (num, currentCoded, threadList[num]->numBlocks - 1);
                else
                  InsertBlockInThread (num, currentCoded, threadList[num]->numBlocks);
          }
        }
        else 
		{
          for (i = 0; i < threadList[num]->numBlocks; i++) 
		  {
                if (threadList[num]->codedBlockList[i]->sequence == -1 ||
#if SUPPORT_PARTIAL_MULTIPART
                        threadList[num]->codedBlockList[i]->sequence > currentCoded->sequence)
#else
                        threadList[num]->codedBlockList[i]->sequence >= currentCoded->sequence)
#endif
                  break;
          }
          InsertBlockInThread (num, currentCoded, i);
        }
  }
  currentDecodeThread = num;
  if (CodingStatusVerbose) 
  {
        sprintf (mybuf, "Decoded block");
        if (currentCoded->sequence == -1) 
		{
          if (currentCoded->beginFlag || currentCoded->endFlag) 
		  {
                if (currentCoded->beginFlag)
                  strcat (mybuf, ", begin block");
                if (currentCoded->endFlag)
                  strcat (mybuf, ", end block");
          }
          else
                strcat (mybuf, " sequence unknown");
        }
        else 
		{
          sprintf (str, " %d", currentCoded->sequence);
          strcat (mybuf, str);
          if (threadList[num]->expectedNumBlocks > 0) 
		  {
                sprintf (str, " of %d.", threadList[num]->expectedNumBlocks);
                strcat (mybuf, str);
          }
          if (currentCoded->sequence == threadList[num]->numBlocksWritten + 1)
                strcat (mybuf, " In sequence");
          else
                strcat (mybuf, " Out of sequence");
        }
        UpdateThreadStatus (num, mybuf);
  }
  thisNumBlocks = -1;

  return (SUCCESS);
}

/* ------------------------------------------------------------------------
 *    Insert/delete a block in a thread list
 *  Inserts and deletes will be O(n).  Since n will never be big,
 *  it's not worth the trouble (and space usage) of using a linked list
 *
 *  index should never be -1 (case of unkown sequence handled by caller)
 *  to insert at front of list, index = 0
 *  to insert at end of list, index = threadList[num]->numBlocks
 */
void
InsertBlockInThread (int num, TypCoded * block, int index)
{
  register int i;

  for (i = threadList[num]->numBlocks; i > index && i > 0; i--)
        threadList[num]->codedBlockList[i] = threadList[num]->codedBlockList[i - 1];

#ifdef DEBUG_DECODE
  sprintf (debug, "\r\nInserting into thread %.200s at posn %d", threadList[num]->ident, i);
  DebugLog ();
#endif
  threadList[num]->codedBlockList[i] = block;
  threadList[num]->numBlocks++;
  return;
}

void
DeleteBlockFromThread (int num, int index)
{
  register int i;

  for (i = index; i < threadList[num]->numBlocks; i++)
        threadList[num]->codedBlockList[i] = threadList[num]->codedBlockList[i + 1];

#ifdef DEBUG_DECODE
  sprintf (debug, "\r\nRemoved from thread %.200s at posn %d", threadList[num]->name, index);
  DebugLog ();
#endif
  threadList[num]->numBlocks--;

  return;
}

/* ------------------------------------------------------------------------
 *    Complete a decode block
 *  Adds to thread list
 *  Writes block to disk if possible (and any others now in sequence)
 *  If it ends a thread, make sure everything is written, and
 *  free up the thread
 */
int	CompleteThisDecode (BOOL bMonitor /*=FALSE*/)
{
  char mybuf[MAXINTERNALLINE] = {0};
  int num, singleBlockDone, foundEnd = 0;
  static BOOL bInDecode = FALSE;

  if (bInDecode)
	return (BUSY);
  if (!currentCoded)
	return (FAIL);

  UpdateBlockStatus (bMonitor);                 // final size displayed
#ifdef DEBUG_DECODE
  sprintf (debug, "\r\nCompleteThisDecode for file %.200s, block %d",
                   currentCoded->ident, currentCoded->sequence);
  DebugLog ();
#endif
  CodingState = DECODE_SKIPPING;

  if (currentCoded->numBytes == 0 ) 
  {
#ifdef DEBUG_DECODE
		sprintf (debug, "\r\nNon-data block.  Discarding it.");
        DebugLog ();
#endif
		
		DestroyCodedBlock (&currentCoded);
        thisContentEncoding = CODE_UNKNOWN; /* ++ S.Yoshida */
        return (SUCCESS);
  }

  if (DumbDecode) 
  {
        if (numDumbDecoded == 0 && !currentCoded->beginFlag) 
		{
          thisContentEncoding = CODE_UNKNOWN; /* ++ S.Yoshida */
          return (SUCCESS);                     // Dumb skipping a data block
        }

        if (numDumbDecoded > 0 && currentCoded->beginFlag) 
		{    // found next begin without an end

          num = currentDecodeThread;
          if (CodingStatusVerbose)
                _snprintf (mybuf, MAXINTERNALLINE,
                                   "Decode missing end, total size %ld, wrote to file %s", 
								   threadList[num]->totalBytes, threadList[num]->dosFileName);
          else
                _snprintf (mybuf, MAXINTERNALLINE,
                                "%s   Decode missing end, total size %ld, wrote to file %s",
                                   threadList[num]->name, threadList[num]->totalBytes, threadList[num]->dosFileName);
          UpdateThreadStatus (num, mybuf);
          DestroyThread (num);
          numDumbDecoded = 0;
        }
  }
  else if (currentCoded->sequence == 1 && currentCoded->seqConfidence == HIGH && !currentCoded->beginFlag) 
  {
			currentCoded->beginFlag = TRUE;
			strntcpy (currentCoded->name, currentCoded->ident, MAXFILENAME-1);
  }

  if (AddToThreadList () == FAIL) 
  {
       thisContentEncoding = CODE_UNKNOWN; /* ++ S.Yoshida */
       return (FAIL);
  }


  num = currentDecodeThread;

  if (bMonitor)
  {
	threadList[num]->previewFlag = TRUE;
	threadList[num]->bMonitor = bMonitor;
  }


  if ( (threadList[num]->contentEncoding == CODE_BASE64 || threadList[num]->contentEncoding == CODE_YENC) &&
          threadList[num]->expectedNumBlocks != 0 &&
          currentCoded->sequence == threadList[num]->expectedNumBlocks) 
  {
        /* base-64 end flag if last block */
        currentCoded->endFlag = TRUE;
  }

  prevContentEncoding = thisContentEncoding;
  thisContentEncoding = CODE_UNKNOWN;

  singleBlockDone = currentCoded->beginFlag && currentCoded->endFlag;

  if (
#ifdef MULTISTATUSWND  
  !CodingStatusVerbose && 
#endif
	(threadList[num]->numBlocksWritten == 0 && threadList[num]->numBlocks == 1)) 
  {
        _snprintf (mybuf, MAXINTERNALLINE, "%.100s   Decode in progress", (threadList[num]->name[0] != '\0') ? threadList[num]->name : threadList[num]->ident);
        UpdateThreadStatus (num, mybuf);
  }

  if (threadList[num]->contentEncoding == CODE_YENC)
  {
	if ( (currentCoded->dwCRC ^ 0xFFFFFFFFL) !=  currentCoded->dwCRCExpected)
	{
          _snprintf (mybuf, MAXINTERNALLINE, "%.100s   Part %d fails yEncode CRC check. Data corrupted", 
			(threadList[num]->name[0] != '\0') ? threadList[num]->name : threadList[num]->ident, 
			 currentCoded->sequence);
          UpdateThreadStatus (num, mybuf);

	}
	if (currentCoded->dwPartSize != currentCoded->numBytes)
	{
		//TODO: ?? what
#ifdef DEBUG_DECODE
                sprintf (debug, "\r\nListed PartSize(%ld) != numBytes(%ld)",
                currentCoded->dwPartSize, currentCoded->numBytes);
                DebugLog ();
#endif

	}
  }

  if (DumbDecode) 
  {
        bInDecode = TRUE;

		if (WriteTopBlockToFile (num, &foundEnd, bMonitor) == FAIL)
		{
		  bInDecode = FALSE;
          return (FAIL);
		}
		bInDecode = FALSE;
        numDumbDecoded++;
  }
  else 
  {
#if 1//MDS - I turned this on since without it, it prevents sequenced files 
     //from being written, which thus causes articles to remain in memory
     //! SUPPORT_PARTIAL_MULTIPART
/* If this block is both begin and end, then go straight to WriteDecodeThread
 * If currentCoded is in sequence, then it was added as block 0 in the 
 * thread's block list, and may have caused other blocks to now be in 
 * sequence as well.  Write all blocks which are in sequence now.
 */
        if (!singleBlockDone && currentCoded->sequence != -1 &&
                currentCoded->seqConfidence != 0)
          while (threadList[num]->numBlocks != 0) 
		  {
                if (threadList[num]->codedBlockList[0]->sequence == threadList[num]->numBlocksWritten) 
				{        // skip duplicate block sequence

                  DestroyCodedBlock (&currentCoded);    // threadList[num]->codedBlockList[0]

                  DeleteBlockFromThread (num, 0);
                  break;
                }
                if (threadList[num]->codedBlockList[0]->sequence != threadList[num]->numBlocksWritten + 1)
                  break;
#ifdef DEBUG_DECODE
                sprintf (debug, "\r\nBlock in sequence, writing it, file %.200s, block %d",
                threadList[num]->name, threadList[num]->codedBlockList[0]->sequence);
                DebugLog ();
#endif
                bInDecode = TRUE;              

				if (WriteTopBlockToFile (num, &foundEnd, bMonitor) == FAIL)
				{
				  bInDecode = FALSE;

                  return (FAIL);
				}
				bInDecode = FALSE;
          }
#endif
  }
  if (  singleBlockDone || 
       (foundEnd && DumbDecode) ||
       (foundEnd && threadList[num]->expectedNumBlocks > 0 && threadList[num]->numBlocksWritten >= threadList[num]->expectedNumBlocks)) 
  {
#ifdef DEBUG_DECODE
        sprintf (debug, "\r\nThread complete: file %.200s", threadList[num]->name);
        DebugLog ();
#endif
        if (bMonitor)
			threadList[num]->bMonitor = bMonitor;
		bInDecode = TRUE;
		if (WriteDecodeThread (num) == FAIL) 
		{
          DestroyThread (num);
          currentCoded = NULL;
		  bInDecode = FALSE;

          return (FAIL);
        }
		bInDecode = FALSE;

        if (CodingStatusVerbose)
          _snprintf (mybuf, MAXINTERNALLINE,
                                 "Decode complete, total size %ld, wrote to file %s", threadList[num]->totalBytes, threadList[num]->dosFileName);
        else
          _snprintf (mybuf, MAXINTERNALLINE,
                                 "%.100s   Decode complete, total size %ld, wrote to file %.200s",
                                 threadList[num]->name, threadList[num]->totalBytes, threadList[num]->dosFileName);
        UpdateThreadStatus (num, mybuf);

        DestroyThread (num);
        numDumbDecoded = 0;
  }
  currentCoded = NULL;
  return (SUCCESS);
}

/* ------------------------------------------------------------------------
 *    Write data for the top block of a thread to a file, and remove
 *  the top block from the thread
 */
int WriteTopBlockToFile (int num, int *endFlag, BOOL bMonitor /*=FALSE*/)
{
  char mybuf[MAXINTERNALLINE];

  threadList[num]->bStart = TRUE;
  WVString last;
  if (bMonitor)
  {
	last = DecodePathName;
	GetTempPath(MAXFILENAME, DecodePathName);
  }
  
  
  if (WriteBlockToFile (num, threadList[num]->codedBlockList[0]) == FAIL) 
  {
        _snprintf (mybuf, MAXINTERNALLINE,
                           "Could not write to file %s", threadList[num]->dosFileName);
        UpdateThreadStatus (num, mybuf);
        DestroyThread (num);
        if (bMonitor)
			strntcpy(DecodePathName, last, MAXFILENAME-1);
        return (FAIL);
  }

  *endFlag = threadList[num]->codedBlockList[0]->endFlag;
  DestroyCodedBlock (&(threadList[num]->codedBlockList[0]));
  DeleteBlockFromThread (num, 0);
  threadList[num]->numBlocksWritten++;
  if (bMonitor)
	strntcpy(DecodePathName, last, MAXFILENAME-1);  
  return (SUCCESS);
}

/* ------------------------------------------------------------------------
 *    Write data for a thread to a file
 */
int WriteDecodeThread (int num)
{
	register int i;
	char mybuf[MAXINTERNALLINE];
#if SUPPORT_PARTIAL_MULTIPART
	const char *prevName = 0;
#endif

	for (i = 0; i < threadList[num]->numBlocks; i++) 
	{
#ifdef DEBUG_DECODE
		sprintf (debug, "\r\nSaving sequence %d", threadList[num]->codedBlockList[i]->sequence);
		DebugLog ();
#endif
#if SUPPORT_PARTIAL_MULTIPART
		if (threadList[num]->codedBlockList[i]->ident[0] != 0 &&
			(prevName == NULL || strcmp(threadList[num]->codedBlockList[i]->ident, prevName)) ) 
		{
			//MDS why? multipart multifile??? 
			if (AbortDownload && threadList[num]->bMonitor)
			{
				//delete this unneeded file
				if (threadList[num]->dosFileName)
					DeleteFile(threadList[num]->dosFileName);
			}			
			
			threadList[num]->dosFileName[0] = '\0';
			prevName = threadList[num]->codedBlockList[i]->ident;
		}
#endif
		if (WriteBlockToFile (num, threadList[num]->codedBlockList[i]) == FAIL) 
		{
			_snprintf (mybuf, MAXINTERNALLINE, "Could not write to file %s", threadList[num]->dosFileName);
			UpdateThreadStatus (num, mybuf);
			return (FAIL);
		}
		else
		{
			threadList[num]->numBlocksWritten++;
		}
	}
	if (AbortDownload && threadList[num]->bMonitor && threadList[num]->dosFileName[0])
	{
		//delete this unneeded file
		if (threadList[num]->dosFileName)
			DeleteFile(threadList[num]->dosFileName);
		return (SUCCESS);
	}
	if (ExecuteDecodedFiles || threadList[num]->bMonitor)
	{
		//don't run partial files.  MDS 20000413
		if ( threadList[num]->bStart && (threadList[num]->numBlocksWritten >= threadList[num]->expectedNumBlocks
			|| (threadList[num]->codedBlockList[0] && threadList[num]->codedBlockList[0]->endFlag) ) )
		{
			threadList[num]->hProcess = ExecuteDecodedFile (num, threadList[num]->dosFileName);
			if (threadList[num]->bMonitor)
			{
				_snprintf (mybuf, MAXINTERNALLINE, "Monitoring file %s", threadList[num]->dosFileName);
				UpdateThreadStatus (num, mybuf);
				
				AddMonitoredFile(threadList[num]->hProcess , threadList[num]->dosFileName);
			}
		}
		else if (threadList[num]->bMonitor)
		{
		AddMonitoredFile(NULL , threadList[num]->dosFileName);
		}
	}

	return (SUCCESS);
}

/* ------------------------------------------------------------------------
 *    Write block data to a file
 *  Verify thread file name will work in DOS, and request a new file
 *  name if necessary
 *  Accepts handle of parent window to allow any necessary dialogs
 */
int WriteBlockToFile (int num, TypCoded * thisDecode)
{  
  char actualFileName[MAXFILENAME*2], mybuf[MAXINTERNALLINE];
  UINT fileMode;

#ifdef _WIN32

	HANDLE hWriteFile;
	DWORD dwWrite;
#else

	OFSTRUCT outFileStruct;
	HFILE hWriteFile;
	unsigned int chunkSize;
	register unsigned long i;

	memset(&outFileStruct, 0, sizeof(OFSTRUCT));
	outFileStruct.cBytes = sizeof(OFSTRUCT);

#define CHUNK_SIZE 65500                /* must not exceed UINT (65534) */

#endif

	if (threadList[num]->name[0] != '\0' && thisDecode->name[0] != '\0' &&
	strcmp( threadList[num]->name, thisDecode->name) &&
	threadList[num]->dosFileName[0] != '\0')
	{

		_snprintf (mybuf, MAXINTERNALLINE,
							 "Ending decode for %s, wrote %ld bytes.", threadList[num]->dosFileName, threadList[num]->totalBytes-thisDecode->numBytes );
		UpdateThreadStatus (num, mybuf);

		_snprintf( mybuf, MAXINTERNALLINE, " New filename to decode to:\"%.200s\"", thisDecode->name);

		UpdateThreadStatus (num, mybuf);

		//new filename for this part?
		threadList[num]->dosFileName[0] = 0;
		strntcpy(threadList[num]->name, thisDecode->name, MAXFILENAME-1);
		threadList[num]->totalBytes = thisDecode->numBytes;

	}

	if (threadList[num]->dosFileName[0] != '\0') 
	{
		fileMode = OF_WRITE;

		if (strchr (threadList[num]->dosFileName, '\\') == NULL) 
		{
			if (threadList[num]->previewFlag)
			{
				GetTempPath(MAXFILENAME, actualFileName);
			}
			else
			{
				strntcpy (actualFileName, DecodePathName, MAXFILENAME-1);
			}
			if (actualFileName[strlen (actualFileName) - 1] != '\\')
				strcat (actualFileName, "\\");
			strncat (actualFileName, threadList[num]->dosFileName, MAXFILENAME-strlen(actualFileName)-1);
		}
		else
		  strntcpy (actualFileName, threadList[num]->dosFileName, MAXFILENAME-1);
	}
	else 
	{
		/* First time write to file
		 */
		fileMode = OF_CREATE;

		*actualFileName = '\0';
#if SUPPORT_PARTIAL_MULTIPART
        if (thisDecode->name[0] != '\0')
			strntcpy (threadList[num]->dosFileName, thisDecode->name, MAXFILENAME-1);
        else if (thisDecode->ident[0] != '\0')
        {
            //make new filename based on (possibly) correct ident name
            strntcpy (threadList[num]->dosFileName, thisDecode->ident, MAXFILENAME-1);
            switch (thisDecode->nAppType)
            {
                case APPLEFILE:
                    strcat(threadList[num]->dosFileName, ".apl");
                    break;
                case TEXTFILE:
                    strcat(threadList[num]->dosFileName, ".txt");
                    break;
                case HTMLFILE:
                    strcat(threadList[num]->dosFileName, ".htm");
                    break;
                case 0:
                default:
                    break;
            }
        }
#endif
        else if (threadList[num]->name[0] != '\0')
			strntcpy (threadList[num]->dosFileName, threadList[num]->name, MAXFILENAME-1);
        else if (threadList[num]->ident[0] != '\0')
			strntcpy (threadList[num]->dosFileName, threadList[num]->ident, MAXFILENAME-1);
#if 1 /* S.Yoshida */
        else 
        {  // make random name
			char *p = threadList[num]->dosFileName;
			static unsigned long y = 0;
			time_t x = (time(0) + (rand() / 37 * rand()) / 12);
			++ y;
			p[0] = base64Table[ x % 62 ]; x /= 62;
			p[1] = base64Table[ x % 62 ]; x /= 62;
			p[2] = base64Table[ x % 62 ]; x /= 62;
			p[3] = base64Table[ x % 62 ]; x = y;
			p[4] = base64Table[ x % 62 ]; x /= 62;
			p[5] = base64Table[ x % 62 ]; x /= 62;
			p[6] = base64Table[ x % 62 ]; x /= 62;
			p[7] = base64Table[ x % 62 ]; x = y;
			strcpy( p + 8, ".txt" );
        }
#endif

        if (threadList[num]->dosFileName[0] == '\0')
			return (FAIL);

		if (threadList[num]->previewFlag)
		{
			GetTempPath(MAXFILENAME, actualFileName);
		}
		else
		{
        
			strntcpy (actualFileName, DecodePathName, MAXFILENAME-1);
		}
        if (actualFileName[strlen (actualFileName) - 1] != '\\')
			strcat (actualFileName, "\\");
        strcat (actualFileName, threadList[num]->dosFileName);
		//Should this be thisDecode->hParentWnd ?
		//If the connection is terminated while this dialog is up,
		//the heartbeat causes continuous re-execution of this code 
		//until stack blows.
		{
			int nOldState = CommState; 
			CommState = ST_WAIT_COMM;
			//if (SmartFile (hCodedBlockWnd, actualFileName) == FAIL)
			if (SmartFile (thisDecode->hParentWnd, actualFileName) == FAIL)
			{
			  CommState = nOldState;
			  return (FAIL);
			}
			CommState = nOldState;
		}
	}
	strntcpy (threadList[num]->dosFileName, actualFileName, MAXFILENAME);



#ifdef _WIN32

	if ( (hWriteFile = CreateFile(actualFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
						fileMode == OF_CREATE ? CREATE_ALWAYS : OPEN_EXISTING, 
						FILE_ATTRIBUTE_NORMAL, NULL))  == INVALID_HANDLE_VALUE )
	{
		/* last chance to fix... smart filer didn't get a good name... */
		actualFileName[0] = '\0'; //otherwise Askfile won't even ask
		if (AskForNewFileName (hCodedBlockWnd, actualFileName, DecodePathName, FALSE) == FAIL ||
			( (hWriteFile = CreateFile(actualFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
			fileMode == OF_CREATE ? CREATE_ALWAYS : OPEN_EXISTING, 
			FILE_ATTRIBUTE_NORMAL, NULL))  == INVALID_HANDLE_VALUE ) ) 
		{
			WVMessageBox(hCodedBlockWnd, IDS_WVCODING_OPEN_FAILURE, IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP);
			return (FAIL);
		}
		else
		{
			//save user selected filename!
			strntcpy (threadList[num]->dosFileName, actualFileName, MAXFILENAME);
		}
	}
	if (fileMode == OF_WRITE)
	{
		long lHigh = 0;
		DWORD dwLow;
		dwLow = SetFilePointer (hWriteFile, 0L, &lHigh, FILE_END);    /* append */
		if (dwLow == (DWORD) -1 && (GetLastError()) != NO_ERROR )
		{
			WVMessageBox(hCodedBlockWnd, IDS_WVCODING_WRITE_FAILED,IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP); 
			CloseHandle (hWriteFile);
			return (FAIL);
		}

	}

	if (!WriteFile (hWriteFile, thisDecode->data, thisDecode->numBytes, &dwWrite, NULL)
		|| dwWrite != thisDecode->numBytes )
	{

		WVMessageBox(hCodedBlockWnd, IDS_WVCODING_WRITE_FAILED,IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP); 
		CloseHandle (hWriteFile);
		return (FAIL);
	}

	CloseHandle (hWriteFile);

#else  //Win16




	if ((hWriteFile = OpenFile ((char far *) actualFileName, &outFileStruct, fileMode)) < 0) 
	{

		/* last chance to fix... smart filer didn't get a good name... */
		actualFileName[0] = '\0'; //otherwise Askfile won't even ask
		if (AskForNewFileName (hCodedBlockWnd, actualFileName, DecodePathName, FALSE) == FAIL ||
			(hWriteFile = OpenFile ((char far *) actualFileName, &outFileStruct, fileMode)) < 0) 
		{
			WVMessageBox(hCodedBlockWnd, IDS_WVCODING_OPEN_FAILURE, IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP);
			//MessageBox (hCodedBlockWnd, "Unable to open output file", "File Error", MB_OK | MB_ICONSTOP);
			return (FAIL);
		}
		else
		{
			//save user selected filename!
			strntcpy (threadList[num]->dosFileName, actualFileName, MAXFILENAME-1);
		}
	}
	if (fileMode == OF_WRITE)
		_llseek (hWriteFile, 0L, 2);    /* append */

	for (i = 0L; i < thisDecode->numBytes; i += chunkSize) 
	{
		if (i + CHUNK_SIZE > thisDecode->numBytes)
			chunkSize = (unsigned int) (thisDecode->numBytes - i);
		else
			chunkSize = CHUNK_SIZE;

		if (_lwrite (hWriteFile, &(thisDecode->data[i]), chunkSize) != chunkSize) 
		{
			WVMessageBox(hCodedBlockWnd, IDS_WVCODING_WRITE_FAILED,IDS_WVCODING_FILE_ERROR, MB_OK | MB_ICONSTOP); 
			//MessageBox (hCodedBlockWnd, "Error writing to file", "File Error", MB_OK | MB_ICONSTOP);
			_lclose (hWriteFile);
			return (FAIL);
		}
	}

	_lclose (hWriteFile);

#endif
	if (CodingStatusVerbose) 
	{
		strcpy (mybuf, "     Wrote block ");
		if (thisDecode->sequence > 0) 
		{
			sprintf (str, "%d ", thisDecode->sequence);
			strcat (mybuf, str);
		}
		//      sprintf (str, "to file %s", threadList[num]->dosFileName);
		//      strcat (mybuf, str);
		UpdateThreadStatus (num, mybuf);
	}
	return (SUCCESS);
}

int TestEndLine(const char *line) //Obsolute S.Yoshida
{
	if (thisContentEncoding == CODE_BASE64) 
	{ 
		return (FALSE);
	}
	if (thisContentEncoding == CODE_YENC)
	{
		if (!strncmp(line, "=yend ", 6))
			return TRUE;
		return FALSE;
	}
	if (!strncmp(line, "end", 3)) 
	{
		return (TRUE);  
	}

	return (FALSE);


}

/* ------------------------------------------------------------------------
 *    Decode a line in article.  Store in thisDecode
 */
int DecodeLine (TypCoded * thisDecode, char *line, BOOL bMonitor /*=FALSE*/)
{
  register int i;
  static int table_count;
  char thisBlockName[MAXHEADERLINE], *ptr;
  int mode;
  BOOL bFirst = FALSE;

  if (!thisDecode)
	return FAIL;


  bFirst = thisDecode->numBytes == 0;
  /* ignore blank lines */
#if 1 //S.Yoshida
  if (*line == '\0')
        return (SUCCESS);
#else
  if (*line == '\0' || (*line == '\n' && *(line + 1) == '\0') ||
          (*line == '\r' && *(line + 1) == '\n' && *(line + 2) == '\0'))
        return (SUCCESS);
#endif
  thisDecode->numLines++;
  if (thisDecode->numLines % STATUS_UPDATE_FREQ == 0)
        UpdateBlockStatus (bMonitor);

  if (UsingMIME && CodingState == DECODE_SKIPPING && thisDecode->nMimeState != MIMEINIT)
  {
    if (ParseMimeHeaderLine (thisDecode, line))
        return (SUCCESS);
  }
  
  if (CodingState == DECODE_YENC_HEADER)
  {

	CodingState = DECODE_PROCESSING;

	if (!strncmp(line, "=ypart",6) )
	{
		//Yenc is a multipart file
		char *p = strstr( line, "begin=");
		DWORD dwBegin = 0, dwEnd = 0;
		
		//some broken encoders use part size instead of total file size for this param!

		if (p)
			dwBegin = atol(p+6);
		p = strstr( line, "end=");
		if (p)
			dwEnd = atol(p+4);
		if (thisDecode->sequence == 1 && thisDecode->nTotal > 1 && 	thisDecode->dwExpectedBytes == dwEnd)
			thisDecode->dwExpectedBytes = 0;
		else if (thisDecode->dwExpectedBytes < dwBegin || thisDecode->dwExpectedBytes < dwEnd)
		{
			thisDecode->dwExpectedBytes = 0;
		} 
		if (thisDecode->nTotal == -1 && thisDecode->dwExpectedBytes > 0 && thisDecode->dwExpectedBytes > dwEnd )
		{
			//broken encoder not listing total= in =ybegin line
			thisDecode->nTotal = thisDecode->dwExpectedBytes/dwEnd + 1;
			thisDecode->endFlag = FALSE;
		}

		
		//this line contains begin=n end=o  where n & o are the starting pos of the data & size
		return (SUCCESS);
	}
	//otherwise we only expect a single part, and the header line
#if SUPPORT_PARTIAL_MULTIPART
	thisPartNum =
#endif
	thisDecode->sequence = 1;
	thisDecode->nTotal = 1;
	thisDecode->beginFlag = TRUE;
	thisDecode->seqConfidence  = HIGH;
	thisNumBlocks = 1;

  }
  
  switch (CodingState) 
  {
    case DECODE_SKIPPING:
        if (strlen (line) < 3)          // if we're skipping, ignore any really short line
        {
			thisDecode->bsubjWrap = FALSE;
			return (SUCCESS);
        }
        
        if (thisDecode->bsubjWrap)
        {	if (!isspace(*line))
				thisDecode->bsubjWrap = FALSE;
			else
			{	
				const char* p = line;
				while (*p && isspace(*p))
					p++;
				int nTotal = sizeof(subjWrapLine)/sizeof(subjWrapLine[0]);
				int nCur = strlen(subjWrapLine);
				strncpy(subjWrapLine+nCur, p, nTotal - nCur -1 );
				subjWrapLine[nTotal-1] = '\0';  
				ParseInfoLine (thisDecode, subjWrapLine, TRUE);
			
			}
		}

        // encoded UU/XX/Custom data always starts with line 
        // like 'begin <mode> <file-name>'
        if (!strncmp (line, "begin", 5) && sscanf (line, "%*s %d %s", &mode, thisBlockName) == 2) 
        {
#ifdef DEBUG_DECODE
          sprintf (debug, "\r\nStart of new encoded file %.200s, mode %d", thisBlockName, mode);
          DebugLog ();
#endif
    
          if (thisBlockName[0] != '\0') 
          {
            // MDS copy full line as path if filename doesn't include '.' (path has a space)
            char * p;
            //p = strrchr(thisBlockName, '.');
            //if (p == NULL || *(p+1) == '\0') 
            {
                p = strstr(line+5, thisBlockName);
                strntcpy(thisBlockName, p, MAXHEADERLINE-1 ); //must avoid very long filename
                thisBlockName[MAXHEADERLINE-1] = '\0';
				while ( *thisBlockName && isspace((unsigned char)thisBlockName[strlen(thisBlockName)-1]) )
				{
					thisBlockName[ strlen(thisBlockName)-1 ] = '\0';	
				}
            }
            NameWithoutPath (thisDecode->name, /*strlwr*/ (thisBlockName)); //preserve filename case
          } 
          else 
          {
            //no filename on begin line?
            //MDS 2K2 NameWithoutPath (thisDecode->name, thisDecode->ident);
          }

          //SubsituteCharInString(thisDecode->name,' ','_');
          //SubsituteCharInString(thisDecode->name,'?','_'); // some filesystems fails to create a filename
          //SubsituteCharInString(thisDecode->name,'*','_'); // with these chars

#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum =
#endif

          thisDecode->sequence = 1;
          thisDecode->beginFlag = TRUE;
//          thisDecode->mode = mode;

          PreAllocDataByLines (thisDecode);
          CodingState = DECODE_PROCESSING;
          return (SUCCESS);
        }
		else if (!strncmp (line, "=ybegin ", 8) )
		{
			
			BOOL bOK = TRUE;
			
			char * p = strstr(line, "name=");
			if (p)
			{

				if (!_strnicmp(p+5, "(null)", 6) )
				{
					//broken poster using filename of "(null)", use subject filename instead
					int nLen, nSeq, nTotal, nGS, nGE;
					const char* pName =ParseSubject(thisDecode->ident, &nLen, &nSeq,&nTotal, &nGS, &nGE,  NULL);
					
					if (pName && nLen > 0)
					{
						if (nLen >= MAXHEADERLINE)
							nLen = MAXHEADERLINE-1;
						strntcpy(thisBlockName, pName, nLen);
						thisBlockName[nLen] = '\0';
					}
				}
				else
				{
					
					//parameter must be included
					strntcpy(thisBlockName, p+5, min(strlen(p+4), MAXHEADERLINE-1)); 
					thisBlockName[MAXHEADERLINE-1] = '\0';

				}

				char * r= thisBlockName + strlen(thisBlockName)-1;
				while (r > thisBlockName && (*r < '\x21')) //trim trailing whitespace
					*(r--) = '\0';

				int nLen = strlen(thisBlockName);
				
				NameWithoutPath (thisDecode->name, /*strlwr*/ thisBlockName); //preserve filename case

				if (VerifyFileName (thisDecode->name) == FAIL)
				{
					//it's gonna ask us for a new filename...
					if (bMonitor ||   ( numDecodeThreads > 0 ? threadList[currentDecodeThread]->bMonitor : 0 ) )
					{
						int eLen = 0;
						int fLen = nLen;
						char * e = strrchr(p+5, '.');
						if (e)
						{
							eLen = strlen(e);
							fLen = e-(p+5);

							if (eLen > MaxFileExtLen)
								eLen = MaxFileExtLen -1;
							if (fLen > MaxFileNameLen)
								fLen = MaxFileNameLen;
							if (fLen + eLen > MAXFILENAME-1)
								fLen = MAXFILENAME-1 - eLen;
								 
							strntcpy(thisDecode->name + fLen, e, eLen);
							thisDecode->name[fLen+eLen] = 0; 
						}
						else
						{
							//no extension? not gonna run
						}
						
					}
				}

				
				thisDecode->MimeID[0] = '\0';
				thisPartId[0] = 0;
				currentCoded->bMime = FALSE;
			}
			else
			{
				bOK = FALSE;
			}

			thisDecode->beginFlag = TRUE;
			CrcInit(&thisDecode->dwCRC );

			p = strstr( line, "part=");
			if (p)
			{
                if (thisDecode->sequence < 2)
                {
#if SUPPORT_PARTIAL_MULTIPART
				    thisPartNum =
#endif
				
                    thisDecode->sequence = atol(p+5);
                }
				if (thisDecode->sequence != 1)
					thisDecode->beginFlag = FALSE;
				thisDecode->seqConfidence  = HIGH;
				char *t = strstr(line, "total=");
				if (t)
				{
					//override sequence numbers
					thisDecode->sequence = atol(p+5);
					thisDecode->nTotal = atol(t+6);
					thisDecode->beginFlag = thisDecode->sequence ==1;
					thisDecode->endFlag	= thisDecode->sequence ==thisDecode->nTotal;
				}

			}
			p = strstr( line, "size=");
			
			if (p)
			{
				//some broken encoders use part size instead of total file size for this param!
				//parameter must be included
				thisDecode->dwExpectedBytes = atol(p+5); 
			}
			else
			{
				bOK = FALSE;
			}

			if (bOK)
			{
				thisContentEncoding = CODE_YENC;
				CodingState = DECODE_YENC_HEADER;
				PreAllocDataByLines (thisDecode);

#ifdef DEBUG_DECODE
          sprintf (debug, "\r\nStart of new yEnc encoded file %.200s", thisBlockName);
          DebugLog ();
#endif
				return (SUCCESS);
			}
		}
/* Check for keywords in the non-data lines which may aid in describing
 * the name for this data, the sequence, a custom table, etc
 */
        else if (!DumbDecode && !_strnicmp (line, "subject:", 8)) 
        {
#if SUPPORT_PARTIAL_MULTIPART
          /* UsingMIME = FALSE; */
          thisPartId[0] = 0;
          thisPartNum = 0;
          lastNumParts = -1;
#endif
          prevBlockIdent[0] = '\0';
          thisDecode->bsubjWrap = TRUE;
          strncpy(subjWrapLine, line+8, sizeof(subjWrapLine)/sizeof(subjWrapLine[0]));

          ParseInfoLine (thisDecode, &line[8], TRUE);
        }
        else if (!DumbDecode && !_strnicmp (line, "summary:", 8))
          ParseInfoLine (thisDecode, &line[8], FALSE);
        else if (!UsingMIME && !_strnicmp (line, "MIME-Version:", 13))
          UsingMIME = TRUE;
        else if (!DumbDecode && ParseAppSpecificLine (thisDecode, line))
          return (SUCCESS);
        else if (!DumbDecode && !_strnicmp (line, "lines:", 6)) 
        {
          for (ptr = &line[6]; isspace ((unsigned char)*ptr); ptr++);  /* skip spaces */
          thisDecode->estNumLines = atol (ptr);
          return (SUCCESS);
        }
        else if (!strncmp (line, "table", 5)) {         // next two lines are table

          CodingState = DECODE_GET_TABLE;
          table_count = 0;
          return (SUCCESS);
        }
//  else if (!strncmp(line, "Content-Type:", 13)) {
//      // This is where we'd probably decode new filename
//      // SubsituteCharInString(threadList[num]->name,' ','_');
//      //
//      //  (JD 3/27/97)
//      
//    return (SUCCESS);
//  }

/*   else if (ParseMimeHeaderLine(thisDecode, line) == SUCCESS)
             return (SUCCESS);   */
 
// Skipping to find new block of data. Skip until a data line 
//   (a line with the correct length) is found
        else if (IsDataLine (line, bFirst)) 
        {

          /*
          if (thisDecode->sequence = 1 && thisDecode->beginFlag != TRUE &&
             (thisContentEncoding == CODE_UU || thisContentEncoding == CODE_XX) )
          {
            //lets be a bit more selective!
            //with out a begin, we do not start decoding
            return (SUCCESS);
          }
          */

          // found good line, process it and continue processing
#ifdef DEBUG_DECODE
          sprintf (debug, "\r\nFound start of next data section");
          DebugLog ();
#endif
          PreAllocDataByLines (thisDecode);
          CodingState = DECODE_PROCESSING;
          return (DecodeDataLine (thisDecode, line));
        }
#ifdef DEBUG_DECODE
        sprintf (debug, "\r\nskipped: %.200s", line);
        DebugLog ();
#endif
        return (SUCCESS);                       // continue skipping 

  case DECODE_GET_TABLE:
        // This is one of two lines containing table info
        memmove ((customTable + table_count * 32), line, 32);

        if (++table_count == 2) 
		{
#ifdef DEBUG_MAP
          strntcpy (str, customTable, CODINGTABLESIZE);
          str[CODINGTABLESIZE] = '\0';
          sprintf (debug, "\r\nfound table: %s", str);
          DebugLog ();
#endif
          CodingState = DECODE_SKIPPING;
          if (i = CreateCodingMap (CodingMap[CODE_CUSTOM], customTable) != -1) 
		  {
#ifdef DEBUG_MAP
                sprintf (debug, "Invalid decoding table in block.  Duplicate character %c.", i);
                DebugLog ();
#endif
                customTable[0] = '\0';  // ditch the table

          }
          else
                thisContentEncoding = CODE_CUSTOM;
        }
        return (SUCCESS);

  case DECODE_PROCESSING:

          //Okay, here's a longer description of why this is needed.
          //When a single message contains multiple mime encoded files
          //WinVN will not decode the files because it thinks the end 
          //has not been found for that file. Using the following code, 
          //an error message appears in the decode window for the file, 
          //but the file is successfully written.   MDS 19991214

          if ( UsingMIME && !IsDataLine (line, bFirst) ) 
          {

            //MDS Recognize an end of sequence marker in MIME!
            if ( *thisBoundary &&
              (!strcmp (&line[2], thisBoundary) || !strcmp (&line[2], thisBoundaryEnded))) 
            {

              //if there is nothing decoded, ignore it
              if (thisDecode->numBytes > 100)
              {
                thisDecode->endFlag = TRUE;
                if (thisNumBlocks == -1 && thisDecode->sequence != -1)
                  thisNumBlocks = thisDecode->sequence;
                
                CodingState = DECODE_SKIPPING;
                prevContentEncoding = thisContentEncoding;
                thisContentEncoding = CODE_UNKNOWN;

                return (END_BLOCK);
              }
            }                   
          }

          if (UsingMIME && thisDecode->numBytes > 100 && !thisDecode->endFlag)
          {
            
            if (thisNumBlocks != -1 && thisNumBlocks == thisDecode->sequence)
              thisDecode->endFlag = TRUE;

            if (thisNumBlocks == -1 && thisDecode->sequence != -1)
            {
                thisNumBlocks = thisDecode->sequence;
            }
            
          }

/* Some encoders place an END line at end of a section, and length it 
 * like a proper encoded line.Make sure we catch it, don't decode it 
 * as data, and switch back to skipping. Note the only End statement 
 * which means End of entire file is lower case "end".  The other tags 
 * are ignored, i.e. "END", "End_of_section", etc
 */
        if ( thisContentEncoding == CODE_YENC && !strncmp(line, "=yend ", 6) )
		{
			//end of this part of yEnc
			char *p = strstr(line, "pcrc32=");
			if (p)
			{
				if (!ReadHEX32( p+7, &thisDecode->dwCRCExpected))
				{
					thisDecode->dwCRCExpected = 0;	
				}	
			}
			else if ( (p = strstr(line, "crc32=") )	)
			{
				//this the the last block of the (possibly) multipart yEnc
				if (!ReadHEX32( p+6, &thisDecode->dwCRCExpected))
				{
					thisDecode->dwCRCExpected = 0;
				}	
			}
			if ( (p = strstr(line, "size=")) )
			{
				thisDecode->dwPartSize = atol(p+5);
				if (thisDecode->dwPartSize == thisDecode->dwExpectedBytes)
					thisDecode->endFlag	= TRUE;	 //single part file
			}
			if ( (p = strstr(line, "part=")) )
			{
				//this may be a multipart block
				int nPart = atoi(p+5);
				if (nPart != thisDecode->sequence)
				{
#ifdef DEBUG_DECODE
          sprintf (debug, "\r\nYENC part=%d does not match sequence %d", nPart,thisDecode->sequence);
          DebugLog ();
#endif
				//this could be a single part file in a multipart sequence.
				}
			}

			return (END_BLOCK);

		}
		
		
		if (  (thisContentEncoding != CODE_BASE64 && TestEndLine(line)) || 
              (!IsDataLine (line, bFirst) && GetLastChar(line) != '\0') ||
              (UsingMIME && *thisBoundary && (!strcmp (&line[2], thisBoundary) || !strcmp (&line[2], thisBoundaryEnded)) ) ) 
        {
#ifdef DEBUG_DECODE
          sprintf (debug, "\r\nSwitching back to skipping\nskipped: %.200s", line);
          DebugLog ();
#endif
          CodingState = DECODE_SKIPPING;
#if 0 /*??? S.Yoshida TESTING */ //MDS turned off
          prevContentEncoding = thisContentEncoding;
          thisContentEncoding = CODE_UNKNOWN;
#endif

/* If we are switching back to skipping and haven't really received any
 * data yet (< about a line's worth), then the line which gave us the coding type
 * was bogus (regular text line where first char happened to = encoded line 
 * length, Here's one: 'From: marnold@cwis.unomaha.edu (Matthew Eldon Arnold)'
 * Ditch all data so far and the current table  and reprocess
 * this line.  Note if we have a custom table, then we trust it.
 */

/* probably we should trust base64 too, but only if UsingMIME */

          if ( thisContentEncoding != CODE_CUSTOM &&
               ( (thisContentEncoding != CODE_BASE64) || ( (thisContentEncoding == CODE_BASE64) && !UsingMIME ) ) &&
               thisDecode->numBytes < 10) //<- this limits the minimum file size we will write
          {
                thisDecode->numBytes = 0;
                thisContentEncoding = CODE_UNKNOWN;
#ifdef DEBUG_DECODE
                sprintf (debug, "\r\nBogus table!  Scrapping so far, reprocessing line: %.200s", line);
                DebugLog ();
#endif
                thisDecode->numLines--;
                return (DecodeLine (thisDecode, line, bMonitor));
          }
          strntcpy (prevBlockIdent, thisDecode->ident, MAXINTERNALLINE-1);
          lastNumParts = thisDecode->nTotal;

          if (TestEndLine(line)) 
          {	
				thisDecode->endFlag = TRUE;
                if (thisNumBlocks == -1 && thisDecode->sequence != -1)
                  thisNumBlocks = thisDecode->sequence;
#ifdef DEBUG_DECODE
                sprintf (debug, "\r\nFound end of file %.200s", thisDecode->ident);
                DebugLog ();
#endif
                if (  thisContentEncoding == CODE_CUSTOM || thisContentEncoding == CODE_UU || thisContentEncoding != CODE_XX)
					return (END_BLOCK);

		  }
#if 1 /* S.Yoshida before valid data ((uu->64->uu like spam;-p)) */
          else if (!IsDataLine (line, bFirst)) 
          {
            HWND hWnd = currentCoded->hParentWnd;
            if (CompleteThisDecode (bMonitor) == FAIL) 
            {
              WVMessageBox(hWnd, IDS_WVCODING_DECODE_ABORT, IDS_WVCODING_PROBLEMS, MB_OK | MB_ICONEXCLAMATION);
            }
            if ((currentCoded = InitCoded (hWnd)) == NULL) 
            {
              WVMessageBox(hWnd, IDS_WVGROUP_LOW_MEM, IDS_WVGROUP_INIT_ERROR, MB_OK | MB_ICONSTOP);

            }
            return (DecodeLine (currentCoded, line, bMonitor));

          }
#endif
          return (END_BLOCK);
        }

        //return (DecodeDataLine (thisDecode, line));
        if (DecodeDataLine (thisDecode, line) == SUCCESS)
        {
          //hack to trigger end of some B64 sections (should really be in decodedataline
          if (thisContentEncoding == CODE_BASE64 && UsingMIME)
          {
            if (GetLastChar(line) == '=')
            {
                
                strntcpy (prevBlockIdent, thisDecode->ident, MAXINTERNALLINE-1);
                lastNumParts = thisDecode->nTotal;
                //boundary may be next line

                if (thisNumBlocks == -1 && thisDecode->sequence != -1)
                {
                  //MDS MPMF thisNumBlocks = thisDecode->sequence; //there may be other pieces
                  thisDecode->seqConfidence  = HIGH;
                }
                thisDecode->endFlag = TRUE;
                return (END_BLOCK);
            
            }
            
          }
          return (SUCCESS);
            
        }
        else
        {
          return (FAIL);
        }

  }
  DEBUG_BREAK; // verify this as proper return value...
  return FAIL;
}

/* ------------------------------------------------------------------------
 *  4 to 3 line decoder
 */
 #define MAXDECODELINE 296
BOOL  DecodeDataLine (TypCoded * thisDecode, char *line)
{
  register unsigned int i, j;
  unsigned int decodedCount = 0, numEncoded = 0, numDecoded = 0, checkSum, startNum = 0, stop;
  unsigned char buf[4], outLine[MAXDECODELINE];
  int offset;
  char *thisMap = NULL;
  int nLen = strlen(line);


  switch (thisContentEncoding) 
  {
  case CODE_QP:
  default:
        // not implemented yet
        return (FAIL);

  case CODE_UU:	 
  case CODE_XX:
  case CODE_CUSTOM:
		thisMap = CodingMap[thisContentEncoding];
        // these are 4-to-3 decodings which have the line length encoded
        // as the first char (count is # of chars after decoding)
        checkSum = decodedCount = thisMap[line[0]];
        // to precisely determine the numEncoded from this count char,
        // add (4 * the number of full 3-byte units) and a remainder offset
        // the remainder offset is 2 if 1 byte over, or 3 if 2 bytes over
        if ((offset = (decodedCount % 3)) != 0) 
        {
          offset++;
        }
        numEncoded = 4 * (decodedCount / 3) + offset;
        startNum = 1;
        break;

  case CODE_BASE64:
        // base64 is 4-to-3 decoding with nonexplicit line lengths 
		thisMap = CodingMap[thisContentEncoding];
        numEncoded = strlen (line);
        decodedCount = 3 * (numEncoded / 4);
        startNum = 0;
        break;

  case CODE_YENC:
		{
			unsigned char c;
			BOOL bEscape = FALSE;
			numDecoded = 0;
			
			if ( !strncmp(line, "=ypart ", 7) )
				return (SUCCESS);

			if ( !strncmp(line, "=yend ", 6) )
			{
				
				return (SUCCESS);	
			}
			
			while ( (c = *line++) != '\0' )
			{
				if (c == 0x0A || c == 0x0D)
					break;

				if (c == '=')
				{
					bEscape = TRUE;
					continue;
				}

				if (bEscape)
				{
					bEscape = FALSE;
					
					if (c >= 64)
						c -= 64;
				}

				c -= 42;
				outLine[numDecoded++] = (unsigned char) c;
				CrcAdd(c, &thisDecode->dwCRC);

				if (numDecoded == MAXDECODELINE && *line)
				{
					//avoid buffer overflow on invalid line
					//rewind one char, add it to next block
					if (AddDataToBlock (thisDecode, (char*)outLine, --numDecoded) == FAIL)
						return (FAIL);
					else
					{
						 
						numDecoded = 0;
						outLine[numDecoded++] = (unsigned char) c;
					}
				}
			}


		}
		break;
  }

  if (thisContentEncoding != CODE_YENC)
  {
	  if( nLen >= 120 ) 
	  { /* S.Yoshida */
		return (SUCCESS);
	  }

	  if (nLen < 3 || GetLastChar(line) == '\0')
		return (SUCCESS);

	  for (i = startNum, numDecoded = 0; numDecoded < decodedCount;) 
	  {
			// Get the next group of four characters 
			// Handle last group of characters in a base64 encoding specially -
			// padding '=' at end means we have fewer than 24 bits after decoding
			// Base64 end scenarios:
			//  'xx==' decodes to 8 bits
			//  'xxx=' decodes to 16 bits
			//  'xxxx' decodes to 24 bits
			//  'x===' can't happen
			if ((stop = numEncoded - i + 1) < 4) 
			{
			  memset (buf, 0, 4);
			}
			else 
			{
			  stop = 4;
			}
			for (j = 0; j < stop; j++, i++) 
			{
			  if (thisContentEncoding == CODE_BASE64 && line[i] == '=') 
			  {
					buf[j] = 0;
					j--;
					break;
			  }
			  else 
			  {
					buf[j] = thisMap[line[i]];
			  }
	//          checkSum += buf[j]; 
			}

			outLine[numDecoded++] = ( buf[0] << 2 ) | ( buf[1] >> 4 );
			if (j == 1 || numDecoded == decodedCount)
			  break;
			outLine[numDecoded++] = ( buf[1] << 4 ) | ( buf[2] >> 2 );
			if (j == 2 || numDecoded == decodedCount)
			  break;
			outLine[numDecoded++] = ( buf[2] << 6 ) | buf[3];

	  }
  }

  if (numDecoded && AddDataToBlock (thisDecode, (char*)outLine, numDecoded) == FAIL)
        return (FAIL);

/*  if (checkSum%64 != line[i])
   {
   printf("\r\nChecksum error: %d vs. %d", checkSum%64, line[i]);
   break;
   } else
   printf("\r\nChecksum ok: %c vs. %c", checkSum%64, line[i]);
 */
  return (SUCCESS);
}

/* ------------------------------------------------------------------------
 * AddDataToBlock is called on a line-by-line basis.
 * dataLen should rarely be > 100 bytes
 * The data is type huge, but it's just pointing to a string of chars, so
 * the size does not have to be a power of 2
 */
int AddDataToBlock (TypCoded * thisDecode, char *newData, unsigned int dataLen)
{

  char huge * p;
  char * q;
  unsigned int i;

  if (thisDecode->numBytes + (unsigned long) dataLen > thisDecode->maxBytes) 
  {
        thisDecode->maxBytes += BASE_BLOCK_SIZE;
#ifdef DEBUG_MEM
        sprintf (debug, "\r\nReallocing data to %ld", thisDecode->maxBytes);
        DebugLog ();
#endif
        if ((thisDecode->data = (char huge *) GlobalReAllocPtr (thisDecode->data,
                                                        thisDecode->maxBytes, GMEM_MOVEABLE)) == NULL) {
          return (FALSE);
        }
  }
  p = thisDecode->data + thisDecode->numBytes;
  q = newData;
  i = dataLen;
  while (i--)
     *p++ = *q++;
  // memmove (thisDecode->data + thisDecode->numBytes , newData, dataLen);
  thisDecode->numBytes += dataLen;
  return (SUCCESS);
}

void PreAllocDataByLines (TypCoded * thisDecode)
{
  unsigned long newSize;
  char huge *resizer;

  if (thisDecode->estNumLines) 
  {
    /* pre-alloc data to rough estimate of # bytes for decode.  allocing 
     * up front saves time - reallocs are slow.  If this fails, allow
     * to continue - the # lines may have been wrong...
     */
    if (thisContentEncoding == CODE_YENC)
	{
		newSize = thisDecode->estNumLines * 129L; 
	}
	else
	{
		newSize = thisDecode->estNumLines * 45L;
	}
    //MDS when encountering multiattach B64 files, don't realloc to smaller size
    //than we've already realloc'd too!
    if (newSize > thisDecode->maxBytes )
	{

      if ((resizer = (char huge *) GlobalReAllocPtr (thisDecode->data, 
                                      newSize, GMEM_MOVEABLE)) != NULL) 
	  {
        thisDecode->data = resizer;
        thisDecode->maxBytes = newSize;
#ifdef DEBUG_MEM
        sprintf (debug, "\r\nRealloced data to %ld based on lines header %ld",
                         thisDecode->maxBytes, thisDecode->estNumLines);
        DebugLog ();
#endif
      }
    }
  }
}

/*

     3.2.  HEADER FIELD DEFINITIONS

          These rules show a field meta-syntax, without regard for the
     particular  type  or internal syntax.  Their purpose is to permit
     detection of fields; also, they present to  higher-level  parsers
     an image of each field as fitting on one line.

     field       =  field-name ":" [ field-body ] CRLF

     field-name  =  1*<any CHAR, excluding CTLs, SPACE, and ":">

     field-body  =  field-body-contents
                    [CRLF LWSP-char field-body]

     field-body-contents =
                   <the ASCII characters making up the field-body, as
                    defined in the following sections, and consisting
                    of combinations of atom, quoted-string, and
                    specials tokens, or else consisting of texts>
*/
//
// looks for <word>:<space> where word is 0-9, A-Z & '-'
BOOL IsHeaderLine(LPCSTR pbuf, int nLength)
{

    BOOL bSet = FALSE;
    int pos = 0;
    if (nLength == 0)
        nLength = strlen(pbuf);
    /*
    if (strncmp(pbuf, "From ", 5) == 0)
    {
        if (strmatch(pbuf+5, "*?@?*") )
            return TRUE; 
    }
    */
    while( pos < nLength && pbuf[pos])
    {
    
        if ( isspace((unsigned char)pbuf[pos]))
            return FALSE;
        else if ( isalpha(pbuf[pos]) || isdigit(pbuf[pos]) || pbuf[pos] == '-' || pbuf[pos] == '_')
            bSet = TRUE;
        else if (pbuf[pos] == ':' )
        {
            if (bSet && isspace((unsigned char)pbuf[pos+1]) )
            {
                
                return IsAdditionalHeader(pbuf);
            }
            else
                return FALSE;
        }
        else
        {
            return FALSE;
        }
        
        pos++;    

    } 

    return FALSE;
   
}

BOOL IsLeftBracket(char c)
{
    if (c == '(' || c == '[' || c == '{' || c == '<')
        return TRUE;
    else
        return FALSE;
}

BOOL IsRightBracket(char c)
{
    if (c == ')' || c == ']' || c == '}' || c == '>')
        return TRUE;
    else
        return FALSE;
}  

/****************************************************************************
 * int ParseNum(const char *subject, const char* search, int *part, int *expected, 
 *              int namestart, int namelen)
 *
 * wild card REVERSE search number parser
 * search pattern # = one or more numbers and whitespace
 *                ? = one character
 *                < = left bracket character  ([{<
 *                > = right bracket character )])>
 * all other characters are considered significant
 *
 * [1 of 3]  [01/03]  01-03  (1/2)
 * 
 *      ST4_TVH.GIF (1/2) ST4 TVH
 *            zbo01.jpg [1/1] - {e - repost - circa Aug 11-17/95}
 *            Nikki Taylor part 2 of 2 
 *      ZONE RAIDERS (C) VIRGIN  [02/13] - 1 files [2/3]
 *
 *  (#/#),  [#/#], part #, #/#, (# of #), [# of #]  {#/#}
 *
 *
 *   Input 
 *      *subject    - start of src string
 *      *search     - start of pattern
 *      namestart   - position of start of search in string
 *      namelen - length of string to search
 *  Return:     
 *      0 if failed otherwise nonzero
 *      *part       -part# pointer
 *      *expected   -total# pointer
 *      *s          -pointer to start of matching pattern offset
 *      *e          -pointer to end of matching pattern offset
 *
 *
 ****************************************************************************/ 
 
int ParseNum(const char *subject, const char* search, int *part, int *expected, 
             int namestart, int namelen, int *s, /*=NULL*/int *e /*=NULL*/)
{
    int nReturn, num1, num2;
    char *pend, *pstart, *psend, *p1, *ps;
    BOOL found;
    nReturn=0;
    
    if (subject == NULL || search ==NULL || part==NULL || expected==NULL)
        return 0;
        
    pstart = (char *)subject;
    pend = (char *)pstart+strlen(subject) -1;
    
    psend = (char *)search + strlen(search)-1;
    p1 = pend;
    ps = psend;
    
    for(char *p = pend; p >=pstart; p--)
    {
        
        //look for match on 1st char of search string
        if (toupper(*p) != toupper(*search) && *search != '#' && *search != '?' && *search != '<' && *search != '>')
        {
            continue;
        }
        if (namestart >= 0 && namelen > 0)
        {
            if ( p >= subject+namestart && p <= subject+namestart+namelen)
                continue;
        }

        
        //found a match, now check the rest of the search string
        found = TRUE;
        num1 = num2 = -1;
        p1 = p;
        ps = (char *) search;
        if (s)
            *s = (p - subject);

        while (*ps && *p1 && found)
        {
            
            if (namestart >= 0 && namelen > 0)
            {
                if ( p1 >= subject+namestart && p1 <= subject+namestart+namelen)
                {
                    found = FALSE;
                    continue;
                }
            }            
            
            if (*ps == '?')
            {
                ps++;
                p1++;
                continue;
            }
			if (*ps == '\t') //match whitespace
			{
				char *p2 = p1;

                if (isspace((BYTE)*p1) && p1 > pstart && isspace((BYTE)*(p1-1)) )
                {
                    //there are spaces preceeding this one and we
                    //dont want to only trigger on the last one so
                    //continue until we're at the first.
                    found = FALSE;
                    continue;
                }



				if (isspace((BYTE)*p2))
				{
					while( *p2 && isspace((BYTE)*p2) )
						p2++;
				}
                if (p2 == p1)
                {
                    found = FALSE;
                    continue;
                }
				else
				{
				    p1 = p2;
                    ps++;
                    continue;
				}


			}

            
           if (*ps == '#')
            {
                BOOL bDigit = FALSE;
                char *p2;
                p2 = p1;
                if (isdigit((BYTE)*p1) && p1 > pstart && isdigit((BYTE)*(p1-1)) )
                {
                    //there are digits preceeding this one and we
                    //dont want to only trigger on the last one so
                    //continue until we're at the first.
                    found = FALSE;
                    continue;
                }

                while( *p2 && isdigit((BYTE)*p2) )
                {
                    
                    if (namestart >= 0 && namelen > 0)
                    {
                        if ( p2 >= subject+namestart && p2 <= subject+namestart+namelen)
                        {
                            found = FALSE;
                            break;
                        }
                    }
					bDigit = TRUE;
                    
                    p2++;    
                }
                if (p2 == p1)
                {
                    found = FALSE;
                    continue;
                }
                else
                {
                    if (num1 != -1)
                    {
                        sscanf(p1, "%d", &num2);
                    }
                    else
                    {
                        sscanf(p1, "%d", &num1);
                    }
                    
                    p1 = p2;
                    ps++;
                    continue;
                }
                
            }
            if (*ps == '<' && IsLeftBracket(*p1))
            {
                ps++;
                p1++;
                continue;
            }
            if (*ps == '>' && IsRightBracket(*p1))
            { 
                ps++;
                p1++;
                continue;            
            }    
            if (toupper(*ps) == toupper(*p1))
            {
                ps++;
                p1++;
                continue;
            }
            
            found = FALSE;
        
        }
        
        if (e)
            *e = ( p1 -subject);        
        if (*ps == '\0' && num1 != -1 && num2 != -1)
        {
            if (e)
                *e = ( p1 -subject);
            *part = num1;
            *expected = num2;
            nReturn =1;
            break;
        }
        else if (*ps == '\0' && num1 != -1)
        {
            if (strchr(search, '#') == strrchr(search, '#'))
            {
                *part = num1;
                *expected = -1;
                nReturn =1;
                break;
            }
        }
        else if (*ps == '\0' && strchr(search, '#') == NULL)
        {
            nReturn =1;
            break;
        }
    }
            
    return nReturn;

}


/* ------------------------------------------------------------------------
 *    Determine if the line is a data line
 */
BOOL IsDataLine (char *line, BOOL bFirst)
{
	if (thisContentEncoding == CODE_UNKNOWN) 
	{
	/* If this block has same ident as prev block, use same decode table
	 * If this block has same ident as any existing threads, use same decode table
	 * as matching thread
	 * Otherwise, test line for UU, XX, or Base64, returning if find a fit
	 */
		if (bFirst && currentCoded->sequence <= 1)
		{
			//1st parts always have a begin of some sort
			//ignore short lines
			if (currentCoded->beginFlag == 0 && strlen(line) < 45)
				return FALSE;
			
			
		}
#if SUPPORT_PARTIAL_MULTIPART
		if ((thisContentEncoding = ThreadTable (customTable, currentCoded->ident)) != CODE_UNKNOWN) 
		{
			if (thisContentEncoding == CODE_CUSTOM)
				CreateCodingMap (CodingMap[CODE_CUSTOM], customTable);
#ifdef DEBUG_MAP
          strcpy (debug, "\r\nUsing stored thread table");
          DebugLog ();
#endif
        }
#else
        if (currentCoded->ident[0] != '\0' && !strcmp (currentCoded->ident, prevBlockIdent)) 
        {
			thisContentEncoding = prevContentEncoding;
#ifdef DEBUG_MAP
			strcpy (debug, "\r\nUsing same table as prev decode block");
			DebugLog ();
#endif
        }
        else if ((thisContentEncoding = ThreadTable (customTable, currentCoded->ident)) != CODE_UNKNOWN) 
        {
			if (thisContentEncoding == CODE_CUSTOM)
				CreateCodingMap (CodingMap[CODE_CUSTOM], customTable);
#ifdef DEBUG_MAP
			strcpy (debug, "\r\nUsing stored thread table");
			DebugLog ();
#endif
        }
#endif
        else 
        {
			thisContentEncoding = CODE_UU;
			if (!bFirst && *line == '\'' && strlen(line) < 3)
				return (TRUE);
			if (TestDataLine (line, bFirst)) 
			{
#ifdef DEBUG_MAP
				sprintf (debug, "\r\nUsing UU table: %s", line);
				DebugLog ();
#endif
                return (TRUE);
          }
          thisContentEncoding = CODE_XX;
          if (TestDataLine (line, bFirst)) 
          {
#ifdef DEBUG_MAP
                sprintf (debug, "\r\nUsing XX table: %s", line);
                DebugLog ();
#endif
                return (TRUE);
          }
          thisContentEncoding = CODE_BASE64;
          if (TestDataLine (line, bFirst)) {
#ifdef DEBUG_MAP
                sprintf (debug, "\r\nUsing Base64 table: %s", line);
                DebugLog ();
#endif
                return (TRUE);
          }
          thisContentEncoding = CODE_UNKNOWN;
          return (FALSE);
        }
	}
	return (TestDataLine (line, bFirst));
}

BOOL TestDataLine (char *line, BOOL bFirst)
{
  unsigned int expectedLineLen, dataLen, lineLen;
  unsigned int decodedCount, offset, tolerance;
  register unsigned int i;
  unsigned int charcount;
  char *thisMap;

  thisMap = CodingMap[thisContentEncoding];
  lineLen = strlen (line);
  switch (thisContentEncoding) 
  {
  case CODE_UU:
  case CODE_XX:
  case CODE_CUSTOM:
/*
 * The first char of a good data UU/XX/Custom line is the encoded character
 * count for the line.  
 * example M = ascii 77, decodes to 45.  This line will decode to 45 characters,
 * so the # encoded chars should be (int)(4 * ((n+2) / 3)) - (round up and re-encode)
 * So if line[0] is 'M', there should be 60 characters on the line
 * The count does not include the first count char, so the expected line 
 * length is count+1 (i.e. 61). 
 * If the line length is actually 2 + count, then we have a checksum (?)
 */
//  expectedLineLen = 4 * (thisMap[line[0]] + 2) / 3) + 1;

        decodedCount = thisMap[line[0]];
        /* to precisely determine the numEncoded from this count char,
         * add (4 * the number of full 3-byte units) and a remainder offset
         * the remainder offset is 2 if 1 byte over, or 3 if 2 bytes over.
         * Not all encoders are smart enough to do this though, and just leave
         * the last (4-offset) bytes as junk (usually bytes found in the same
         * location on the previous line).  So allow a tolerance of (4-offset)
         * bytes
         */
        if (decodedCount > 45 || (decodedCount == 0 && bFirst) )
            return (FALSE); //MDS no UU/XX encoding scheme uses more than 45 characters max
        
        if (IsHeaderLine(line, 0))
            return (FALSE);        
		
		if ((offset = (decodedCount % 3)) != 0) 
        {
          offset++;
          tolerance = 4 - offset;
        }
        else 
        {
          tolerance = 0;
        }
        expectedLineLen = 4 * (decodedCount / 3) + offset + 1;  // add 1 for start char

        //fix broken uuencoder using ' instead of `
		if (thisContentEncoding == CODE_UU && lineLen <= 3 && *line == '\'' )
				return (TRUE);

		//MDS fix case where trailing spaces have been cropped. We allow two removed 20000505
        if (lineLen < expectedLineLen ) 
        {
          if ( thisContentEncoding != CODE_UU )//|| line[0] != 'M' || lineLen +2 < expectedLineLen  )
            return (FALSE);

		    dataLen = lineLen;
			while(line[dataLen-1] < 0x20)
				dataLen--;
			for (i = 0; i < dataLen; i++)
			{
			  if (thisMap[line[i]] == UNUSED)
					return (FALSE);
			}

        }
/*
 * Count the number of encoded data characters on the line (up to any whitespace)
 * (Some encoders pad with spaces at the end).  Don't forget, a space may be
 * a valid encoded char, so a proper line may very well end with a space.
 * Don't blindly remove all trailing white space!
 */
        // first strip trailing non-spaces (i.e. \n\r)
        for (dataLen = lineLen;
                 isspace ((unsigned char)line[dataLen - 1]) && (thisMap[' '] == UNUSED || line[dataLen - 1] != ' ') && dataLen > 0;
                 --dataLen);

        // now deal with spaces
        if (thisMap[' '] != UNUSED) 
        {
          for (; line[dataLen - 1] == ' ' && dataLen > 0 && dataLen != expectedLineLen;
                   --dataLen);
        }

		// May have a checksum character, so allow for one extra char
        // also allow for encoders which include the count char in the line count
        if (CodingState != DECODE_PROCESSING && expectedLineLen > dataLen && abs( (long)(expectedLineLen - dataLen)) > tolerance+1)
          return (FALSE);

		 if ( CodingState == DECODE_PROCESSING && thisContentEncoding == CODE_UU && line[0] != 'M'  )
		 {
			//be very suspicious
			if (expectedLineLen < dataLen && abs( (long)(expectedLineLen - dataLen)) > tolerance+5 )
				return FALSE;
			if (stristr(line, "cut here"))
				return FALSE;
		 }


        // May have a checksum character, so allow for one extra char
        // also allow for encoders which include the count char in the line count
        //if (expectedLineLen != dataLen && expectedLineLen + tolerance != dataLen &&
        //        expectedLineLen + 1 != dataLen && expectedLineLen + tolerance + 1 != dataLen &&
        //        expectedLineLen - 1 != dataLen && expectedLineLen + tolerance - 1 != dataLen)
        //  return (FALSE);


        line[dataLen] = '\0';           // permanently chop off the white space
        lineLen = dataLen;
		// It's the right length, now check content for match w/ table

        for (i = 0; i < dataLen; i++)
        {
          if (thisMap[line[i]] == UNUSED)
                return (FALSE);
        }
        
        //MDS this is a bad hack. The UUData has probably been cropped during transit.
        //we add up to two bytes to restore it's length. This will fix the problem most 
        //of the time, and rarely break anything else. Why do encoders still use space? 20000505
        // WE CANT Go WRITING TO SOME MEORY BUFFER SOMEWHERE!
        if (lineLen < expectedLineLen && !bFirst) 
        {
            while (lineLen < expectedLineLen && dataLen+2 > lineLen)
              line[lineLen++] = ' ';
          line[lineLen] = '\0';
        }
        if (lineLen > (expectedLineLen + 5) && bFirst)
			return FALSE;

		if (bFirst && stristr(line, "cut here"))
			return FALSE;
 

        return (TRUE);

  case CODE_BASE64:
        // permanently remove all trailing space
        while (lineLen > 0 && isspace ((unsigned char)line[lineLen - 1]))
          line[--lineLen] = '\0';

        
        // for base 64, just check if all chars in coding map (allow pad '=')
        charcount = 0;
        for (i = 0; i < lineLen; i++) {
          if (thisMap[line[i]] == UNUSED && line[i] != '=')
                return (FALSE);
          if (line[i] == '=')
                charcount++;
        }
        // if all chars are pads ('=') this isn't realy a base64 line
        if (charcount == lineLen)
           return (FALSE);
        
        //MDS *must* have length check, otherwise any character that
        //happen to match Base64 charset trigger base64 start, so unless 
        //we're decoding, only accept resonable length b64
        if (CodingState != DECODE_PROCESSING && lineLen < 60)
          return FALSE;

        // if all char are the same we accept only if MIME-header used
        charcount = 0;
        for (i = 1; i < lineLen; i++) 
        {
           if (line[i-1] == line[i])
              charcount++;
        }
        if (charcount == lineLen-1)
           return (UsingMIME);
        
        return (TRUE);

  case CODE_QP:
        // not implemented yet
		return (FALSE);
		break;
  case CODE_YENC:
		//anthing is a data line ?
		return (TRUE);
  default:
        return (FALSE);
  }
}

/* ------------------------------------------------------------------------
 *    Mappings for encoding/decoding
 */
int CreateCodingMap (char *map, char *table)
{
  register unsigned int i;

  for (i = 0; i < CODINGMAPSIZE; i++)
        map[i] = UNUSED;

  // if you see a table[i] character, decode it to i
  // i.e. UU, table[1] is a '!'.  So a '!' should decode to 1

  for (i = 0; i < CODINGTABLESIZE; i++)
        if (map[table[i]] != UNUSED)
          return (table[i]);            // table[i] is duplicate 

        else
          map[table[i]] = (unsigned char) i;

#ifdef DEBUG_MAP2
  sprintf (debug, "\r\nCoding Map:\n");
  DebugLog ();
  for (i = 0; i < 256; i++) 
  {
        sprintf (debug, "%d ", map[i]);
        DebugLog ();
  }
#endif
  return (-1);
}

int ThreadTable (char *dest, char *ident)
{
  int num = -1;
  register int i;

#if SUPPORT_PARTIAL_MULTIPART
  if( thisPartId[0] ) 
  {
    for (i = 0; i < numDecodeThreads; i++ ) 
	{
      if(!strcmp( thisPartId, threadList[i]->PARTIALID)) 
	  {
        num = i;
        break;
      }
    }
    if( i > numDecodeThreads )
      num = -1;
  }
  if( num == -1 )
#endif

#if ! SUPPORT_PARTIAL_MULTIPART  /* ?? S.Yoshida TESTING COMMENT OUT */
  for (num = -1, i = 0; i < numDecodeThreads; i++)
        if (!_stricmp (ident, threadList[i]->ident))
          num = i;
#else
  {};
#endif

  if (num == -1)                                // no thread by that name 

        return (CODE_UNKNOWN);
  else 
  {
        if (threadList[num]->contentEncoding == CODE_CUSTOM)
          strntcpy (dest, threadList[num]->customTable, CODINGTABLESIZE);
        return (threadList[num]->contentEncoding);
  }
}
#if 0
void
CreateUUTable ()
{
  register unsigned int i;

  for (i = 0; i < CODINGTABLESIZE; i++)
        uuTable[i] = i + 32;

  uuTable[0] = 96;                              /* set space to map to back-quote */
}
#endif

/* ------------------------------------------------------------------------
 *    Search through a subject: or BEGIN line 
 *  (for a Subject: line,  "Subject:" has already been removed).
 *
 *  Attempt to obtain a block number, and a total # of blocks
 *  Generate an identifier based on the subject, preferably based on the
 *  actual file name.  If guessIdent is TRUE, choose an ident even if
 *  it doesn't look like a fileName.  If guessIdent is FALSE, only fill
 *  ident if we see a fileName (word containing a dot).
 *
 *  Note: at this point, we haven't seen any MIME header, and we're not
 *  really expecting one.  So now is the time to get what we can
 *
 *  Common Subject styles which this function deals with:
 *  filename.ext 1/2 comment
 *  filename.ext(1/2)comment
 *  filename.ext (1/2) comment
 *  filename.ext [1/2] comment
 *  filename.ext 1 of 2 comment
 *  filename.ext part 1 of 2 comment
 *  filename.ext 1 of 2 comment
 *  This is part 1/2 of filename.ext
 *
 *  Also handles the following common BEGIN lines:
 *  BEGIN --- CUT HERE --- Cut Here --- cut here --- abcd.efg n/N
 *  BEGIN --- CUT HERE --- Cut Here --- cut here --- abcd.efg
 *  BEGIN-------cut here-------CUT HERE-------PART n               // This is TIN
 *  BEGIN -- CUT HERE -- cut here -- abcd.efg part n of N
 *  BEGIN---CUT HERE---BEGIN---CUT HERE---BEGIN PART n/N
 *
 *  For subject lines beginning with the comment, or free text, the
 *  name is harder to guess.  In these cases, prefer a word containing
 *  a dot (filename.ext), or if none, just take the first word.  Scan
 *  any existing threads to see if any of their identifiers matches.
 *  For example,
 *  another encoded file: filename.ext (1/2)
 *  testing encoded files (1/2) i.e. ident would be testing
 *
 *  Known to be incorrectly handled by this function:
 *  filename.ext 3.4 (1/2)      i.e. name w/ version number
 *  filename.ext 001        i.e. sequence w/ no # parts
 *  filename.ext1           i.e. part number appended to file name
 */
#define SEEK_PARTNUM    1
#define SEEK_NUMPARTS   2
#define IGNORE_NUMBERS  3
void ParseInfoLine (TypCoded * thisDecode, char *line, BOOL guessIdent)
{

	int nSequence = -1, nTotal = -1, nGroupSeq = -1, nGroupTotal = -1;
	const char *guessName; int nameLen;


#if SUPPORT_PARTIAL_MULTIPART //OBSOLUTE S.Yoshida
	if (thisPartId[0] != '\0') 
	{
		return;
	}
#endif

	guessName =  ParseSubject(line, &nameLen, &nSequence, &nTotal, &nGroupSeq, &nGroupTotal, thisDecode->ident, MAXINTERNALLINE-1 );

	if (guessName)
	{
		//strntcpy(thisDecode->ident, guessName, nameLen);
		//thisDecode->ident[nameLen+1] = '\0';
	}

    if (nSequence >=0 && nTotal >=0)
    {
        thisDecode->seqConfidence = HIGH;
#if SUPPORT_PARTIAL_MULTIPART
        thisPartNum =
#endif
        thisDecode->sequence = nSequence;
        thisNumBlocks = 
        thisDecode->nTotal = nTotal;
        nSequence = -1;
    }
    else if (nSequence != -1)
    {
        thisDecode->seqConfidence = LOW;
#if SUPPORT_PARTIAL_MULTIPART
        thisPartNum =
#endif
        thisDecode->sequence = nSequence;
        thisNumBlocks = 
        thisDecode->nTotal = nTotal;
        nSequence = -1;

    }
   

/*
  while (*ptr) 
  {
        if (!ReadSubjectToken (thisTok = tok, &ptr))
          break;

//  WORDS
        // Prefer a word containing a dot for the ident (but not a period
        // at the end of a sentence
        if ((tmp = strchr (thisTok, '.')) && isalpha (*(tmp + 1)))
          strntcpy (thisDecode->ident, thisTok, MAXINTERNALLINE-1);
        else if (guessIdent) {
          // skip if already have name w/ confidence
          if (thisDecode->ident[0] == '\0') 
          {
                // check if any threads have this token as name
                if (SearchThreadNames (thisTok))
                  strntcpy (thisDecode->ident, thisTok, MAXINTERNALLINE-1);

                // Save first word found for thread name
                // prefer ident not starting with 're'
                else if ((guessName[0] == '\0' || !_strnicmp (guessName, "re", 2)) && strlen (thisTok) > 1)
                  strcpy (guessName, thisTok);
          }
        }
  }
*/  
  if (guessIdent && thisDecode->ident[0] == '\0')
  {
	if (!guessName)
		guessName = line;
    strntcpy (thisDecode->ident, guessName, MAXINTERNALLINE-1);
  }

#ifdef DEBUG_DECODE
  sprintf (debug, "\r\nSubject header: name %.200s, part number %d (confidence %d), num parts %d",
                   thisDecode->ident, thisDecode->sequence, thisDecode->seqConfidence, thisNumBlocks);
  DebugLog ();
#endif


}

//returns pointer to filename or subject if no file found
//

const char* ParseSubject(const char *line, int* nameLength, int* pnSeq, int* pnTotal, int* pnGroupStart, int* pnGroupEnd,  char* pfullname, int fullnamelen )
{

    const char *guessName = NULL; 
    const char *end, *scan, *start;
    int nSequence = -1, nTotal = -1, hasdot = 0;
	BOOL bGroupWarn = FALSE, bQuoted = FALSE;
    int s=0, e=0, namestart =0, namelen =0, nLength = 0;

	*nameLength = 0;
	nLength = strlen(line);
    end = (nLength > 0 ? (line + nLength -1) : line);
    if (pfullname && fullnamelen > 0)
        *pfullname = '\0';

	*pnSeq = -1;
	*pnTotal = -1;

    //skip leading whitespace
	while (*line == ' ' || *line == '\t' || *line == '-') 
        line++;

    start = line;
	//skip repost
    if ( !_strnicmp(start, "repost", 6)) 
    {
        for (start += 6; *start == ' ' || *start == '\t'
             || *start == ':' || *start == '-'; start++);
    }

    //skip repost
    if (!_strnicmp(start, "re:", 3))
    { 
        for (start += 3; *start == ' ' || *start == '\t'
             || *start == ':' || *start == '-'; start++);
    }
    
                     
    /* Get filename */

    

    /* Try looking for a filename with a "." in it later in the subject line.
     Exclude <digit>.<digit>, since that is usually a version number. */
    scan = end;
    
	*nameLength = 0;    
    
	while (scan >= start)
	{
		if ( *scan == '"')
		{
			guessName = scan;
			s++;
		}
		scan--;
		
	}
	if (s == 2)
	{
		guessName++;
		scan = guessName;
		while (scan <= end && *scan != '"')
			scan++;

		*nameLength = scan - guessName;
		while (scan > guessName)
		{
			if ( *scan == '.')
				hasdot = 1;
			
			scan--;
		}
		int tpart, tnparts;

		if (*nameLength)
		{
			char * fnamep = new char[*nameLength+1];
			if (fnamep)
			{
				strntcpy(fnamep, guessName, *nameLength);
				*(fnamep+*nameLength) = '\0';
			

				//found part# inside of subject, don't do quote wrap filename
				if (ParseNum(fnamep, "(#/#)", &tpart, &tnparts, -1, -1, &s, &e) > 0)
				{
					bQuoted = FALSE;
					hasdot = 0;
					namelen=0;

				}
				delete [] fnamep;
			}
		}


		
	}

	

	if (*nameLength < 3 || hasdot==0)
	{
		scan = end;
		/* while not at end of line, and reset start */
		while (scan > start ) 
		{
			/* skip whitespace */
			while (isspace((BYTE)*end)) 
				--end;
            
			/* look for continuous word units */
			for (    scan =end; isalnum((BYTE)*scan) || *scan == '-' || *scan == '+'
				 || *scan == '&' || *scan == '_' || *scan == '.' || *scan == '!'
				 || *scan == '~' || *scan == '#' || *scan == '%' || *scan == '$'
				 || *scan == '(' || *scan == ')';
				  --scan) 
			{
				/* found a dot, flag it, except when preceeded & followed by int or space or dot */
				if (*scan == '.' /*&& (!isdigit(scan[-1]) || !isdigit(scan[1]) ) */ //breaks flt-rf1.001
					&& !isspace((BYTE)scan[-1]) && !isspace((BYTE)scan[1]) 
					&& *(scan-1) != '.' && *(scan+1) != '.' ) 
				{
					hasdot++;
				}
			}
            
			if (hasdot > 0 && hasdot < 3 && scan < end && *(scan+1) != '.' && *end != '.') 
			{
            
				
				if (*scan == '/')
				{
					//skip URL in subject
					if (scan -6 > start && _strnicmp(scan-6, "http://", 7) == 0)
					{
						scan -=7;
						continue;
					}
					else if (scan -7 > start && _strnicmp(scan-7, "https://", 8) == 0)
					{
						scan -=8;
						continue;
					}
					else if ( _strnicmp(scan+1, "www.", 4) == 0)
					{
						continue;	
					}
				}
				
				
				guessName = scan+1;
				*nameLength = end - scan;

				//strntcpy(guessName, scan+1, end - scan);
				//*(guessName +(end - scan)) = '\0';
				scan--;
				namestart = (int)(scan+1 - line);
				break;
			}
			else
			{
				hasdot = 0;
			}
            
			/* while there is something here */
			while (scan > start && !isalnum((BYTE)*scan)) 
				--scan;
			end = scan;
		}
	}
	else
	{
		namestart = (int)(guessName - line);
	}

    if (hasdot && *guessName)
        namelen = *nameLength;
    else
    {
        namelen = -1;
        namestart= -1;
    }
    s = e = -1;


	// Look for sequence numbers, excluding filename part
    // wildcard bracket match char is < & >
	// look specifically for (x/y)
    if (ParseNum(line, "(#/#)", pnSeq, pnTotal, namestart, namelen, &s, &e))
	{
		int part2 = *pnSeq, nparts2=*pnTotal, namestart2=namestart;
		int namelen2=namelen, s2=s, e2=e;

		//was that the last bracket on the line?
		if (ParseNum(line, "<#/#>", pnGroupStart, pnGroupEnd, namestart2, namelen2, &s2, &e2))
		{
			//no it wasn't
			if ( *pnSeq != *pnGroupStart || *pnTotal != *pnGroupEnd)
			{
#ifdef DEBUG_DECODE
				sprintf (debug, "\r\nLast part # is not (#/#), could be group number %.200s", line);
				DebugLog ();
#endif

				bGroupWarn = TRUE;
			}
		}
	}
	else if	(ParseNum(line, "(\t#/#\t)", pnSeq, pnTotal, namestart, namelen, &s, &e))
		;

	else if	(ParseNum(line, "<#/#>", pnSeq, pnTotal, namestart, namelen, &s, &e))
		;

    else if ( ParseNum(line, "part#of#", pnSeq, pnTotal, namestart, namelen, &s, &e))
        ;
    /*else if ( ParseNum(line, "#of#", pnSeq, pnTotal, namestart, namelen, &s, &e))
	{
		int part2 = *pnSeq, nparts2=*pnTotal, namestart2=namestart,namelen2=namelen;
		int s2=s, e2=e;

		if (ParseNum(line, "day#of#", &part2, &nparts2, namestart2, namelen2, &s2, &e2))
		{
			s = e = -1;
			*pnSeq = *pnTotal = -1;

			bGroupWarn = TRUE;

		}

    }
	*/
    else if ( ParseNum(line, "part #/#", pnSeq, pnTotal, namestart, namelen, &s, &e))
        ;
    else if ( ParseNum(line, "part#", pnSeq, pnTotal, namestart, namelen, &s, &e))
        ;
    else if (ParseNum(line, "_#/#_", pnSeq, pnTotal, namestart, namelen, &s, &e))
        ;
    else if ( ParseNum(line, "#/#", pnSeq, pnTotal, namestart, namelen, &s, &e))
    {
        int p1, p2;
        if (ParseNum(line, " #/#/", &p1, &p2, -1, -1, NULL, NULL))
        {
            //avoid dates
            if (p1 == *pnSeq && p2 == *pnTotal)
            {
                *pnSeq = -1;
                *pnTotal = -1;
            }
        }
    }
        
    else if ( ParseNum(line, " #-#", pnSeq, pnTotal, namestart, namelen, &s, &e))
        ;
    else
    {
        //TRACE("Part numbers not found in %s\n", line);
		/*
		if ( guessName &&  (ParseNum(guessName, " #/#", pnSeq, pnTotal, 0,0, &s, &e) ||
							ParseNum(guessName, "<#/#>", pnSeq, pnTotal, 0,0, &s, &e) )	)

		{
			//part number inside quoted subject?
			//"(1/2) subject.ext" "Subject.ext (1/2)" "some word (1/2) subject.ext"
			if (s )
			{
				namelen = s;
			}
			else
			{
				guessName += e+1;
				namelen -= (e+1);
			}
		}
		*/ 
    }

    if (!hasdot)
    {
        int n = 0;
        const char *temp = NULL;
        //now figure out a subject,
        if (s >= 0 || e > 0)
        {
            if (s > 1)
            {
                n = s-1;
                while ( n > 0 && isspace((unsigned char)*(line+n)))
                    n--;
                if (n != 0)
                {
                    guessName = line;
					*nameLength = n+1;
					//strntcpy(guessName, line, n+1);
                    //*(guessName+n+1) = '\0';
                }
            }
            else if (e < nLength)
            {
                n = e+1;
                while ( n < nLength && isspace((unsigned char)*(line+n)))
                    n++;
                if (n != nLength)
                {                
                    guessName = line+n;
					*nameLength = nLength - n +1;
					//strntcpy(guessName, line+n, nLength - n +1);
                    //*(guessName+nLength - n+1) = '\0';                
                }
            }
        }
        //Find a word to use as the subject, we cant use the whole subject
        //because an archive volume number will cause subject match to fail.
        if (guessName)
        {
            int count = 0;
			//v96i059:
            if (toupper(*guessName) == 'V')
            {
                temp = guessName;
                            
                //probably an archive header?
                while (temp[count] && !isspace((unsigned char)temp[count]) && count < *nameLength)  //end of archive hdr
                    count++;
                while (temp[count] && isspace((unsigned char)temp[count]) && count < *nameLength)   //start of next word
                    count++;
                if (count < *nameLength)
                {
                    guessName = temp;
					*nameLength = count;
					//strcpy(guessName, temp);
                    namestart += count;
                }
            }
        }
    }

    if (pfullname && fullnamelen > 0)
    {
        *pfullname = '\0';
        if ( end > line && end-line < max(511, fullnamelen))
        {
            if (s < end-line &&  end-line +1 > e && (s > 0 || e > 0) )
            {
                //part number inside "full" filname
				//if (s>0)
				//	s--;
				if (!guessName)
					guessName = line;				
                strntcpy(pfullname, line, min(s,fullnamelen-1) );
				if (s < fullnamelen)
				{
					strntcpy(pfullname+s, line + e, min(end-line - e+1, fullnamelen-s) );
					*(pfullname+s+(min(end-line - e+1, fullnamelen-s) )) = '\0';
				}

            } 
            else if (s>=0)
            {
                //was 01/00 strntcpy(pfullname, subject, end-subject+1);
                strntcpy(pfullname, line, min(s, fullnamelen-1) );
				*(pfullname+(min(s, fullnamelen-1))) = '\0';
				if (!guessName)
					guessName = line;
				if (e < nLength && e>0 && s<fullnamelen)
				{
					strntcpy(pfullname+(s), line+e, min(nLength-e, fullnamelen-s));
					*(pfullname+ s + min(nLength-e, fullnamelen-s)) = '\0';
				}
                
            }
            else
			*(pfullname) = '\0';	
        }
        else
        {
			if (s>=0)
            {
                //was 01/00 strntcpy(pfullname, subject, end-subject+1);
                strntcpy(pfullname, line, min(s, fullnamelen-1) );
				*(pfullname+(min(s, fullnamelen-1))) = '\0';
				if (!guessName)
					guessName = line;
				if (e < nLength && e>0 && s<fullnamelen)
				{
					strntcpy(pfullname+(s), line+e, min(nLength-e, fullnamelen-s));
					*(pfullname+ s + min(nLength-e, fullnamelen-s)) = '\0';
				}
                
            }
			else
				strncpy(pfullname, ident_with_frob(line), min(fullnamelen, MAXFILENAME+1));
        
        }

		//skip past volume identifier v2323232: 
		if (toupper(*pfullname) == 'V' && isdigit(*(pfullname+1)) )
		{
			char *w, *t;
			w = pfullname +2;
			while ( *w && *w != ':' && !isspace((unsigned char)*w) )
				w++;
			if (*w == ':' && *(w+1) == ' ')
			{
				w+=2;
				t = pfullname;
				while(*w)
					*t++ = *w++;
				*t = '\0';
					
			}
		}
    }


	return guessName;

}

BOOL ReadSubjectToken (char *dest, char **ptr)
{
  int len;
  register int i;
  char *str = *ptr;

  if (*str == '\0')
        return FALSE;

  len = strcspn (str, " ()[]\\/:,\"'`<>{};\t\n\r");

/*  Ignore (skip) all of these delimiters except '/'
 *    Return '/' as a token of length 1
 */
  if (len == 0)
  {
        if (*str == '/')
        {
          len = 1;
        }
        else 
        {                                          // skip this delimiter

          (*ptr)++;
          return (ReadSubjectToken (dest, ptr));
        }
  }
  for (i = 0; i < len; i++)
        dest[i] = tolower (str[i]);
  dest[len] = '\0';

  *ptr = str + len;                             // increment line ptr

  return TRUE;
}

BOOL
SearchThreadNames (char *name)
{
  register int i;
  int len;

  len = strlen (name);

  for (i = 0; i < numDecodeThreads; i++)
        if (!_strnicmp (name, threadList[i]->ident, len))
          return (TRUE);

  return (FALSE);
}

/* ------------------------------------------------------------------------
 * simple MIME header handling - not very smart!
 */
BOOL 
SkipSpace (char **ptr)
{
  while (**ptr && isspace ((unsigned char)**ptr)) {
        (*ptr)++;
  }

  return (**ptr != '\0');
}


BOOL 
SkipToNextClause (char **ptr)
{
  while (**ptr && **ptr != ';') {       // skip to semi-colon or end

        (*ptr)++;
  }
  if (**ptr == ';') {                   // skip semi-colon

        (*ptr)++;
  }
  if (!SkipSpace (ptr)) {               // skip space after semi-colon

        return FALSE;
  }
  return TRUE;
}

BOOL 
GetNumber (int *dest, char *src)
{
  char temp[20];
  char *ptr, *destPtr;

  ptr = src;
  destPtr = temp;
  *destPtr = '\0';
  while (*ptr && isdigit (*ptr)) {
        *destPtr++ = *ptr++;
  }
  if (*temp == '\0')
        return FALSE;

  *dest = atoi (temp);
  return TRUE;
}

BOOL GetPossiblyQuotedStr (char *dest, char *src, int maxLen)
{
	char *ptr, *destPtr;
	int len;
	char quoted = 0;

	ptr = src;
	destPtr = dest;
	*destPtr = '\0';
	len = 0;
	if (*ptr == '"' || *ptr == '\'') 
	{
		quoted = *ptr;
		ptr++;
	}

	// if quoted, seek to end quote (allow spaces and semicolons inside quotes)
	// if not in quotes, read up to space or semicolon
	while (*ptr && len < maxLen) 
	{
		if (quoted) 
		{
			if (*ptr == quoted) 
			{
				break;
			}
		}
		else 
		{
			if (isspace ((unsigned char)*ptr) || *ptr == ';') 
			{
				break;
			}
		}
		len++;
		*destPtr++ = *ptr++;
	}
	*destPtr = '\0';

#ifdef RFC1522
  decodeStrRFC1522(dest);
#endif

	if (*dest == '\0')
		return FALSE;

//  if (quoted && *ptr != quoted)  // missing end-quot
  //      return FALSE;

  return TRUE;
}

/* ParseMIMEHeader
 * this a very simplisitc approach
 * returns nonzero if it was a MIME header (regardless of whether we handled it ok) 
 * return -1 if not finished, 1 if finished, 0 if failed
 *
 */
int ParseMimeHeaderLine (TypCoded * thisDecode, char *line)
{
  char *ptr, *p;
  int result, num;
  char temp[MAXINTERNALLINE];
  int nMimeState = thisDecode->nMimeState;
  ptr = line;
  result = FALSE;

  if (!_strnicmp (ptr, "Content-Transfer-Encoding:", 26)) 
  {
        ptr += 26;
        nMimeState = MIMETRANSFER;
        if (GetLastChar(ptr) == ';')
            thisDecode->nMimeState = MIMETRANSFER;
        else
            thisDecode->nMimeState = MIMEINIT;
  }
  if (nMimeState == MIMETRANSFER)
  {      
        if (SkipSpace (&ptr) && GetPossiblyQuotedStr (temp, ptr, MAXINTERNALLINE - 1)) 
        {
              if (!_stricmp (temp, "base64")) 
              {
                    thisContentEncoding = CODE_BASE64;
                    thisDecode->beginFlag = 1;  //MDS Flag start This is the best indicator for B64
#if SUPPORT_PARTIAL_MULTIPART
                    //thisPartNum =
#endif
                    //thisDecode->sequence = 1; //this may break multipart multifile decodings

                    if (*thisDecode->ident == '\0')
                    {
                        strntcpy(thisDecode->ident, prevBlockIdent, MAXINTERNALLINE-1);
                        thisDecode->nTotal = 
                        thisNumBlocks = lastNumParts;

                    }

              }
              else if (!_strnicmp (temp, "x-uu", 4)) 
              {
                    thisContentEncoding = CODE_UU;
              }
              else if (!_strnicmp (temp, "x-xx", 4)) 
              {
                    thisContentEncoding = CODE_XX;
              }
        }
        return TRUE;
  }
  if (!_strnicmp (ptr, "Content-Disposition:", 20))
  {
        ptr += 20;
        if (GetLastChar(ptr) == ';')
            thisDecode->nMimeState = MIMETYPE;
        else
            thisDecode->nMimeState = MIMEINIT;
        nMimeState = MIMETYPE;



  }  
  if (!_strnicmp (ptr, "Content-Type:", 13))
  {
        ptr += 13;
        if (GetLastChar(ptr) == ';')
            thisDecode->nMimeState = MIMETYPE;
        else
            thisDecode->nMimeState = MIMEINIT;
        nMimeState = MIMETYPE;

  }
  if (nMimeState == MIMETYPE)
  {
        if (GetLastChar(ptr) != ';')
            thisDecode->nMimeState = MIMEINIT;
    //Content-Type:
    //name=                            application/                                  
    //id=                                          octet-stream        zip          
    //number=                                      postscript          applefile    
    //total=                                       mac-binhex40        ms-tnef      
    //message/partial                              x-stuffit           msword       
    //        external-body                        x-zip-compressed    ms-excel     
    //        digest                               x-shar              ms-powerpoint
    //boundary=                                    x-tar               ms-works     
    //multipart/                                   x-gtar                            
    //          mixed                              x-compress                        
    //          appledouble                        x-gzip                            
    //          alternative            video/                                        
    //          digest                       mpeg                                    
    //          parallel                     x-msvideo                               
    //text/                                  quicktime                               
    //     plain                       media clip/                                   
    //     html
    //                                 audio/      
    //charset=                               x-wav 
    //image/                                 x-aiff
    //      jpeg                             x-voc 
    //      gif                              midi  
    //      tiff                             x-mpeg
    //      x-MS-bmp
    //

        if ( (p = stristr(ptr, "name=")) != NULL)
        {
            
            if (GetPossiblyQuotedStr (temp, p+5, MAXINTERNALLINE - 1)) 
            {
                //actual name get stored in name field, ident field remains to match
                //other same-subject messages

                //SubsituteCharInString(thisDecode->ident,' ','_'); //MDS match UUCODE & dont break execute()
                NameWithoutPath( thisDecode->name, temp);  //also strip path
                strntcpy(thisDecode->ident, thisDecode->name, MAXINTERNALLINE-1); 
                                
                result = TRUE;
                if (thisContentEncoding == CODE_BASE64)
                {
                    //MDS trigger part start on filename to avoid tossing
                    //files from multipiece mime files
                    //i.e. file 1 of 11 which has no boundary separator
                    //and doesn't trigger end of B64 cause B64 data doesn't
                    //end with '='
                    //otherwise there isn't a way to tell b64 end
                    thisDecode->beginFlag = 1;  
                }

                if (thisDecode->nAppType == APPLEFILE)
                {

                    if (*thisDecode->name)
                        strcat(thisDecode->name, ".apl");
                    if (*thisDecode->ident)
					{
						thisDecode->ident[MAXINTERNALLINE-5]='\0';
                        strcat(thisDecode->ident, ".apl");
					}
                }
                else if (thisDecode->nAppType == TEXTFILE)
                {
						thisDecode->ident[MAXINTERNALLINE-5]='\0';
					if (*thisDecode->name && strchr(thisDecode->name, '.') == NULL)
                        strcat(thisDecode->name, ".txt");
                    if (*thisDecode->ident)
                        strcat(thisDecode->ident, ".txt");

                }

            }
        }

        if ( (p = stristr(ptr, "number=" )) != NULL )
        {
            if (GetNumber (&(thisDecode->sequence), p+7)) 
            {
#if SUPPORT_PARTIAL_MULTIPART
                thisPartNum = thisDecode->sequence;
#endif
                result = TRUE;
                thisDecode->seqConfidence = HIGH;
            }
        }
        if ( (p = stristr(ptr, "total=")) != NULL) 
        {
            if (GetNumber (&num, p+6)) 
            {
                result = TRUE;
                
                if (thisNumBlocks < 2)
                    thisNumBlocks = num;
                thisDecode->seqConfidence = HIGH;
            }
        }
#if SUPPORT_PARTIAL_MULTIPART
        if ( (p= stristr(ptr, "id=")) != NULL) 
        {
            if (GetPossiblyQuotedStr (thisPartId, p+3, MAXINTERNALLINE - 1)) 
            {
                //this ID should be unique for a multipart MIME message stream
                //and should be used over a matching filename or subject line
                strntcpy(thisDecode->MimeID, thisPartId, MAXINTERNALLINE);
                thisDecode->bMime = TRUE;
                result = TRUE;
                thisDecode->seqConfidence = HIGH;
            }
        }
#endif
        if ( (p = stristr(ptr, "boundary=")) != NULL)
        {
            /* this is no good for encapsulated multipart sections. but ok for now */
            if (GetPossiblyQuotedStr (thisBoundary, p+9, MAXINTERNALLINE - 1)) 
            {
                _snprintf (thisBoundaryEnded, MAXINTERNALLINE, "%s--", thisBoundary);
                result = TRUE;
            }
        }

        if ( (p = stristr(ptr, "application/")) != NULL)
        {
            p+= 12;

            thisDecode->nAppType = APP_FILE;
            if ( !_strnicmp( p, "applefile", 9) )
            {
                thisDecode->nAppType = APPLEFILE;

                if (*thisDecode->name)
                    strcat(thisDecode->name, ".apl");
                if (*thisDecode->ident)
                    strcat(thisDecode->ident, ".apl");
            }

        }
        if ( (p = stristr(ptr, "text/")) != NULL)
        {
            p += 5;

            if ( !_strnicmp(p, "plain", 5) )
            {
                if ( !thisDecode->nAppType)
                    thisDecode->nAppType = TEXTFILE;
            
            }
            else if ( !_strnicmp(p, "html", 4) )
            {
                if ( !thisDecode->nAppType)
                    thisDecode->nAppType = HTMLFILE;

            }

        }
        if ( (p = stristr(ptr, "image/") ) )
        {
            p += 6;
            thisDecode->nAppType = IMAGEFILE;
        }

  }
  if (nMimeState == MIMEDESC)
  { 
    if (GetLastChar(ptr) != ';')
        thisDecode->nMimeState = MIMEINIT;
        
        if ( ( p = stristr(ptr, "filename=")) != NULL) 
        {
            if (GetPossiblyQuotedStr (temp, p+9, MAXINTERNALLINE - 1)) 
            {
                //SubsituteCharInString(thisDecode->ident,' ','_'); //MDS match UUCODE & dont break execute()
                NameWithoutPath( thisDecode->name, temp);  //also strip path
                *thisDecode->name = '\0';                
                result = TRUE;

                if (thisContentEncoding == CODE_BASE64)
                {
                    //MDS trigger part start on filename to avoid tossing
                    //files from multipiece mime file  
                    thisDecode->beginFlag = 1;  
                }
                if (thisDecode->nAppType == APPLEFILE)
                {

                    if (*thisDecode->name)
					{
						thisDecode->name[MAXFILENAME-5]='\0';
                        strcat(thisDecode->name, ".apl");
					}
                    if (*thisDecode->ident)
					{
						thisDecode->ident[MAXINTERNALLINE-5]='\0';
                        strcat(thisDecode->ident, ".apl");
					}
                }
            }
        }

    }

  return result;
}

/* ------------------------------------------------------------------------
 * ParseAppSpecificLine
 * returns TRUE if it handles the line, else FALSE
 *
 * Example info lines handled by this function:
 *  part=n
 *  file=abc.def
 *  pfile=xyz.abc 
 *  Archive-name: fileident/part0n      // no extension included for filename
 *
 *  section N of uuencode 5.10 of file abcd.efg   by R.E.M.
 *  section n/N   file abcd.efg   [ Wincode v2.3 ]
 *  [ Section: n/N  File: abcd.efg  Encoder: Wincode v1.4 ]
 *  section n/N abcd.efg  [EnUU 2.1]
 *  abcd.efg    section  n/N   UUXFER ver 2.0 by David M. Read
 *  POST V2.0.0 abcd.efg (Part n/N)
 *  Interim MIME support
 *  Content-Type: application/octet-stream; [tokens]; name="abcd.efg"; [tokens]
 *  name=abcd.efg
 */
BOOL ParseAppSpecificLine (TypCoded * thisDecode, char *line)
{
  char *orig, *ptr;
  int seq, num, len;
  float ver;
  char name[MAXINTERNALLINE];
  char copy[MAXINTERNALLINE];

  for (orig = line; *orig && isspace ((unsigned char)*orig); orig++);  // skip leading spaces

  if (*orig == '\0')                    // blank line (or all spaces)

        return (SUCCESS);

  len = min (MAXINTERNALLINE - 1, strlen (orig));
  strntcpy (copy, orig, len);   // make a lower-case copy

  _strlwr (copy);

  if (!*thisDecode->ident && !strncmp (orig, "BEGIN", 5))      // specifically comparing case-sensitive
   {
        ParseInfoLine (thisDecode, copy, FALSE);
        return (TRUE);
  }

  if (sscanf (copy, "file=%s", name) == 1) {
        strntcpy (thisDecode->ident, name, MAXFILENAME);
        return (TRUE);
  }

  if (*copy == 'p')                             // info headers starting with 'p'
   {
        if (sscanf (copy, "part=%d", &seq) == 1) {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
          thisDecode->sequence = seq;
          if (!thisDecode->seqConfidence)
                thisDecode->seqConfidence = LOW;        // still don't have number of parts

          return (TRUE);
        }
        if (sscanf (copy, "pfile=%s", name) == 1) {
          strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
          return (TRUE);
        }
        /* POST */
        if (sscanf (copy, "POST V%*s %s (Part %d/%d)", name, &seq, &num) == 3) {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
          thisDecode->sequence = seq;
          thisNumBlocks = num;
          thisDecode->seqConfidence = HIGH;
          strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
          return (TRUE);
        }
        return (FALSE);                         // started with 'p' but we didn't have a template

  }

  /* note some of these look for a version number, then don't use it.  the
     version number is scanned simply to add to our confidence that this is
     a valid info line.  we want it to be included in the count - so we know
     if the line fits the template (hence don't use %*f)
   */

  if (UsingMIME && ParseMimeHeaderLine (thisDecode, orig)) 
  {
        return TRUE;
  }

  /* R.E.M., EnUU, and WinCode v2.x all start with "section " */
  if (!strncmp (copy, "section", 7)) 
  {
        ptr = &copy[7];
        /* R.E.M. */
        if (sscanf (ptr, " %d of uuencode %f of file %s", &seq, &ver, name) == 3) 
        {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
          thisDecode->sequence = seq;
          if (!thisDecode->seqConfidence)
                thisDecode->seqConfidence = LOW;        // still don't have number of parts

          strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
          thisContentEncoding = CODE_UU;
          return (TRUE);
        }
        /* EnUU */
        if (sscanf (ptr, " %d/%d %s [EnUU %f]", &seq, &num, name, &ver) == 4) 
        {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
          thisDecode->sequence = seq;
          thisNumBlocks = num;
          thisDecode->seqConfidence = HIGH;
          strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
          thisContentEncoding = CODE_UU;
          return (TRUE);
        }
        /* Wincode v2.x */
        if (sscanf (ptr, " %d/%d  file %s [ Wincode v%f ]", &seq, &num, name, &ver) == 4) 
        {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
          thisDecode->sequence = seq;
          thisNumBlocks = num;
          thisDecode->seqConfidence = HIGH;
          strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
          return (TRUE);
        }
        return (FALSE);                         // started with 'section' but we didn't have a template

  }
  /* Wincode v1.x */
  if (sscanf (copy, "[ section: %d/%d  File: %s  Encoder: Wincode v%f", &seq, &num, name, &ver) == 4) 
  {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
        thisDecode->sequence = seq;
        thisNumBlocks = num;
        thisDecode->seqConfidence = HIGH;
        strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
        return (TRUE);
  }

  /* UUXFER */
  if (sscanf (copy, "%s section %d/%d  UUXFER ver %f", name, &seq, &num, &ver) == 4) 
  {
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
        thisDecode->sequence = seq;
        thisNumBlocks = num;
        thisDecode->seqConfidence = HIGH;
        strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
        thisContentEncoding = CODE_UU;
        return (TRUE);
  }

  if (sscanf (copy, "archive-name: %[^/]/part%d", name, &seq) == 2)             // %[^/] reads a string up to a slash
  {
        strntcpy (thisDecode->ident, name, MAXINTERNALLINE-1);
#if SUPPORT_PARTIAL_MULTIPART
         thisPartNum = 
#endif
        thisDecode->sequence = seq;
        if (!thisDecode->seqConfidence)
          thisDecode->seqConfidence = LOW;      // still don't have number of parts

        return (TRUE);
  }

  return (FALSE);
}


//-------------------------------------------------------------------------
HANDLE ExecuteDecodedFile (int num, char *fileName)
{
	char association[MAXFILENAME];
	char ext[MAXFILENAME];
	HINSTANCE err;
	char *p, *search;
	BOOL bMatch = FALSE;
	HANDLE hReturn = NULL;

	//Make sure we don't blindly execute executables!
	GetFileExtension (ext, fileName);

	//look for matching extension in comma separated extensions string
	if (!GetPrivateProfileString (CODING, WARNEXTENTIONS, DEF_WarnOnEXE,  association, MAXFILENAME, szAppProFile)) 
	{
		strcpy(association, DEF_WarnOnEXE);
	}

	search = association;
	while ( *search && (p = stristr(search, ext) ) )
	{
		if ( (p == association) || (*(p-1) == ' ' || *(p-1) == ',') )
		{
			//make sure we haven't matched a substring
			int nLen = strlen(ext);
			if (nLen <= (int)strlen(p))
			{
				if ( *(p+nLen) == '\0' || *(p+nLen) == ',')
					bMatch = TRUE;
				break;
			}

		}
		search++;
	}

	if (bMatch)
	{

		if (NeverExeWarnFiles)
			return NULL;

		if (WVMessageBox(NetDoc.hWndFrame, IDS_WVCODING_APP_WARNING, 
			 IDS_WVCODING_APP_TITLE, MB_OKCANCEL | MB_ICONEXCLAMATION | MB_DEFBUTTON2) == IDCANCEL)
			return NULL;
	}

	if ( FindExecutable( fileName, ".", association) < (HINSTANCE) 32) 
	{
		_snprintf (str, MAXINTERNALLINE,"Cannot execute - no association for file type %s", 
				GetFileExtension (ext, fileName));
		UpdateThreadStatus (num, str);
		//return NULL;
	}

	// err = ShellExecute( NetDoc.hWndFrame, NULL, (LPCSTR)fileName, NULL, ".", SW_SHOWNORMAL);
	{

		SHELLEXECUTEINFO ExecInfo;

		ZeroMemory(&ExecInfo, sizeof(SHELLEXECUTEINFO));
		ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
		ExecInfo.fMask  = SEE_MASK_NOCLOSEPROCESS;
		ExecInfo.hwnd = NetDoc.hWndFrame;
		ExecInfo.lpVerb = NULL;
		ExecInfo.lpFile = (LPCSTR)fileName;
		ExecInfo.lpParameters = NULL;
		ExecInfo.lpDirectory = NULL;
		ExecInfo.nShow = SW_SHOWNORMAL;
		ExecInfo.hInstApp = NULL;

		ShellExecuteEx(&ExecInfo);
		hReturn = ExecInfo.hProcess;

		err = ExecInfo.hInstApp;
	}

	if (err < (HINSTANCE) 32) 
	{
		_snprintf (str, MAXINTERNALLINE, "Error executing %s, error %u", association, (int) err);
		UpdateThreadStatus (num, str);
		return NULL;
	}

	if (CodingStatusVerbose) 
	{
		_snprintf (str, MAXINTERNALLINE, "Executed %s", association);
		UpdateThreadStatus (num, str);
	}
	return hReturn;
}

void AddMonitoredFile(HANDLE hProcess, char * szFile)
{
	AutoDelete *pEntry = AutoDeleteList, *pLast = NULL;

	if (AutoDeleteList)
	{
		while (pEntry)
		{
			pLast = pEntry;
			pEntry = pEntry->pNext;
		}
	}

	pEntry = new AutoDelete;
	if (pEntry)
	{
		if (pLast)
			pLast->pNext = pEntry;
		else
		{
			AutoDeleteList = pEntry;
			AppCheckId = SetTimer (NetDoc.hDocWnd, ID_TIMER+1, 1000, (TIMERPROC) NULL);
		}

		strcpy(pEntry->szFile, szFile);
		pEntry->hProcess = hProcess;
		pEntry->pNext = NULL;
	}

}

//see if any monitored files need closing
void CheckMonitoredFiles(BOOL bForceClose)
{
	AutoDelete *pEntry = AutoDeleteList, *pLast = NULL;
	static BOOL bBusy = FALSE;
	if (bBusy)
		return; //no reentrant
	
	bBusy = TRUE;

	while (pEntry)
	{
		DWORD dwInfo = 0;

		if (!pEntry->hProcess || bForceClose || !GetExitCodeProcess(pEntry->hProcess, &dwInfo) || dwInfo != STILL_ACTIVE )
		{
			
			BOOL bDeleted = FALSE;
			if (GetFileAttributes(pEntry->szFile) != (DWORD) -1)
			{
				if (DeleteFile(pEntry->szFile))
				{
					bDeleted = TRUE;
				}
			}
			else
			{
				//something else deleted it
				bDeleted = TRUE;
			}
			if (pEntry->hProcess)
			{
				//something went wrong with the handle, stop using it
				CloseHandle(pEntry->hProcess);
				pEntry->hProcess = NULL;
			}
			if (bDeleted)
			{
				if (pLast)
				{
					pLast->pNext = pEntry->pNext;
					delete pEntry;
					pEntry = pLast;

				}
				else
				{
					AutoDeleteList = pEntry->pNext;
					delete pEntry;
					pEntry = AutoDeleteList;
					continue;
				}
			}

  
		} //still running
		pLast = pEntry;
		pEntry = pEntry->pNext;
	}
	
	if (!AutoDeleteList && AppCheckId)
	{
		KillTimer(NetDoc.hDocWnd, AppCheckId);	
		AppCheckId = 0;
	}
	bBusy = FALSE;

}

//we are going away, shut it all down
void CloseMonitoredFiles()
{
	CheckMonitoredFiles(TRUE);
}

/* this is for emacs
 * Local variables:
 * tab-width: 2
 * end:
 */
