484 lines
13 KiB
C
484 lines
13 KiB
C
/* ************************************************************ *
|
|
*
|
|
* 'Packer.C
|
|
*
|
|
* Packer.C is a tool that packages the structure and data within
|
|
* a given directory into two C header files. Currently this tool
|
|
* is specific for the WorkGroup PostOffice. If there is some
|
|
* interest in making this tool more general, I would be happy
|
|
* to make it so, time permitting.
|
|
*
|
|
* ************************************************************ */
|
|
#include <windows.h>
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#pragma pack(1)
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* Constants and definitions
|
|
*
|
|
* ************************************************************ */
|
|
|
|
#define rgbMaxBuffer 16 * 1024
|
|
|
|
#define crgMaxFile 256
|
|
#define cchMaxFile 64
|
|
|
|
#define szCR "\n"
|
|
|
|
typedef int FO;
|
|
|
|
#define FO_Open 101
|
|
#define FO_Write 102
|
|
#define FO_Close 103
|
|
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* Function prototypes
|
|
*
|
|
* ************************************************************ */
|
|
|
|
int EcTravelDir(char *);
|
|
int EcFileInfo(FO, char *);
|
|
int EcFileData(FO, char *);
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* Global variables
|
|
*
|
|
* ************************************************************ */
|
|
|
|
// Store file info
|
|
char rgszFile[crgMaxFile][cchMaxFile];
|
|
int rgFileState[crgMaxFile];
|
|
|
|
// Store file data
|
|
unsigned char rgbBuffer[rgbMaxBuffer];
|
|
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* void 'Main
|
|
*
|
|
* In: int cArg Count of arguments
|
|
* In: char *rgszArg[] Argument string array
|
|
*
|
|
* Main returns void.
|
|
*
|
|
* Main checks if the command-line arguments are valid.
|
|
* If so, it calls EcTravelDir to pack the given directory.
|
|
*
|
|
* Main causes no side-effects.
|
|
*
|
|
* Main indicates any error with a message to stdout.
|
|
*
|
|
* ************************************************************ */
|
|
|
|
void
|
|
__cdecl
|
|
main(
|
|
int cArg,
|
|
char *rgszArg[]
|
|
)
|
|
{
|
|
int ec = 0;
|
|
|
|
// Check count of command-line arguments
|
|
if (cArg != 2) {
|
|
printf("USAGE: packer [directory]\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (EcTravelDir(rgszArg[1]) != 0)
|
|
exit(EXIT_FAILURE);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* int 'EcTravelDir
|
|
*
|
|
* In: char *szTravelDir Directory to pack
|
|
*
|
|
* EcTravelDir returns a standard exit code.
|
|
*
|
|
* EcTravelDir packages the directory structure and all files
|
|
* within the given directory into two C header files. The
|
|
* directory tree is travelled in a current node, "low" branch,
|
|
* "high" branch pattern. This means the files and sub-directories
|
|
* of the current directory are enumerated and saved, then the
|
|
* sub-directories are traversed from the lowest to the highest
|
|
* as they appear in the directory list. Directory structure
|
|
* info is handled by EcFileInfo while file data is handled by
|
|
* EcFileData.
|
|
*
|
|
* EcTravelDir causes a disk side-effect.
|
|
*
|
|
* EcTravelDir indicates any error via the exit code.
|
|
*
|
|
* ************************************************************ */
|
|
|
|
int
|
|
EcTravelDir(
|
|
char *szTravelDir
|
|
)
|
|
{
|
|
int ec = 0;
|
|
|
|
int iFile = 0;
|
|
int iFileParent;
|
|
|
|
char szFile[cchMaxFile] = "";
|
|
char szDir[cchMaxFile] = "";
|
|
|
|
char szDrive[_MAX_DRIVE] = "";
|
|
char szPath[_MAX_DIR] = "";
|
|
char szFileName[_MAX_FNAME] = "";
|
|
char szFileExt[_MAX_EXT] = "";
|
|
|
|
int ixType;
|
|
long lcbxFile;
|
|
char szxFile[cchMaxFile];
|
|
|
|
unsigned short us;
|
|
HANDLE hdir;
|
|
WIN32_FIND_DATA findbuf;
|
|
|
|
_splitpath(szTravelDir, szDrive, szPath, szFileName, szFileExt);
|
|
_makepath(szDir, "", szPath, szFileName, szFileExt);
|
|
if (szDir[strlen(szDir)-1] != '\\')
|
|
strcat(szDir, "\\");
|
|
|
|
// *** Open header files ***
|
|
ec = EcFileInfo(FO_Open, "_fileinf.h");
|
|
if (ec != 0)
|
|
goto RET;
|
|
|
|
ec = EcFileData(FO_Open, "_filedat.h");
|
|
if (ec != 0)
|
|
goto RET;
|
|
|
|
// *** Traverse directory tree ***
|
|
|
|
// Initial directory
|
|
strcpy(rgszFile[0], "");
|
|
rgFileState[0] = '\0';
|
|
|
|
while (1) {
|
|
if (rgFileState[iFile] == 0) {
|
|
// Keep track of parent directory
|
|
rgFileState[iFile] = 1;
|
|
iFileParent = iFile;
|
|
|
|
// Prepare szFile and hdir
|
|
strcpy(szFile, szDrive);
|
|
strcat(szFile, szDir);
|
|
strcat(szFile, rgszFile[iFile]);
|
|
strcat(szFile, "*.*");
|
|
|
|
// Skip . entry
|
|
hdir = FindFirstFile(szFile, &findbuf);
|
|
if (hdir == INVALID_HANDLE_VALUE) {
|
|
ec = 1;
|
|
goto CLOSE;
|
|
}
|
|
|
|
// Skip .. entry
|
|
//FindNextFile(hdir, &findbuf);
|
|
|
|
// *** Enumerate current directory ***
|
|
|
|
while (1) {
|
|
if(!FindNextFile(hdir, &findbuf) &&
|
|
GetLastError() == ERROR_NO_MORE_FILES)
|
|
break;
|
|
|
|
//
|
|
// Skip over any file that begins with '.'.
|
|
//
|
|
if (findbuf.cFileName[0] == '.')
|
|
continue;
|
|
|
|
//
|
|
// Skip over hidden and system files.
|
|
//
|
|
if (findbuf.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM))
|
|
continue;
|
|
|
|
// *** Set file info data ***
|
|
|
|
if (findbuf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
iFile += 1;
|
|
strcpy(rgszFile[iFile], rgszFile[iFileParent]);
|
|
strcat(rgszFile[iFile], findbuf.cFileName);
|
|
strcat(rgszFile[iFile], "\\");
|
|
rgFileState[iFile] = 0;
|
|
ixType = 0;
|
|
lcbxFile = 0;
|
|
strcpy(szxFile, rgszFile[iFile]);
|
|
} else {
|
|
ixType = 1;
|
|
lcbxFile = findbuf.nFileSizeLow;
|
|
strcpy(szxFile, rgszFile[iFileParent]);
|
|
strcat(szxFile, findbuf.cFileName);
|
|
}
|
|
|
|
// *** Write file info ***
|
|
sprintf(szFile, "%d, %4ld, \"%s\"," szCR,
|
|
ixType, lcbxFile, szxFile);
|
|
ec = EcFileInfo(FO_Write, szFile);
|
|
if (ec != 0)
|
|
goto CLOSE;
|
|
|
|
if (lcbxFile == 0)
|
|
continue;
|
|
|
|
// *** Write file data ***
|
|
strcpy(szFile, szDrive);
|
|
strcat(szFile, szDir);
|
|
strcat(szFile, szxFile);
|
|
ec = EcFileData(FO_Write, szFile);
|
|
if (ec != 0)
|
|
goto CLOSE;
|
|
}
|
|
} else {
|
|
iFile -= 1;
|
|
}
|
|
|
|
if (iFile == 0)
|
|
break;
|
|
}
|
|
|
|
CLOSE:
|
|
EcFileInfo(FO_Close, "");
|
|
EcFileData(FO_Close, "");
|
|
|
|
RET:
|
|
if (ec != 0) {
|
|
printf("ERROR: Disk error %d\n", ec);
|
|
remove("_fileinf.h");
|
|
remove("_filedat.h");
|
|
}
|
|
|
|
return ec;
|
|
|
|
}
|
|
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* int 'EcFileInfo
|
|
*
|
|
* In: FO foType File operation
|
|
* In: char *szFileInfo File name or data string
|
|
*
|
|
* EcFileInfo returns a standard exit code.
|
|
*
|
|
* EcFileInfo opens, writes, and closes the directory structure
|
|
* header file. The info is saved in an array of type HDR.
|
|
* The array is rghdrFile and the constant chdrFile counts the
|
|
* number of elements in the array.
|
|
*
|
|
* EcFileInfo causes a disk side-effect.
|
|
*
|
|
* EcFileInfo indicates any error via the exit code.
|
|
*
|
|
* ************************************************************ */
|
|
|
|
int
|
|
EcFileInfo(
|
|
FO foType,
|
|
char *szFileInfo
|
|
)
|
|
{
|
|
int ec = 0, dos;
|
|
char szT[cchMaxFile] = "";
|
|
int iszT;
|
|
|
|
static FILE *hfFileInfo;
|
|
static int chdrFileInfo;
|
|
|
|
unsigned iszFileInfo;
|
|
|
|
switch (foType) {
|
|
case FO_Open:
|
|
// Open szFileInfo file
|
|
hfFileInfo = fopen(szFileInfo, "wt");
|
|
if (hfFileInfo == NULL) {
|
|
printf("ERROR: Can't open %s\n", szFileInfo);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Open rghdrFile declaration and set chdFileInfo to 0
|
|
fprintf(hfFileInfo, szCR);
|
|
fprintf(hfFileInfo, "static CSRG(HDR) rghdrFile[] =" szCR);
|
|
fprintf(hfFileInfo, "{" szCR);
|
|
chdrFileInfo = 0;
|
|
|
|
break;
|
|
|
|
case FO_Write:
|
|
// Double every backlash in szFileInfo
|
|
for (iszFileInfo = 0, iszT = 0; iszFileInfo < strlen(szFileInfo);
|
|
iszFileInfo += 1)
|
|
{
|
|
szT[iszT++] = szFileInfo[iszFileInfo];
|
|
if (szFileInfo[iszFileInfo] == '\\') szT[iszT++] = '\\';
|
|
}
|
|
szT[iszT] = '\0';
|
|
|
|
// Write file info
|
|
fwrite(szT, sizeof(char), iszT, hfFileInfo);
|
|
chdrFileInfo += 1;
|
|
break;
|
|
|
|
case FO_Close:
|
|
// Close rghdrFile definition and set chdrFile
|
|
fprintf(hfFileInfo, szCR);
|
|
fprintf(hfFileInfo, "// END" szCR);
|
|
fprintf(hfFileInfo, szCR);
|
|
fprintf(hfFileInfo, "0, 0, \"\"" szCR);
|
|
fprintf(hfFileInfo, szCR);
|
|
fprintf(hfFileInfo, "}; // rghdrFile" szCR);
|
|
fprintf(hfFileInfo, szCR);
|
|
fprintf(hfFileInfo, "#define chdrFile %d" szCR, chdrFileInfo);
|
|
fprintf(hfFileInfo, szCR);
|
|
|
|
// Close szFileInfo
|
|
dos = fclose(hfFileInfo);
|
|
assert(dos == 0);
|
|
break;
|
|
|
|
default:
|
|
printf("ERROR: Unknown EcFileInfo foType %d\n", foType);
|
|
break;
|
|
|
|
}
|
|
return ec;
|
|
}
|
|
|
|
|
|
/* ************************************************************ *
|
|
*
|
|
* int 'EcFileData
|
|
*
|
|
* In: FO foType File operation
|
|
* In: char *szFileData File name or data string
|
|
*
|
|
* EcFileData returns a standard exit code.
|
|
*
|
|
* EcFileData opens, writes, and closes the file data header
|
|
* file. The file data is saved in an array of char (bytes).
|
|
* The array is named rgbFile and the constant cbFile counts
|
|
* the number of elements in the array.
|
|
*
|
|
* EcFileData causes a disk side-effect.
|
|
*
|
|
* EcFileData indicates any error via the exit code.
|
|
*
|
|
* ************************************************************ */
|
|
|
|
int
|
|
EcFileData(
|
|
FO foType,
|
|
char *szFileData
|
|
)
|
|
{
|
|
int ec = 0, dos;
|
|
int fContinue;
|
|
|
|
static FILE *hfFileData;
|
|
static int cbFileData;
|
|
|
|
FILE *hfSourceFile;
|
|
long lcbRead;
|
|
|
|
long ibBuffer, ibOffset, cbOffset;
|
|
|
|
switch (foType) {
|
|
case FO_Open:
|
|
// Open szFileData file
|
|
hfFileData = fopen(szFileData, "wt");
|
|
if (hfFileData == NULL) {
|
|
printf("ERROR: Can't open %s\n", szFileData);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Write rgbFile declaration and set cbFiledata to 0
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "static CSRG(unsigned char) rgbFile[] =" szCR);
|
|
fprintf(hfFileData, "{" szCR);
|
|
cbFileData = 0;
|
|
break;
|
|
|
|
case FO_Write:
|
|
// Open source file
|
|
hfSourceFile = fopen(szFileData, "rb");
|
|
if (!hfSourceFile) {
|
|
break;
|
|
}
|
|
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "// %s" szCR, szFileData);
|
|
fprintf(hfFileData, szCR);
|
|
|
|
// Write file data
|
|
fContinue = 1;
|
|
while (fContinue == 1) {
|
|
// Read source file
|
|
lcbRead = fread(rgbBuffer, sizeof(char),
|
|
rgbMaxBuffer, hfSourceFile);
|
|
assert(ferror(hfSourceFile) == 0);
|
|
|
|
if (feof(hfSourceFile) != 0) fContinue = 0;
|
|
|
|
// Write target file
|
|
for (ibBuffer = 0; ibBuffer < lcbRead; ibBuffer += 16) {
|
|
cbOffset = min((long) 16, lcbRead-ibBuffer);
|
|
for (ibOffset = 0; ibOffset < cbOffset; ibOffset += 1) {
|
|
fprintf(hfFileData, "%3u, ",
|
|
rgbBuffer[ibBuffer+ibOffset]);
|
|
cbFileData += 1;
|
|
}
|
|
fprintf(hfFileData, szCR);
|
|
}
|
|
}
|
|
|
|
// Close source file
|
|
dos = fclose(hfSourceFile);
|
|
assert(dos == 0);
|
|
break;
|
|
|
|
case FO_Close:
|
|
// Close rgbFile definition and set cbFile
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "// END" szCR);
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "000" szCR);
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "}; // rgbFile" szCR);
|
|
fprintf(hfFileData, szCR);
|
|
fprintf(hfFileData, "#define cbFile %d" szCR, cbFileData);
|
|
fprintf(hfFileData, szCR);
|
|
|
|
// Close szFileData
|
|
dos = fclose(hfFileData);
|
|
assert(dos == 0);
|
|
break;
|
|
|
|
default:
|
|
printf("ERROR: Unknown EcFileData foType %d\n", foType);
|
|
break;
|
|
|
|
}
|
|
return ec;
|
|
}
|