#ifdef DOCUMENTATION
spp is a preprocessor for C code that allows you to take
string constants in your source and convert them into
calls that load the string constant from a resource. You provide
the GetStringResource() call, and change your makefile a bit.
spp generates three files:
filename.sc - Contains the new C code with strings replaced.
filename.src - Contains the STRINGTABLE of string constants.
filename.sh - Contains the generated int constants for the strings.
The options are described below. There is one undocumented
feature, the "escape". If you place the comment /*!*/ in front
of any string, spp will leave it alone.
Makefile entries change from a C compiler call to something like:
file.obj: file.c
spp -b 5000 -a inert -o src\file.c
$(CC) /Tc inert\file.sc
This invokes the spp preprocessor with the source as input, and
places the results in a subdirectory called inert. This keeps
"generated" files away from your source. The /Tc option in the
C compiler command points C to the generated source file.
Also, you will need to make the .src file depend on the .obj
file generated, and the .res file depend on the .src file.
file.res: file.rc inert\file.src
inert\file.src: file.obj
file.obj: file.c
or you might try:
file.res: file.rc inert\src
file.obj: inert\file.src
inert\file.src: file.c
Then, you also need to make sure you #include "file.src" in
your .rc file to include the strings, and #include "file.sh"
to get the generated string ids.
Example GetStringResource function:
GetStringResource(id)
int id;
{
static char gsr_buffer[256];
if (LoadString(gInstance, id, (LPSTR)gsr_buffer, 255) < 1)
strcpy(gsr_buffer, "*** LoadString() ERROR");
return gsr_buffer;
}
#endif
/*
** This code was written by Tim Endres on August 14, 1991.
**
** This code will read C source and convert all string constants
** into function calls to a routine designed to load the string
** constant from a resource. I wrote this primarily for Windows
** development because I have an application with over 50K bytes
** of string constants, and I could not fit this in the 64K data
** segment.
**
** I have donated this code to USENET, and poor miserable DOS
** and Windows developers everywhere dealing with a horrifying
** CPU model (oh, I *know* it has some advantages, but not for
** Windows in my opinion).
**
** Please do as you wish with this code, but please be kind
** enough to leave a little credit here for me someplace. :^)
**
** This, and all of my code is dedicated to Sky Marie.
** Daddy misses you Sky Marie!
**
*/
struct {
char *str;
} usage_tbl [] = {
"OPTIONS:",
" -a <directory>",
" Sets ALL output file directories.",
" -d <directory>",
" Sets the header output file directory.",
" -s <directory>",
" Sets the source output file directory.",
" -r <directory>",
" Sets the resource script output file directory.",
" -i <rsrc_id_increment>",
" Sets the resource ID increment.",
" -b <rsrc_id_base>",
" Sets the resource ID starting value.",
" -v",
" Turns on verbose output to stdout.",
" -o",
" Sets the resource script output file directory.",
" -w",
" Turn off warning messages (produced on stderr).",
" -1 <suffix>",
" Sets the resource output file suffix (default .src).",
" -2 <suffix>",
" Sets the source output file suffix (default .sc).",
" -3 <suffix>",
" Sets the header output file suffix (default .sh).",
" --",
" Sends the source output to stdio.",
(char *) 0
};
#include <dos.h>
#include <stdio.h>
#include <string.h>
extern char *malloc();
struct str_cache {
struct str_cache *next;
int rsrc_id;
char *str;
};
struct str_cache *cache_list = NULL;
char progname[256];
int verbose = 0,
optimize = 0,
warning = 1,
stringing = 0,
commenting = 0,
quoting = 0,
pass_string = 0,
rsrc_id = 1000,
rsrc_id_inc = 1;
long line_num = 0;
char def_id_name[64],
filename[64],
rcname[64],
defname[64],
outname[64],
src_path[256] = ".",
res_path[256] = ".",
def_path[256] = ".",
src_suffix[8] = ".sc",
res_suffix[8] = ".src",
def_suffix[8] = ".sh",
path[256],
inbuf[8192],
strbuf[8192];
FILE *infile,
*outfile,
*rcfile,
*deffile;
usage()
{
int i;
printf("USAGE: %s [options] file_spec\n", progname);
for (i=0; usage_tbl[i].str != NULL; i++)
printf("%s\n", usage_tbl[i].str);
}
main(argc, argv)
int argc;
char *argv[];
{
int myerr;
char *ptr;
struct find_t myfind;
ptr = strrchr(argv[0], '\\');
strcpy(progname, (ptr == NULL) ? argv[0] : ptr + 1);
ptr = strchr(progname, '.');
if (ptr != NULL)
*ptr = '\0';
argc--; argv++;
outfile = NULL;
while (argv[0][0] == '-') {
switch (argv[0][1]) {
case 'a':
strcpy(def_path, argv[1]);
strcpy(src_path, argv[1]);
strcpy(res_path, argv[1]);
argc--; argv++;
break;
case 'd':
strcpy(def_path, argv[1]);
argc--; argv++;
break;
case 's':
strcpy(src_path, argv[1]);
argc--; argv++;
break;
case 'r':
strcpy(res_path, argv[1]);
argc--; argv++;
break;
case 'i':
rsrc_id_inc = atoi(argv[1]);
argc--; argv++;
break;
case 'b':
rsrc_id = atoi(argv[1]);
argc--; argv++;
break;
case 'v':
verbose = 1;
break;
case 'o':
optimize = 1;
break;
case 'w':
warning = 0;
break;
case '1':
strcpy(res_suffix, argv[1]);
argc--; argv++;
break;
case '2':
strcpy(src_suffix, argv[1]);
argc--; argv++;
break;
case '3':
strcpy(def_suffix, argv[1]);
argc--; argv++;
break;
case '-':
outfile = stdout;
break;
case '?':
usage();
exit(1);
break;
default:
fprintf(stderr, "ERROR: invalid option -%c\n", argv[0][1]);
usage();
exit(1);
break;
}
argc--; argv++;
}
ptr = strrchr(argv[0], '\\');
if (ptr != NULL) {
*ptr = '\0';
strcpy(path, argv[0]);
*ptr = '\\';
}
else
strcpy(path, ".");
if (src_path[0] == '\0')
strcpy(src_path, path);
if (res_path[0] == '\0')
strcpy(res_path, path);
if (def_path[0] == '\0')
strcpy(def_path, path);
myerr = _dos_findfirst(argv[0], _A_NORMAL, &myfind);
if (myerr != 0)
fprintf(stderr, "File not found - '%s'\n", argv[0]);
for ( ; myerr == 0 ; ) {
if (myfind.attrib == _A_SUBDIR) {
if (verbose)
printf("Skipping directory %s...\n", myfind.name);
}
else {
if (verbose)
printf("Converting %s\\%s...\n", path, myfind.name);
strcpy(filename, myfind.name);
do_string_to_rsrc();
}
myerr = _dos_findnext(&myfind);
}
if (myerr != 18 && myerr != -1) // *** -1 seems OK too ***
printf("Error #%d getting next file.\n", myerr);
else
myerr = 0;
return myerr;
}
buildpath(fullname, path, name)
char *fullname;
char *path;
char *name;
{
sprintf(fullname, "%s\\%s", path, name);
}
do_string_to_rsrc()
{
char fullname[256];
char *ptr;
strcpy(rcname, filename);
ptr = strrchr(rcname, '.');
if (ptr != NULL)
strcpy(ptr, res_suffix);
else
strcat(rcname, res_suffix);
strcpy(defname, filename);
ptr = strrchr(defname, '.');
if (ptr != NULL)
strcpy(ptr, def_suffix);
else
strcat(defname, def_suffix);
strcpy(outname, filename);
ptr = strrchr(outname, '.');
if (ptr != NULL)
strcpy(ptr, src_suffix);
else
strcat(defname, src_suffix);
buildpath(fullname, path, filename);
infile = fopen(fullname, "r");
buildpath(fullname, res_path, rcname);
rcfile = fopen(fullname, "w");
buildpath(fullname, def_path, defname);
deffile = fopen(fullname, "w");
if (outfile == NULL) {
buildpath(fullname, src_path, outname);
outfile = fopen(fullname, "w");
}
if (rcfile == NULL || deffile == NULL || infile == NULL || outfile == NULL) {
if (rcfile != NULL) fclose(rcfile);
if (deffile != NULL) fclose(deffile);
if (infile != NULL) fclose(infile);
if (outfile != NULL) fclose(outfile);
}
else {
process_strings_to_rsrc();
fclose(rcfile);
fclose(deffile);
fclose(infile);
fclose(outfile);
}
}
process_strings_to_rsrc()
{
char *ptr,
*strptr;
strcpy(def_id_name, filename);
ptr = strchr(def_id_name, '.');
if (ptr != NULL)
*ptr = '_';
fprintf(rcfile, "\n\nSTRINGTABLE\nBEGIN\n\n");
fprintf(outfile, "\n/* Include %s generated header file...*/\n", progname);
fprintf(outfile, "#include \"%s\"\n\n", defname);
fprintf(outfile, "extern char *GetStringResource(int);\n\n");
fprintf(outfile, "extern char *_gStr_emptyStr;\n");
fprintf(outfile, "extern char *_gStr_dotStr;\n\n");
for ( ; ; ) {
if (fgets(inbuf, 8192, infile) == NULL)
break;
line_num++;
if (inbuf[0] == '#') {
if (verbose)
printf("[%ld] Skip compiler directive <%s>\n",
line_num, inbuf);
fprintf(outfile, "%s", inbuf);
continue;
}
strptr = strbuf;
for (ptr = inbuf; *ptr; ptr++) {
if (commenting) {
fputc(*ptr, outfile);
if (*ptr == '*' && *(ptr + 1) == '/') {
commenting = 0;
fputc('/', outfile);
ptr++;
}
}
else if (quoting) {
fputc(*ptr, outfile);
if (*ptr == '\\' && *(ptr + 1) == '\'') {
fputc('\'', outfile);
ptr++;
}
else if (*ptr == '\\' && *(ptr + 1) == '\\') {
fputc('\\', outfile);
ptr++;
}
else if (*ptr == '\n') {
if (warning)
fprintf(stderr, "[%d] WARNING New line in quote constant.\n", line_num);
quoting = 0;
}
else if (*ptr == '\'')
quoting = 0;
}
else if (*ptr == '"') {
if (pass_string) {
fputc('"', outfile);
if (pass_string > 0)
pass_string = -1;
else
pass_string = 0;
}
else if (stringing) {
stringing = 0;
*strptr = '\0';
do_string_entry();
}
else {
stringing = 1;
strptr = strbuf;
}
}
else if (*ptr == '\'' && (! (stringing || pass_string))) {
fputc(*ptr, outfile);
quoting = 1;
}
else if ( *ptr == '/' && *(ptr + 1) == '*' &&
(! (stringing || pass_string)) )
{
if (*(ptr + 2) == '!' && *(ptr + 3) == '*' && *(ptr + 4) == '/') {
pass_string = 1;
fprintf(outfile, "/*!*/");
ptr += 4;
}
else {
fputc('/', outfile); fputc('*', outfile);
ptr++;
commenting = 1;
}
}
else if (*ptr == '\\' && (stringing || pass_string)) {
if (*(ptr + 1) == '"') {
ptr++;
if (pass_string) {
fputc('\\', outfile);
fputc('"', outfile);
}
else {
*strptr++ = '\\';
*strptr++ = '"';
}
}
else if (*(ptr + 1) == '\\') {
ptr++;
if (pass_string) {
fputc('\\', outfile);
fputc('\\', outfile);
}
else {
*strptr++ = '\\';
*strptr++ = '\\';
}
}
else {
if (pass_string)
fputc(*ptr, outfile);
else
*strptr++ = *ptr;
}
}
else if (*ptr == '\n' && (stringing || pass_string)) {
if (warning)
fprintf(stderr, "[%d] WARNING New line in string constant.\n", line_num);
}
else {
if (! stringing)
fputc(*ptr, outfile);
else
*strptr++ = *ptr;
}
}
stringing = 0;
quoting = 0;
}
fprintf(rcfile, "\nEND\n");
}
do_string_entry()
{
int used_id;
if (strlen(strbuf) > 254) {
if (verbose)
printf("[%ld] SKIP string longer than 254 <%s>\n",
line_num, strbuf);
fprintf(outfile, /*!*/"\"%s\"", strbuf);
}
else {
if (optimize && strbuf[0] == '\0') {
if (verbose)
printf("[%ld] OPT Empty String\n", line_num);
fprintf(outfile, "_gStr_emptyStr");
}
else if (optimize && strcmp(strbuf, ".") == 0) {
if (verbose)
printf("[%ld] OPT Dot String\n", line_num);
fprintf(outfile, "_gStr_dotStr");
}
else {
if (optimize)
used_id = check_string(strbuf);
else
used_id = -1;
if (verbose)
printf("[%ld] RSRC %sID #%d <%s>\n",
line_num, ((used_id != -1) ? "CACHED " : ""),
((used_id != -1) ? used_id : rsrc_id), strbuf);
if (used_id != -1) {
fprintf(outfile, "GetStringResource(IDS_%s_%d)",
def_id_name, used_id);
/* NO rc file entry... Its already there!!! */
/* DITTO for the define file entry! */
}
else {
fprintf(outfile, "GetStringResource(IDS_%s_%d)",
def_id_name, rsrc_id);
fprintf(rcfile, "\tIDS_%s_%d, \"%.254s\"\n",
def_id_name, rsrc_id, strbuf);
fprintf(deffile, "#define IDS_%s_%d\t\t%d\n",
def_id_name, rsrc_id, rsrc_id);
rsrc_id += rsrc_id_inc;
}
}
}
}
check_string(string)
char *string;
{
struct str_cache *cache_ptr;
for (cache_ptr=cache_list; cache_ptr != NULL; cache_ptr=cache_ptr->next) {
if (strcmp(cache_ptr->str, string) == 0) {
if (verbose)
printf("[%ld] HIT CACHE <%s> %d\n",
line_num, cache_ptr->str, cache_ptr->rsrc_id);
return cache_ptr->rsrc_id;
}
}
add_new_string(string, rsrc_id);
return -1;
}
add_new_string(string, rsrc_id)
char *string;
int rsrc_id;
{
struct str_cache *cache_ptr;
cache_ptr = (struct str_cache *) malloc(sizeof(struct str_cache));
if (cache_ptr == NULL)
return 0;
cache_ptr->str = malloc(strlen(string) + 1);
if (cache_ptr->str == NULL) {
free(cache_ptr);
return 0;
}
strcpy(cache_ptr->str, string);
cache_ptr->rsrc_id = rsrc_id;
cache_ptr->next = cache_list;
cache_list = cache_ptr;
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1