home *** CD-ROM | disk | FTP | other *** search
- /********************************************************************
- * *
- * MODULE : WVCODING.C *
- * *
- * 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 *
- * Encode - Encodes a file into a text block *
- * EncodeToFile - Calls Encode, then writes block to *
- * an output 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@netcom.com) *
- * Date: Sept 13, 1993 *
- * *
- * Let me know if you like this code, or if you re-use any piece of *
- * it. I'm just curious to know what people think. *
- ********************************************************************/
- /*
- * $Id: wvcoding.c 1.12 1994/09/18 22:44:20 jcooper Exp $
- * $Log: wvcoding.c $
- * Revision 1.12 1994/09/18 22:44:20 jcooper
- * Better handling of status windows, using IsBusy flag, etc
- *
- * Revision 1.11 1994/09/16 00:58:48 jcooper
- * new coding-status window style. optimized info-header parser.
- * general cleanup for 92.6
- *
- * Revision 1.10 1994/08/24 18:00:29 jcooper
- * misc encoding/decoding changes
- *
- * Revision 1.9 1994/08/11 21:03:19 rushing
- * FindExecutable fix for NT
- *
- * Revision 1.8 1994/08/11 20:16:25 rushing
- * bug fix
- *
- * Revision 1.6 1994/07/25 20:05:13 jcooper
- * execution of decoded files
- *
- * Revision 1.5 1994/05/23 18:36:00 jcooper
- * new attach code, session [dis]connect
- *
- * Revision 1.4 1994/03/01 19:14:22 rushing
- * ifdef'd the DEBUG info
- *
- * Revision 1.3 1994/02/24 21:28:04 jcoop
- * jcoop changes.
- *
- * Revision 1.2 1994/02/09 18:01:08 cnolan
- * cnolan 90.2 changes
- *
- * Revision 1.1 1994/01/18 09:54:20 jcoop
- * Initial revision
- *
- */
- #include <windows.h>
- #include <windowsx.h>
- #include "wvglob.h"
- #include "winvn.h"
- #pragma hdrstop
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h> /* for isspace, isalnum, etc */
-
- #ifdef _DEBUG
- #define DEBUG_MAP
- #define DEBUG_MEM
- #define DEBUG_DECODE
- #endif
-
- #define LOW 1 // sequence confidence levels
- #define HIGH 2
- #define UNUSED 127 // unused mapping slots
-
- #define END_BLOCK 2
- #define CODE_LINE_LEN 45
- /*
- * Globals for this func
- */
- char
- xxTable[CODINGTABLESIZE] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- char
- uuTable[CODINGTABLESIZE] = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
- char
- base64Table[CODINGTABLESIZE] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- char *codingTable;
- char customTable[CODINGTABLESIZE];
-
- TypDecodeThread *threadList[MAX_DECODE_THREADS];
- int numDecodeThreads; // number of active decode threads
- int currentDecodeThread;
- int numDumbDecoded;
- BOOL bossanovaMIME; // = TRUE if found MIME Version header
- int thisNumBlocks; // MIME vars for current block
- char thisContentType[80];
- char thisContentDesc[MAXINTERNALLINE];
- char thisBoundary[MAXINTERNALLINE];
- int thisContentEncoding, prevContentEncoding;
- char prevBlockIdent[MAXFILENAME];
- /*
- * Private functions
- */
- int EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num);
- void EncodeUnit (unsigned char *out, unsigned char *in, int num);
- 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);
- BOOL WriteDecodeThread(int num);
- BOOL WriteBlockToFile(int num, TypCoded *thisDecode);
- BOOL WriteTopBlockToFile (int num, int *endFlag);
- BOOL AddDataToBlock (TypCoded *thisDecode, char *newData, unsigned int dataLen);
- void ParseInfoLine (TypCoded *thisDecode, char *line, BOOL guessIdent);
- BOOL ParseAppSpecificLine (TypCoded *thisDecode, char *line);
- char *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);
-
- void CreateCodingStatusWnd();
- int DecodeDataLine (TypCoded *decode, char *line);
- void CreateUUTable();
- BOOL TestIgnoreLine (char *line);
- int IsDataLine (char *line);
- BOOL TestDataLine (char *line);
- int ThreadTable (char *dest, char *name);
- void UpdateThreadStatus (int num, char *str);
-
- void 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
-
- #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);
- }
- }
- 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
- /* ------------------------------------------------------------------------
- * 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();
- if ((DecodeFile = fopen (fileName, "r")) == NULL)
- {
- MessageBox (hParentWnd, "Failure to read file", "File error", MB_OK);
- return;
- }
- if ((currentCoded = InitCoded(hParentWnd)) == NULL)
- {
- MessageBox (hParentWnd, "Unable to continue due to memory constraints. Aborted", "Init Coded Object Error", MB_OK);
- return;
- }
- while (1)
- {
- if ((ptr = fgets(mybuf, MAXINTERNALLINE, DecodeFile)) == NULL)
- strcpy (mybuf, "EOF"); // make sure wrap up occurs
-
- result = DecodeLine (currentCoded, mybuf);
- if (result == FAIL)
- {
- MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
- goto abortDecodeFile;
- }
- if (result == END_BLOCK)
- {
- if (CompleteThisDecode () == FAIL)
- {
- MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
- goto abortDecodeFile;
- }
- if ((currentCoded = InitCoded(hParentWnd)) == NULL)
- {
- MessageBox (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:;
- fclose(DecodeFile);
- DecodeDone ();
- }
- /* ------------------------------------------------------------------------
- * Decode a doc
- * Uses a doc instead of the comm article as a line-provider to the
- * decode routines
- */
- void
- DecodeDoc (HWND hParentWnd, TypDoc *Document)
- {
- int result;
- TypBlock far *BlockPtr;
- TypLine far *LinePtr;
-
- DecodeInit();
- if ((currentCoded = InitCoded(hParentWnd)) == NULL)
- {
- MessageBox (hParentWnd, "Unable to continue due to memory constraints. Aborted", "Init Coded Object Error", MB_OK);
- return;
- }
-
- LockLine (Document->hFirstBlock, sizeof (TypBlock), (TypLineID) 0L, &BlockPtr, &LinePtr);
- while (LinePtr->length != END_OF_BLOCK)
- {
- result = DecodeLine (currentCoded,
- ((char far *) LinePtr) + sizeof (TypText) + sizeof (TypLine));
-
- if (result == FAIL)
- {
- MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
- goto abortDecodeDoc;
- }
-
- if (result == END_BLOCK)
- {
- if (CompleteThisDecode () == FAIL)
- {
- MessageBox (hParentWnd, "Aborted decode", "Problems during decode", MB_OK | MB_ICONEXCLAMATION);
- goto abortDecodeDoc;
- }
- if ((currentCoded = InitCoded(hParentWnd)) == NULL)
- {
- MessageBox (hParentWnd, "Unable to continue due to memory constraints. Aborted", "Init Coded Object Error", MB_OK);
- return;
- }
- }
- NextLine (&BlockPtr, &LinePtr);
- }
- if (currentCoded != NULL) // finish final block
- CompleteThisDecode ();
-
- abortDecodeDoc:;
- GlobalUnlock (BlockPtr->hCurBlock);
- DecodeDone ();
- }
- /* ------------------------------------------------------------------------
- * Encode a file and ask the user for a file to name to store it in
- */
- void
- EncodeToFile (HWND hParentWnd, char *inFile)
- {
- register unsigned long i;
- char fileName[MAXFILENAME];
- OFSTRUCT outFileStruct;
- HFILE hAttachFile;
- TypTextBlock *encodeData;
-
- if ((encodeData = InitTextBlock (hParentWnd)) != NULL)
- {
- CreateStatusArea(hParentWnd);
- strcpy (currentCoded->ident, inFile); // status window info
-
- if (Encode (encodeData, inFile, ADD_TO_FILE) != FAIL)
- {
- fileName[0]='\0';
- if (AskForNewFileName (hParentWnd, fileName, "", FALSE) != FAIL)
- {
- if ((hAttachFile = OpenFile ((char far *) fileName, &outFileStruct, OF_CREATE)) < 0)
- MessageBox(hParentWnd, "Unable to open output file", "File Error", MB_OK|MB_ICONSTOP);
- else
- {
- for (i = 0; i < encodeData->numLines; i++)
- _lwrite (hAttachFile, TextBlockLine (encodeData, i), lstrlen(TextBlockLine (encodeData, i)));
- _lclose (hAttachFile);
- }
- }
- }
- FreeTextBlock (encodeData);
- DestroyStatusArea ();
- }
- }
-
- /* ------------------------------------------------------------------------
- * 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 and ReadFileToTextBlock
- * to show status in progress
- * Use UpdateBlockStatus to update/display status
- * Use DestroyStatusArea to clean up when done
- */
- void
- CreateStatusArea (HWND hParentWnd)
- {
- if ((currentCoded = InitCoded(hParentWnd)) == NULL)
- return;
- CreateCodingStatusWnd();
- currentCoded->sequence = 1; // status window info
-
- }
-
- void
- DestroyStatusArea ()
- {
- DestroyCodedBlock (¤tCoded);
- DestroyWindow (hCodedBlockWnd);
- hCodedBlockWnd = (HWND)NULL;
- }
-
- void
- CreateCodingStatusWnd()
- {
- /* Create coding status window, top center */
- hCodedBlockWnd = CreateWindowEx (WS_EX_DLGMODALFRAME, "WinVnBlockCoding", "Block Encoding Status",
- WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_THICKFRAME,
- (xScreen-STATUSWIDTH)>>1, 1, STATUSWIDTH, STATUSHEIGHT,
- (HWND)NULL, (HMENU)NULL, hInst, (void far *)NULL);
-
- SetHandleBkBrush( hCodedBlockWnd, hStatusBackgroundBrush);
- ShowWindow (hCodedBlockWnd, SW_SHOWNORMAL);
- }
-
-
- /* ------------------------------------------------------------------------
- * Adds text to a thread coding status block, and refreshes
- * the thread status window
- */
- void
- UpdateThreadStatus (int num, char *str)
- {
- AddLineToTextBlock (threadList[num]->statusText, str);
- InvalidateRect (threadList[num]->statusText->hTextWnd, NULL, FALSE);
- UpdateWindow (threadList[num]->statusText->hTextWnd);
- }
-
- /* ------------------------------------------------------------------------
- * Refreshes the block status window
- */
- void
- UpdateBlockStatus ()
- {
- InvalidateRect (hCodedBlockWnd, NULL, FALSE);
- UpdateWindow (hCodedBlockWnd);
- }
-
- /* ------------------------------------------------------------------------
- * Encode a file
- * Called _after_ the attach dialog, so all globals for this encode
- * are set (sorry about the globals...)
- * Result is encoded file in TextBlock
- * Assumes currentCoded is initialized
- */
- int
- Encode (TypTextBlock *textBlock, char *fileName, int mode)
- {
- HFILE hFile;
- int numRead, lineLen, start;
- register int i;
- unsigned char inBuf[CODE_LINE_LEN];
- unsigned char outLine[MAXINTERNALLINE];
- extern char *NameWithoutPath ();
-
- CodingState = ATTACH_PROCESSING;
-
- // Set up encoding map
- switch (EncodingTypeNum)
- {
- 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_CUSTOM:
- codingTable = UserCodingTable;
- // add 2 table lines
- if (AddEndedLineToTextBlock (textBlock, "table", mode))
- return (FAIL);
- memmove (str, UserCodingTable, 32);
- str[32] = '\0';
- if (AddEndedLineToTextBlock (textBlock, str, mode))
- return (FAIL);
- memmove (str, &UserCodingTable[32], 32);
- if (AddEndedLineToTextBlock (textBlock, str, mode))
- return (FAIL);
- break;
- }
-
- // Prepare for encoding
- switch (EncodingTypeNum)
- {
- 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
- NameWithoutPath (str, fileName);
- sprintf (outLine, "begin 755 %s", str);
- if (AddEndedLineToTextBlock (textBlock, outLine, mode))
- return (FAIL);
- currentCoded->numLines++;
- // set 1st char to appropriate line length value
- outLine[0] = codingTable[CODE_LINE_LEN];
- start = 1; // count includes the count char itself
- break;
- case CODE_BASE64:
- // base64 is a 3-to-4 encoding, with no line len indicator and
- // no 'begin' or 'end' lines
- start = 0;
- }
- lineLen = CODE_LINE_LEN;
-
- // open the file and have at it
- hFile = _lopen (fileName, OF_READ);
- while (1)
- {
- if ((numRead = _lread(hFile, inBuf, lineLen)) == 0)
- break;
- if (numRead < lineLen) // last line length value
- {
- if (EncodingTypeNum != CODE_BASE64)
- outLine[0] = codingTable[numRead];
-
- for (i = numRead; i < lineLen; i++)
- inBuf[i] = 0;
- }
- EncodeLine (outLine, inBuf, start, numRead);
- if (AddEndedLineToTextBlock (textBlock, outLine, mode))
- return (FAIL);
- currentCoded->numLines++;
- currentCoded->numBytes += numRead;
- if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
- UpdateBlockStatus ();
- }
- _lclose(hFile);
- switch (EncodingTypeNum)
- {
- case CODE_UU:
- case CODE_XX:
- case CODE_CUSTOM:
- // add zero length line
- outLine[0] = codingTable[0];
- outLine[1] = '\0';
- if (AddEndedLineToTextBlock (textBlock, outLine, mode))
- return (FAIL);
- // add 'end' line
- strcpy (outLine, "end");
- if (AddEndedLineToTextBlock (textBlock, outLine, mode))
- return (FAIL);
- currentCoded->numLines+=2;
- }
- UpdateBlockStatus ();
-
- CodingState = INACTIVE;
- return (SUCCESS);
- }
-
- /* ------------------------------------------------------------------------
- * Encode num chars from line
- * Put data in outLine starting at start
- */
- int
- EncodeLine (unsigned char *outLine, unsigned char *line, int start, int num)
- {
- register int i, j;
-
- for (j = start, i = 0; i < num; j+=4, i+=3)
- EncodeUnit (&outLine[j], &line[i], (i+3 > num) ? num - i : 3);
-
- outLine[j] = '\0';
- return (j);
- }
-
- void
- EncodeUnit (unsigned char *out, unsigned char *in, int num)
- {
- out[0] = codingTable[((in[0]>>2) & 63)];
- out[1] = codingTable[(((in[0]<<4)|(in[1]>>4)) & 63)];
- if (num == 1)
- {
- if (EncodingTypeNum == CODE_BASE64)
- strcpy (&out[2], "==");
- return;
- }
-
- out[2] = codingTable[(((in[1]<<2)|(in[2]>>6)) & 63)];
- if (num == 2)
- {
- if (EncodingTypeNum == CODE_BASE64)
- strcpy (&out[3], "=");
- 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 ()
- {
- register int i;
-
- #ifdef DEBUG_MEM
- InitDebugLog();
- #endif
-
- for (i = 0; i < MAX_DECODE_THREADS; 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;
- customTable[0] = '\0';
-
- #ifdef DEBUG_MEM
- sprintf(debug, "\nInitialized decoding, %d threads", MAX_DECODE_THREADS);
- DebugLog();
- #endif
- // Create coding status window (open for duration of coding only)
- CreateCodingStatusWnd();
- 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 ()
- {
- char mybuf[MAXINTERNALLINE], name[MAXINTERNALLINE];
-
- #ifdef DEBUG_MEM
- sprintf(debug, "\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 (!CodingStatusVerbose)
- if (threadList[0]->name[0] != '\0')
- sprintf (name, "%s ", threadList[0]->name);
- else
- sprintf (name, "%s ", threadList[0]->ident);
-
- if (threadList[0]->numBlocks == 0)
- { // premature end of file
- WriteDecodeThread (0);
- sprintf (mybuf, "%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)
- {
- sprintf (mybuf, "%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);
- sprintf (mybuf, "%sDecode is missing parts, written total size %ld", name, threadList[0]->totalBytes);
- UpdateThreadStatus (0, mybuf);
- }
- else
- {
- sprintf (mybuf, "%sCompleteness confidence is medium, written total size %ld", name, threadList[0]->totalBytes);
- UpdateThreadStatus (0, mybuf);
- WriteDecodeThread (0);
- }
- DestroyThread (0); // decrements numDecodeThreads
- }
- currentCoded = NULL;
- CodingState = INACTIVE;
- CommDecoding = FALSE;
- DestroyWindow (hCodedBlockWnd);
- hCodedBlockWnd = (HWND)NULL;
- }
-
- /* ------------------------------------------------------------------------
- * 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, "\nInitializing a coded object");
- DebugLog();
- #endif
- if ((thisCoded = (TypCoded *) GlobalAllocPtr ( GMEM_MOVEABLE, sizeof (TypCoded))) == NULL)
- return ((TypCoded *) NULL);
-
- #ifdef DEBUG_MEM
- sprintf(debug, "\nAllocing data size %d", BASE_BLOCK_SIZE);
- DebugLog();
- #endif
- if ((thisCoded->data = (char huge *) GlobalAllocPtr (GMEM_MOVEABLE, BASE_BLOCK_SIZE * sizeof (char))) == NULL)
- return ((TypCoded *) NULL);
-
- thisCoded->maxBytes = BASE_BLOCK_SIZE;
- thisCoded->numBytes = 0;
- thisCoded->numLines = 0;
- thisCoded->sequence = -1;
- thisCoded->seqConfidence = 0;
- thisCoded->name[0] = '\0';
- thisCoded->ident[0] = '\0';
- thisCoded->endFlag = thisCoded->beginFlag = FALSE;
- thisCoded->hParentWnd = hParentWnd;
- bossanovaMIME = 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, "\nDestroying thread %d", num);
- DebugLog();
- #endif
- if (CodingStatusVerbose)
- threadList[num]->statusText->IsBusy = FALSE;
-
- 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, "\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;
- char mybuf[MAXINTERNALLINE];
- int x, y, width, height, maxX, maxY;
-
- if (DumbDecode)
- {
- if (numDecodeThreads == 0)
- num = -1;
- else
- num = 0;
- } else
- /* Search existing threads for an ident match. If
- * no match found, or no threads exist yet, add new thread.
- */
- for (num = -1, i = 0; i < numDecodeThreads; i++)
- 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, "\nCreating new thread ident %s", currentCoded->ident);
- DebugLog();
- #endif
- num = numDecodeThreads;
- numDecodeThreads++;
-
- threadList[num] = (TypDecodeThread *) GlobalAllocPtr (GMEM_MOVEABLE, sizeof (TypDecodeThread));
- threadList[num]->expectedNumBlocks = 0;
- threadList[num]->numBlocksWritten = 0;
- threadList[num]->numBlocks = 0;
- threadList[num]->totalBytes = 0;
- threadList[num]->dosFileName[0] = '\0';
- if (thisContentEncoding == CODE_CUSTOM)
- strncpy (threadList[num]->customTable, customTable, CODINGTABLESIZE);
- threadList[num]->contentEncoding = thisContentEncoding;
- strcpy (threadList[num]->ident, currentCoded->ident);
- if (currentCoded->name[0] != '\0')
- strcpy (threadList[num]->name, currentCoded->name);
- else
- threadList[num]->name[0] = '\0';
-
- if (CodingStatusVerbose || NumStatusTexts == 0)
- {
- if ((threadList[num]->statusText = InitTextBlock(hCodedBlockWnd)) == NULL)
- MessageBox (hCodedBlockWnd, "Memory allocation error in text block", "Memory Error", MB_OK);
-
- if (NumStatusTexts + 1 > MAX_DECODE_THREADS);
- // ?????
-
- CodingStatusText[NumStatusTexts++] = threadList[num]->statusText;
-
- if (CodingStatusVerbose)
- sprintf(mybuf, "Decoding Status for file %s", (threadList[num]->name[0]!='\0')?threadList[num]->name:threadList[num]->ident);
- else
- strcpy(mybuf, "Decoding Status");
-
- width = xScreen>>1;
- height = (yScreen>>1) - CaptionHeight;
- maxX = 3 * (width>>1); /* 3/4 screen width */
- maxY = 3 * (height>>1); /* 3/4 screen height */
-
- x = (NumStatusTexts * 10) % maxX;
- y = (yScreen>>2) + (NumStatusTexts * CaptionHeight) % maxY;
-
- threadList[num]->statusText->hTextWnd =
- CreateWindow ("WinVnCoding", mybuf,
- WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
- x, y, width, height,
- (HWND)NULL, (HMENU)NULL, hInst, (void far *)NULL);
-
- if (threadList[num]->statusText->hTextWnd == (HWND)NULL)
- {
- FreeTextBlock(threadList[num]->statusText);
- MessageBox (hCodedBlockWnd, "Couldn't create status text window","Window Creation Error", MB_OK);
- return (FAIL);
- }
- SetHandleBkBrush (threadList[num]->statusText->hTextWnd,
- hStatusBackgroundBrush);
- ShowWindow (threadList[num]->statusText->hTextWnd, SW_SHOWNORMAL);
- UpdateWindow (threadList[num]->statusText->hTextWnd);
- }
- else
- threadList[num]->statusText = CodingStatusText[0];
-
- }
-
- /* If this new block enlightens us with any useful thread info, update
- * the thread
- */
- if (currentCoded->name[0] != '\0')
- strcpy (threadList[num]->name, currentCoded->name);
- // if (threadList[num]->contentType[0] == '\0')
- // strcpy (threadList[num]->contentType, thisContentType);
- 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 ||
- threadList[num]->codedBlockList[i]->sequence >= currentCoded->sequence)
- break;
- }
- InsertBlockInThread (num, currentCoded, i);
- }
- }
- currentDecodeThread = num;
- if (!CodingStatusVerbose)
- return (SUCCESS);
-
- 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, "\nInserting into thread %s 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, "\nRemoved from thread %s 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
- */
- BOOL
- CompleteThisDecode ()
- {
- char mybuf[MAXINTERNALLINE];
- int num, singleBlockDone, foundEnd;
-
- UpdateBlockStatus (); // final size displayed
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nCompleteThisDecode for file %s, block %d",
- currentCoded->ident, currentCoded->sequence);
- DebugLog();
- #endif
- if (currentCoded->numBytes == 0)
- {
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nNon-data block. Discarding it.");
- DebugLog();
- #endif
- DestroyCodedBlock (¤tCoded);
- return (SUCCESS);
- }
-
- if (DumbDecode)
- {
- if (numDumbDecoded == 0 && !currentCoded->beginFlag)
- return (SUCCESS); // Dumb skipping a data block
- if (numDumbDecoded > 0 && currentCoded->beginFlag)
- { // found next begin without an end
- if (CodingStatusVerbose)
- sprintf (mybuf, "Decode missing end, total size %ld, wrote to file %s", threadList[num]->totalBytes, threadList[num]->dosFileName);
- else
- sprintf (mybuf, "%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 for some reason (?) we didn't get a begin, but this is block 1
- if (currentCoded->sequence == 1 &&
- currentCoded->seqConfidence == HIGH &&
- !currentCoded->beginFlag)
- {
- currentCoded->beginFlag;
- strcpy (currentCoded->name, currentCoded->ident);
- }
-
- if (AddToThreadList () == FAIL)
- return (FAIL);
-
- prevContentEncoding = thisContentEncoding;
- thisContentEncoding = CODE_UNKNOWN;
-
- num = currentDecodeThread;
- singleBlockDone = currentCoded->beginFlag && currentCoded->endFlag;
-
- if (!CodingStatusVerbose &&
- (threadList[num]->numBlocksWritten == 0 && threadList[num]->numBlocks == 1))
- {
- sprintf (mybuf, "%s Decode in progress", (threadList[num]->name[0]!='\0')?threadList[num]->name:threadList[num]->ident);
- UpdateThreadStatus (num, mybuf);
- }
-
- if (DumbDecode)
- {
- if (WriteTopBlockToFile (num, &foundEnd) == FAIL)
- return (FAIL);
- numDumbDecoded++;
- }
- else
- {
- /* 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 (¤tCoded); // threadList[num]->codedBlockList[0]
- DeleteBlockFromThread (num, 0);
- break;
- }
- if (threadList[num]->codedBlockList[0]->sequence != threadList[num]->numBlocksWritten + 1)
- break;
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nBlock in sequence, writing it, file %s, block %d",
- threadList[num]->name, threadList[num]->codedBlockList[0]->sequence);
- DebugLog();
- #endif
- if (WriteTopBlockToFile (num, &foundEnd) == FAIL)
- return (FAIL);
- }
- }
- if (singleBlockDone || (foundEnd && DumbDecode) ||
- (foundEnd && threadList[num]->expectedNumBlocks > 0 &&
- threadList[num]->numBlocksWritten >= threadList[num]->expectedNumBlocks))
- {
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nThread complete: file %s", threadList[num]->name);
- DebugLog();
- #endif
- if (WriteDecodeThread(num) == FAIL)
- {
- DestroyThread(num);
- currentCoded = NULL;
- return (FAIL);
- }
-
- if (CodingStatusVerbose)
- sprintf (mybuf, "Decode complete, total size %ld, wrote to file %s", threadList[num]->totalBytes, threadList[num]->dosFileName);
- else
- sprintf (mybuf, "%s Decode complete, total size %ld, wrote to file %s",
- 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
- */
- BOOL
- WriteTopBlockToFile (int num, int *endFlag)
- {
- char mybuf[MAXINTERNALLINE];
-
- if (WriteBlockToFile (num, threadList[num]->codedBlockList[0]) == FAIL)
- {
- sprintf (mybuf, "Could not write to file %s", threadList[num]->dosFileName);
- UpdateThreadStatus (num, mybuf);
- DestroyThread(num);
- return (FAIL);
- }
-
- *endFlag = threadList[num]->codedBlockList[0]->endFlag;
- DestroyCodedBlock (&(threadList[num]->codedBlockList[0]));
- DeleteBlockFromThread (num, 0);
- threadList[num]->numBlocksWritten++;
- return (SUCCESS);
- }
-
- /* ------------------------------------------------------------------------
- * Write data for a thread to a file
- */
- BOOL
- WriteDecodeThread (int num)
- {
- register int i;
- char mybuf[MAXINTERNALLINE];
-
- for (i = 0; i < threadList[num]->numBlocks; i++)
- {
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nSaving sequence %d", threadList[num]->codedBlockList[i]->sequence);
- DebugLog();
- #endif
- if (WriteBlockToFile (num, threadList[num]->codedBlockList[i]) == FAIL)
- {
- sprintf (mybuf, "Could not write to file %s", threadList[num]->dosFileName);
- UpdateThreadStatus (num, mybuf);
- return (FAIL);
- }
- }
- if (ExecuteDecodedFiles)
- ExecuteDecodedFile(num, 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
- */
- BOOL
- WriteBlockToFile(int num, TypCoded *thisDecode)
- {
- register unsigned long i;
- unsigned int chunkSize;
- char actualFileName[MAXFILENAME], mybuf[MAXINTERNALLINE];
- OFSTRUCT outFileStruct;
- HFILE hWriteFile;
- UINT fileMode;
-
- #define CHUNK_SIZE 65500 /* must not exceed UINT (65534) */
-
- if (threadList[num]->dosFileName[0] != '\0') {
- fileMode = OF_WRITE;
- if (strchr (threadList[num]->dosFileName, '\\') == NULL)
- {
- strcpy (actualFileName, DecodePathName);
- if (actualFileName[strlen(actualFileName)-1] != '\\')
- strcat(actualFileName, "\\");
- strcat(actualFileName, threadList[num]->dosFileName);
- }
- else
- strcpy (actualFileName, threadList[num]->dosFileName);
- }
- else
- {
- /* First time write to file
- */
- fileMode = OF_CREATE;
-
- actualFileName[0] = '\0';
- if (threadList[num]->name[0] != '\0')
- strcpy (threadList[num]->dosFileName, threadList[num]->name);
- else if (threadList[num]->ident[0] != '\0')
- strcpy (threadList[num]->dosFileName, threadList[num]->ident);
- if (threadList[num]->dosFileName[0] != '\0')
- {
- strcpy (actualFileName, DecodePathName);
- if (actualFileName[strlen(actualFileName-1)] != '\\')
- strcat(actualFileName, "\\");
- strcat(actualFileName, threadList[num]->dosFileName);
- }
-
- /* smart filer work */
- if (SmartFile(hCodedBlockWnd, actualFileName) == FAIL)
- return (FAIL);
-
- strcpy (threadList[num]->dosFileName, actualFileName);
- }
-
- if ((hWriteFile = OpenFile ((char far *) actualFileName, &outFileStruct, fileMode)) < 0)
- {
- MessageBox(hCodedBlockWnd, "Unable to open output file", "File Error", MB_OK|MB_ICONSTOP);
- return (FAIL);
- }
- 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)
- {
- MessageBox(hCodedBlockWnd, "Error writing to file", "File Error", MB_OK|MB_ICONSTOP);
- _lclose(hWriteFile);
- return (FAIL);
- }
- }
-
- 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);
- }
- _lclose(hWriteFile);
- return (SUCCESS);
- }
-
- /* ------------------------------------------------------------------------
- * Decode a line in article. Store in thisDecode
- */
- int
- DecodeLine(TypCoded *thisDecode, char *line)
- {
- register int i;
- static int table_count;
- char thisBlockName[MAXINTERNALLINE];
- int mode;
-
- if (strlen(line) == 0)
- return (SUCCESS);
-
- thisDecode->numLines++;
- if (thisDecode->numLines % STATUS_UPDATE_FREQ == 0)
- UpdateBlockStatus ();
-
- switch (CodingState)
- {
- case DECODE_SKIPPING:
- if (strlen(line) < 3) // if we're skipping, ignore any really short line
- return (SUCCESS);
-
- // 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, "\nStart of new encoded file %s, mode %d", thisBlockName, mode);
- DebugLog();
- #endif
- if (thisBlockName[0] != '\0')
- strcpy (thisDecode->name, strlwr(thisBlockName));
- else
- strcpy (thisDecode->name, thisDecode->ident);
-
- thisDecode->sequence = 1;
- thisDecode->beginFlag = TRUE;
- // thisDecode->mode = mode;
-
- CodingState = DECODE_PROCESSING;
- 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))
- ParseInfoLine (thisDecode, &line[8], TRUE);
- else if (!DumbDecode && !_strnicmp(line, "summary:", 8))
- ParseInfoLine (thisDecode, &line[8], FALSE);
- else if (!DumbDecode && ParseAppSpecificLine (thisDecode, line))
- return (SUCCESS);
- else if (!strncmp(line, "table", 5))
- { // next two lines are table
- CodingState=DECODE_GET_TABLE;
- table_count = 0;
- 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))
- {
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nFound start of next data section", line);
- DebugLog();
- #endif
- CodingState = DECODE_PROCESSING;
- // found good line, process it and continue processing
- return (DecodeDataLine(thisDecode, line));
- }
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nskipped: %s", 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
- strncpy (str, customTable, 64);
- str[64]='\0';
- sprintf(debug, "\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:
- /* 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 (!_strnicmp (line, "end", 3) || !IsDataLine(line) ||
- (bossanovaMIME && !strcmp (&line[2], thisBoundary))) // skip '--'
- {
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nSwitching back to skipping\nskipped: %s", line);
- DebugLog();
- #endif
- CodingState = DECODE_SKIPPING;
-
- /* 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.
- */
- if (thisContentEncoding != CODE_CUSTOM && thisDecode->numBytes < 80)
- {
- thisDecode->numBytes = 0;
- thisContentEncoding = CODE_UNKNOWN;
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nBogus table! Scrapping so far, reprocessing line: %s", line);
- DebugLog();
- #endif
- thisDecode->numLines--;
- return (DecodeLine (thisDecode, line));
- }
- strcpy (prevBlockIdent, thisDecode->ident);
-
- if (!strncmp (line, "end", 3))
- {
- thisDecode->endFlag = TRUE;
- if (thisNumBlocks == -1 && thisDecode->sequence != -1)
- thisNumBlocks = thisDecode->sequence;
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nFound end of file %s", thisDecode->ident);
- DebugLog();
- #endif
- }
- return (END_BLOCK);
- }
- return (DecodeDataLine(thisDecode, line));
- }
- }
-
- /* ------------------------------------------------------------------------
- * 4 to 3 line decoder
- */
- BOOL DecodeDataLine (TypCoded *thisDecode, char *line)
- {
- register unsigned int i, j;
- unsigned int decodedCount, numEncoded, numDecoded, checkSum, startNum, stop;
- unsigned char buf[4], outLine[120];
- char *thisMap = CodingMap[thisContentEncoding];
-
- switch (thisContentEncoding)
- {
- case CODE_QP:
- // not implemented yet
- return (FAIL);
-
- case CODE_UU:
- case CODE_XX:
- case CODE_CUSTOM:
- // 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]];
- numEncoded = 4 * ((thisMap[line[0]] + 2) / 3); // don't include count char
- startNum = 1;
- break;
- case CODE_BASE64:
- // base64 is 4-to-3 decoding with nonexplicit line lengths
- numEncoded = strlen(line);
- decodedCount = 3 * (numEncoded / 4);
- startNum = 0;
- break;
- }
- 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
- stop = min(4, numEncoded - i + 1);
- 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) break;
- outLine[numDecoded++] = buf[1]<<4|buf[2]>>2;
- if (j == 2) break;
- outLine[numDecoded++] = buf[2]<<6|buf[3];
- }
- if (numDecoded && AddDataToBlock (currentCoded, outLine, numDecoded) == FAIL)
- return (FAIL);
-
- /* if (checkSum%64 != line[i])
- {
- printf("\nChecksum error: %d vs. %d", checkSum%64, line[i]);
- break;
- } else
- printf("\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
- */
- BOOL
- AddDataToBlock (TypCoded *thisDecode, char *newData, unsigned int dataLen)
- {
- if (thisDecode->numBytes + (unsigned long) dataLen > thisDecode->maxBytes)
- {
- thisDecode->maxBytes += BASE_BLOCK_SIZE;
- #ifdef DEBUG_MEM
- sprintf(debug, "\nReallocing data to %ld", thisDecode->maxBytes);
- DebugLog();
- #endif
- if ((thisDecode->data = (char huge *) GlobalReAllocPtr (thisDecode->data, thisDecode->maxBytes, GMEM_MOVEABLE)) == NULL)
- return (FALSE);
- }
- memmove (thisDecode->data + thisDecode->numBytes, newData, dataLen);
- thisDecode->numBytes += dataLen;
- return (SUCCESS);
- }
-
- /* ------------------------------------------------------------------------
- * Determine if the line is a data line
- */
- BOOL
- IsDataLine (char *line)
- {
- 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 (currentCoded->ident[0] != '\0' &&
- !strcmp (currentCoded->ident, prevBlockIdent))
- {
- thisContentEncoding = prevContentEncoding;
- #ifdef DEBUG_MAP
- strcpy (debug, "\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, "\nUsing stored thread table");
- DebugLog();
- #endif
- }
- else
- {
- thisContentEncoding = CODE_UU;
- if (TestDataLine (line))
- {
- #ifdef DEBUG_MAP
- sprintf (debug, "\nUsing UU table: %s", line);
- DebugLog();
- #endif
- return (TRUE);
- }
- thisContentEncoding = CODE_XX;
- if (TestDataLine (line))
- {
- #ifdef DEBUG_MAP
- sprintf (debug, "\nUsing XX table: %s", line);
- DebugLog();
- #endif
- return (TRUE);
- }
- thisContentEncoding = CODE_BASE64;
- if (TestDataLine (line))
- {
- #ifdef DEBUG_MAP
- sprintf (debug, "\nUsing Base64 table: %s", line);
- DebugLog();
- #endif
- return (TRUE);
- }
- thisContentEncoding = CODE_UNKNOWN;
- return (FALSE);
- }
- }
- return (TestDataLine(line));
- }
-
- BOOL
- TestDataLine (char *line)
- {
- unsigned int expectedLineLen, dataLen, lineLen;
- register unsigned int i;
- 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;
-
- if (lineLen < expectedLineLen)
- 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!
- */
- for (dataLen = lineLen;
- isspace (line[dataLen-1]) && dataLen > 0 && dataLen != expectedLineLen;
- --dataLen);
-
- // May have a checksum character, so allow for one extra char
- if (expectedLineLen != dataLen && expectedLineLen+1 != dataLen)
- return (FALSE);
-
- line[dataLen] = '\0'; // permanently chop off the white space
- // It's the right length, now check content for match w/ table
- for (i = 0; i < expectedLineLen; i++)
- if (thisMap[line[i]] == UNUSED)
- return (FALSE);
- return (TRUE);
-
- case CODE_BASE64:
- // permanently remove all trailing space
- while (lineLen > 0 && isspace (line[lineLen-1]))
- line[--lineLen] = '\0';
-
- // for base 64, just check if all chars in coding map (allow pad '=')
- for (i = 0; i < lineLen; i++)
- if (thisMap[line[i]] == UNUSED && line[i] != '=')
- return (FALSE);
- return (TRUE);
-
- case CODE_QP:
- // not implemented yet
- default:
- return (FALSE);
- }
- }
-
- /* ------------------------------------------------------------------------
- * Mappings for encoding/decoding
- */
- int
- CreateCodingMap(char *map, char *table)
- {
- register unsigned int i;
-
- for (i=0; i < 128; 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]]=i;
-
- #ifdef DEBUG_MAP2
- sprintf(debug, "\nCoding Map:\n"); DebugLog();
- for (i=0; i < 128; i++)
- {
- sprintf(debug, "%d ",map[i]);
- DebugLog();
- }
- #endif
- return (-1);
- }
-
- int
- ThreadTable (char *dest, char *ident)
- {
- int num;
- register int i;
-
- for (num = -1, i = 0; i < numDecodeThreads; i++)
- if (!_stricmp (ident, threadList[i]->ident))
- num = i;
-
- if (num == -1) // no thread by that name
- return (CODE_UNKNOWN);
- else
- {
- if (threadList[num]->contentEncoding == CODE_CUSTOM)
- strncpy (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)
- {
- char tok[MAXINTERNALLINE], next[MAXINTERNALLINE];
- char guessName[MAXINTERNALLINE];
- char *ptr, *thisTok, *tmp;
- int numberMode;
-
- numberMode = SEEK_PARTNUM;
- ptr = line;
-
- guessName[0] = '\0';
- while (*ptr)
- {
- if (ReadSubjectToken (thisTok = tok, &ptr) == NULL)
- break;
-
- // NUMBERS
- if (numberMode != IGNORE_NUMBERS)
- if (isnumber (thisTok))
- {
- if (numberMode == SEEK_PARTNUM)
- {
- thisDecode->sequence = atoi (thisTok);
- thisDecode->seqConfidence = LOW;
-
- if (ReadSubjectToken (next, &ptr) == NULL)
- break;
- if (!_stricmp (next, "/") ||
- !_stricmp (next, "of"))
- {
- numberMode = SEEK_NUMPARTS;
- continue;
- }
- else // no numParts immediately following
- thisTok = next; // process the next tok
- }
- else // numberMode == SEEK_NUMPARTS
- {
- thisNumBlocks = atoi (tok);
- numberMode = IGNORE_NUMBERS;
- thisDecode->seqConfidence = HIGH;
- continue;
- }
- }
- else if (numberMode == SEEK_NUMPARTS)
- // We had '# of non#', a red herring, seek again
- numberMode == SEEK_PARTNUM;
-
- // 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)))
- strcpy (thisDecode->ident, thisTok);
- 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))
- strcpy (thisDecode->ident, thisTok);
-
- // Save first word found for thread name
- // prefer ident not starting with 're'
- else if (guessName[0] == '\0' || !_strnicmp(guessName, "re", 2))
- strcpy (guessName, thisTok);
- }
- }
- }
- if (guessIdent && thisDecode->ident[0] == '\0')
- strcpy (thisDecode->ident, guessName);
-
- #ifdef DEBUG_DECODE
- sprintf(debug, "\nSubject header: name %s, part number %d (confidence %d), num parts %d",
- thisDecode->ident, thisDecode->sequence, thisDecode->seqConfidence, thisNumBlocks);
- DebugLog();
- #endif
- }
-
- char *
- ReadSubjectToken (char *dest, char **ptr)
- {
- int len;
- register int i;
- char *str = *ptr;
-
- if (*str == '\0')
- return ('\0');
-
- len = strcspn (str, " ()[]\\/:,\"'`{};\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
- }
-
- 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);
- }
-
- /* 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)
- */
- 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(*orig); orig++); // skip leading spaces
-
- if (*orig == '\0') // blank line (or all spaces)
- return (SUCCESS);
-
- len = min(MAXINTERNALLINE-1, strlen(orig));
- strncpy (copy, orig, len); // make a lower-case copy
- copy[len] = '\0';
- strlwr(copy);
-
- if (!strncmp(orig, "BEGIN", 5)) // specifically comparing case-sensitive
- {
- ParseInfoLine(thisDecode, copy, FALSE);
- return (TRUE);
- }
-
- if (sscanf(copy, "file=%s", name) == 1)
- {
- strcpy (thisDecode->ident, name);
- return (TRUE);
- }
-
- if (*copy == 'p') // info headers starting with 'p'
- {
- if (sscanf(copy, "part=%d", &seq) == 1)
- {
- 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)
- {
- strcpy (thisDecode->ident, name);
- return (TRUE);
- }
- /* POST */
- if (sscanf (copy, "POST V%*s %s (Part %d/%d)", name, &seq, &num) == 3)
- {
- thisDecode->sequence = seq;
- thisNumBlocks = num;
- thisDecode->seqConfidence = HIGH;
- strcpy (thisDecode->ident, name);
- 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)
- */
-
- /* R.E.M., EnUU, and WinCode v2.x all start with "section " */
- if (!strncmp(copy, "section", 7))
- {
- ptr = ©[7];
- /* R.E.M. */
- if (sscanf (ptr, " %d of uuencode %f of file %s", &seq, &ver, name) == 3)
- {
- thisDecode->sequence = seq;
- if (!thisDecode->seqConfidence)
- thisDecode->seqConfidence = LOW; // still don't have number of parts
-
- strcpy (thisDecode->ident, name);
- thisContentEncoding = CODE_UU;
- return (TRUE);
- }
- /* EnUU */
- if (sscanf (ptr, " %d/%d %s [EnUU %f]", &seq, &num, name, &ver) == 4)
- {
- thisDecode->sequence = seq;
- thisNumBlocks = num;
- thisDecode->seqConfidence = HIGH;
- strcpy (thisDecode->ident, name);
- thisContentEncoding = CODE_UU;
- return (TRUE);
- }
- /* Wincode v2.x */
- if (sscanf (ptr, " %d/%d file %s [ Wincode v%f ]", &seq, &num, name, &ver) == 4)
- {
- thisDecode->sequence = seq;
- thisNumBlocks = num;
- thisDecode->seqConfidence = HIGH;
- strcpy (thisDecode->ident, name);
- 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, &ver) == 4)
- {
- thisDecode->sequence = seq;
- thisNumBlocks = num;
- thisDecode->seqConfidence = HIGH;
- strcpy (thisDecode->ident, name);
- return (TRUE);
- }
-
- /* UUXFER */
- if (sscanf (copy, "%s section %d/%d UUXFER ver %f", name, &seq, &num, &ver) == 4)
- {
- thisDecode->sequence = seq;
- thisNumBlocks = num;
- thisDecode->seqConfidence = HIGH;
- strcpy (thisDecode->ident, name);
- thisContentEncoding = CODE_UU;
- return (TRUE);
- }
-
- if (sscanf(copy, "archive-name: %[^/]/part%d", name, &seq) == 2) // %[^/] reads a string up to a slash
- {
- strcpy (thisDecode->ident, name);
- thisDecode->sequence = seq;
- if (!thisDecode->seqConfidence)
- thisDecode->seqConfidence = LOW; // still don't have number of parts
- return (TRUE);
- }
-
- return (FALSE);
- }
- //-------------------------------------------------------------------------
- void
- ExecuteDecodedFile(int num, char *fileName)
- {
- char ext[4]; // these are DOS specific...
- // char name[9];
- char association[MAXINTERNALLINE];
- char execute[MAXINTERNALLINE+MAXFILENAME];
- int len;
- unsigned int err;
- char *src, *dest, *src2;
- char mybuf[MAXINTERNALLINE];
-
- #ifndef WIN32
- GetFileExtension (ext, fileName);
-
- if (!_stricmp (ext, "exe"))
- strcpy (execute, fileName);
- else
- // example association: doc=C:\WIN\WINWORD\winword.exe ^.doc
-
- if ((len = GetProfileString ("Extensions", (LPCSTR)ext, "", association, MAXINTERNALLINE)) != 0)
- {
- for (src = association, dest = execute; *src; src++)
- {
- if (*src == '^')
- {
- for (src2 = fileName; *src2 && *src2 != '.'; src2++)
- *(dest++) = *src2;
- }
- else
- *(dest++) = *src;
- }
- *dest = '\0';
- }
- else
- {
- sprintf(mybuf, "Cannot execute - no association for file type %s", ext);
- UpdateThreadStatus (num, mybuf);
- return;
- }
-
- #else
- FindExecutable (fileName, ".", association);
- wsprintf (execute, "%s %s", association, fileName);
- #endif
- if ((err = WinExec (execute, SW_SHOWNORMAL)) < 32)
- {
- sprintf(mybuf, "Error executing %s, error %u", execute, err);
- UpdateThreadStatus (num, mybuf);
- }
- else if (CodingStatusVerbose)
- {
- sprintf(mybuf, "Executed %s", execute);
- UpdateThreadStatus (num, mybuf);
- }
- }
-