windows-nt/Source/XPSP1/NT/sdktools/wcshdr/wcshdr.c
2020-09-26 16:20:57 +08:00

878 lines
23 KiB
C

/* WCSHDR
* generate UNICODE, ANSI & NEUTRAL typedefs and prototypes from a master file
*
* string% = string{W,A,}
* LPTSTR% = {LPWSTR,LPSTR,LPTSTR}
* TCHAR% = {WCHAR,CHAR,TCHAR}
* LPTCH% = {LPWCH,LPCH,LPTCH}
* If whitespace follows the symbol, a space is appended as required to
* prevent shortening, and thus screwwing layout.
*
* History:
* 04-Mar-1991 IanJa Wrote it.
* 19-Mar-1991 IanJa Not all fgets() implementations append '\0' upon EOF.
* 29-Mar-1991 IanJa Workaround NT fgets bug ("\r\n" not collapsed to "\n"),
* & Command line, Usage and Version numbers added.
* 13-May-1991 IanJa All neutrality achieved by #define - no neutral structs
* 14-May-1991 IanJa Minor improvements to version display, help
* 21-May-1991 IanJa Realloc() pbs->pStart when required
* 27-May-1991 GregoryW bug fix, add LPTSTRID, LPTSTRNULL
* 13-Jun-1991 IanJa Convert #define's too. Eg: #define Fn%(a) FnEx%(0, a)
* 19-Jun-1991 IanJa improve #define treatment & simplify main loop
* 12-Aug-1991 IanJa fix multi-line #defines, NEAR & FAR typedefs
* 12-Aug-1991 IanJa fix braceless typedefs with %s; add LPTSTR2
* 13-Aug-1991 IanJa add braceless typedefs #defines
* 21-Aug-1991 IanJa fix string% substitutions for #defines
* 21-Aug-1991 IanJa add BCHAR% -> BYTE or WCHAR as per BodinD request
* 26-Aug-1991 IanJa init pbs->iType (NT-mode bug fix)
* 26-Aug-1991 IanJa workaround NT fgets bug (CR LF not collapsed to NL)
* 17-Nov-1992 v-griffk map #defines to typedef's on structs
* for debugger support
* 08-Sep-1993 IanJa add pLastParen for complex function typedefs such as
* typedef BOOL ( CALLBACK * FOO% ) (BLAH% blah) ;
* 24-Feb-1994 IanJa add CONV_FLUSH for blocks starting #if, #endif etc.
* #if (WINVER > 0x400)
* foo%(void);
* #endif
* 11-Nov-1994 RaymondC propagate ;internal-ness to trailers
*/
char *Version = "WCSHDR v1.20 1994-11-11:";
#include <excpt.h>
#include <ntdef.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define INITIAL_STORE_SIZE 2048
#define EXTRA_STORE_SIZE 1024
#define FN_NAME_SIZE 100
#define CONV_NONE 0
#define CONV_FN_PROTO 1
#define CONV_TYPEDEF 2
#define CONV_DEFINE 3
#define CONV_FLUSH 4
#define ASSERT(pbs, exp) if (!(exp)) AssertFail(__FILE__, __LINE__, pbs, #exp);
typedef int BOOL;
typedef char *PSZ;
typedef struct {
char *pStart; // 1st char in store
char *pLastLine; // 1st char of last line in store
int line; // number of lines read
char *pEnd; /* '\0' at end of store */
size_t cbSize;
size_t cbFree;
int iType; // FnPrototype, Typedef, #define or none
int nParen; // nesting index: ( & { increment; ) & } decrement
char *p1stParen; // Pointer to first '(' or '{' in current block.
char *pLastParen; // Pointer to last '(' or '{' in current block.
char *pSymNam; // copy of function name, null-terminated
int cbSymNam; // bytes available for fn name
char *pszInternal; // "" if external or "\t// ;internal" if internal
} BLOCKSTORE, *PBLOCKSTORE;
void ArgProcess(int argc, PSZ argv[]);
void Usage(void);
void InitBS(PBLOCKSTORE);
void SetInternalnessBS(PBLOCKSTORE);
BOOL ReadLineBS(PBLOCKSTORE);
void WriteBS(PBLOCKSTORE);
void WriteAllTypesBS(PBLOCKSTORE, int);
int ConversionRequiredBS(PBLOCKSTORE);
void GetSymNameBS(PBLOCKSTORE, int);
BOOL WriteRedefinedTypeNamesBS(PBLOCKSTORE);
void WriteConvertBS(PBLOCKSTORE, int, BOOL);
void EmptyBS(PBLOCKSTORE);
DECLSPEC_NORETURN void error_exit(PBLOCKSTORE pbs, int exitval);
void PrintSubstitute(PBLOCKSTORE, PSZ, PSZ, int, BOOL);
void AssertFail(PSZ pszfnam, int lineno, PBLOCKSTORE pbs, PSZ pszExp);
#define NEUT 0
#define ANSI 1
#define UNIC 2
/*
* Command line flags
*/
int fDebug = FALSE;
void
__cdecl main(
int argc,
PSZ argv[])
{
/*
* block store.
* lines from input are saved in here until we know
* enough about how to process them.
*/
BLOCKSTORE bs;
int BlockType;
ArgProcess(argc, argv);
/*
* buffer is empty
*/
InitBS(&bs);
if (fDebug) {
fprintf(stderr, "About to start main loop\n");
}
while (ReadLineBS(&bs)) {
/*
* if line is blank then we have a complete block not requiring
* any conversion.
*/
if (bs.pLastLine[strspn(bs.pLastLine, " \t\r")] == '\n') {
WriteBS(&bs);
EmptyBS(&bs);
continue;
}
if ((BlockType = ConversionRequiredBS(&bs)) != 0) {
WriteAllTypesBS(&bs, BlockType);
}
}
/*
* Flush last BlockStore
*/
WriteBS(&bs);
}
void
WriteAllTypesBS(PBLOCKSTORE pbs, int BlockType)
{
if (fDebug) {
fprintf(stderr, "WriteAllTypes(%p, %d)\n", pbs, BlockType);
}
switch (BlockType) {
case CONV_NONE:
/*
* No conversion required, keep accumulating block.
*/
return;
case CONV_DEFINE:
case CONV_FN_PROTO:
SetInternalnessBS(pbs);
GetSymNameBS(pbs, BlockType);
WriteConvertBS(pbs, ANSI, TRUE);
WriteConvertBS(pbs, UNIC, TRUE);
ASSERT(pbs, pbs->pszInternal);
/*
* UNICODE defn.
*/
fprintf(stdout, "#ifdef UNICODE%s\n#define %s %sW%s\n",
pbs->pszInternal, pbs->pSymNam, pbs->pSymNam, pbs->pszInternal);
/*
* ANSI defn.
*/
fprintf(stdout, "#else%s\n#define %s %sA%s\n",
pbs->pszInternal, pbs->pSymNam, pbs->pSymNam, pbs->pszInternal);
fprintf(stdout, "#endif // !UNICODE%s\n", pbs->pszInternal);
/*
* Neutral defn.
*/
break;
case CONV_TYPEDEF:
SetInternalnessBS(pbs);
WriteConvertBS(pbs, ANSI, FALSE);
WriteConvertBS(pbs, UNIC, FALSE);
WriteRedefinedTypeNamesBS(pbs);
break;
case CONV_FLUSH:
WriteBS(pbs);
EmptyBS(pbs);
break;
default:
fprintf(stderr, "Don't understand block");
error_exit(pbs, 2);
}
EmptyBS(pbs);
}
BOOL
ReadLineBS(PBLOCKSTORE pbs)
{
int cbLine;
if (fDebug) {
fprintf(stderr, "ReadLineBS(%p)\n", pbs);
}
/*
* Not all implementations of fgets() put a '\0' in the buffer upon EOF.
* This will cause ReadLineBS() to leave the BlockStore untouched when
* it returns FALSE.
* We must ensure that BlockStore contents are valid whenever this routine
* is called. InitBS() and EmptyBS() must set contents to '\0' !!
*/
if (fgets(pbs->pEnd, pbs->cbFree, stdin) == NULL) {
return FALSE;
}
cbLine = strlen(pbs->pEnd);
if (fDebug) {
fprintf(stderr, "read %d characters: \"%s\"\n", cbLine, pbs->pEnd);
}
pbs->pLastLine = pbs->pEnd;
pbs->pEnd += cbLine;
pbs->cbFree -= cbLine;
pbs->line++;
if (pbs->cbFree <= 1) {
PSZ p;
p = realloc(pbs->pStart, pbs->cbSize + EXTRA_STORE_SIZE);
/*
* Fatal Errror if allocation failed
*/
ASSERT(pbs, p != NULL);
if (p == NULL) {
fprintf(stderr, "Reallocate BlockStore to %d bytes failed",
pbs->cbSize + EXTRA_STORE_SIZE);
error_exit(pbs, 2);
}
/*
* adjust the pointers and counts
*/
pbs->pLastLine = p + (pbs->pLastLine - pbs->pStart);
pbs->pEnd = p + (pbs->pEnd - pbs->pStart);
pbs->cbSize += EXTRA_STORE_SIZE;
pbs->cbFree += EXTRA_STORE_SIZE;
pbs->pStart = p;
}
return TRUE;
}
void
WriteBS(PBLOCKSTORE pbs)
{
if (fDebug) {
fprintf(stderr, "WriteBS(%p)\n", pbs);
}
fputs(pbs->pStart, stdout);
}
/*
* Each time a new line is read in, this function is called to determine
* whether a complete block has been accumulated for conversion and output.
*/
int
ConversionRequiredBS(PBLOCKSTORE pbs)
{
PSZ p;
if (fDebug) {
fprintf(stderr, "ConversionRequiredBS(%p)\n", pbs);
}
if (pbs->iType == CONV_NONE) {
if (strncmp(pbs->pStart, "#define", 7) == 0) {
/*
* The block starts with #define
*/
pbs->iType = CONV_DEFINE;
} else if (pbs->pStart[0] == '#') {
/*
* The block starts with #if, #else, #endif etc.
*/
return CONV_FLUSH;
}
}
if (pbs->iType != CONV_DEFINE) {
/*
* Scan this line for parentheses and braces to identify
* a complete Function Prototype or Structure definition.
* NOTE: comments containing unbalanced parentheses or braces
* will mess this up!
*/
for (p = pbs->pLastLine; p <= pbs->pEnd; p++) {
if ((*p == '(') || (*p == '{')) {
pbs->pLastParen = p;
if (pbs->p1stParen == NULL) {
pbs->p1stParen = p;
}
pbs->nParen++;
} else if ((*p == ')') || (*p == '}')) {
pbs->nParen--;
}
if ((*p == ';') && (pbs->nParen == 0)) {
/*
* We have a function prototype or a typedef
* (Balanced brackets and a semi-colon)
*/
if (pbs->p1stParen && *(pbs->p1stParen) == '(') {
pbs->iType = CONV_FN_PROTO;
} else {
pbs->iType = CONV_TYPEDEF;
}
goto CheckPercents;
}
}
/*
* Not a #define, nor a complete Typedef or Function prototype.
*/
if (fDebug) {
fprintf(stderr, " CONV_NONE (incomplete fn.proto/typedef)\n");
}
return CONV_NONE;
} else if (pbs->iType == CONV_DEFINE) {
/*
* We know the block is a #define - we must detect the end
* (it can extend for more than one line using backslashes)
*/
if ((p = strrchr(pbs->pStart, '\\')) != NULL) {
/*
* There is a backslash on the line: if is it the last
* non-whitespace character on the line, then this #define
* is continuing on to the next line.
*/
p++;
p += strspn(p, " \t\r\n");
if (*p == '\0') {
/*
* No conversion required *yet*. Continue accumulating
* the multi-line #define statement.
*/
if (fDebug) {
fprintf(stderr, " CONV_NONE (incomplete #define)\n");
}
return CONV_NONE; // ...yet
}
}
}
CheckPercents:
/*
* We have a complete block of known type pbs->iType. We will need
* to convert this block if it contains any %'s, so search for '%'
*/
p = pbs->pStart;
while ((p = strchr(p, '%')) != NULL) {
if (!isalnum(p[1])) {
if (fDebug) {
fprintf(stderr, " return %d (%% found)\n", pbs->iType);
}
return pbs->iType;
}
/*
* We found a %, but it followed by an alphanumeric character,
* so can't require wcshdr.exe substitution. Look for more '%'s
*/
p++;
}
if (fDebug) {
fprintf(stderr, " CONV_FLUSH (no %%'s)\n");
}
return CONV_FLUSH;
}
BOOL
GetDefinedNameBS(PBLOCKSTORE pbs) {
PSZ pPercent = pbs->p1stParen - 1;
PSZ pStartNam;
if (fDebug) {
fprintf(stderr, "GetDefinedNameBS(%p)\n", pbs);
}
/*
* Scan forwards for name (starting from beyond the "#define")
*/
pStartNam = pbs->pStart + 7;
while (isspace(*pStartNam)) {
pStartNam++;
}
/*
* Scan forwards for '%', starting at beginning of literal name
*/
for (pPercent = pStartNam; *pPercent; pPercent++) {
if (*pPercent == '%') {
/*
* Make sure we have enough space to store the literal name
*/
if ((pPercent - pStartNam) > pbs->cbSymNam) {
fprintf(stderr, "REALLOCATE DEFINED NAME BUFFER!");
error_exit(pbs, 2);
}
/*
* store the literal name
*/
*pPercent = '\0';
strcpy(pbs->pSymNam, pStartNam);
*pPercent = '%';
return TRUE;
}
}
/*
* didn't find percent!
*/
fprintf(stderr, "DEFINED NAME ???");
error_exit(pbs, 2);
}
BOOL
GetFnNameBS(PBLOCKSTORE pbs)
{
PSZ pPercent = pbs->pLastParen - 1;
PSZ pStartNam;
if (fDebug) {
fprintf(stderr, "GetFnNameBS(%p)\n", pbs);
}
/*
* Scan backwards for '%'
*/
while (*pPercent != '%') {
if (--pPercent <= pbs->pStart) {
fprintf(stderr, "FUNCTION NAME ???");
error_exit(pbs, 2);
}
}
/*
* Scan back for start of function name
*/
for (pStartNam = pPercent - 1; pStartNam >= pbs->pStart; pStartNam--) {
if (!isalnum(*pStartNam) && *pStartNam != '_')
break;
}
pStartNam++;
/*
* Make sure we have enough space to store the function name
*/
if ((pPercent - pStartNam) > pbs->cbSymNam) {
fprintf(stderr, "REALLOCATE FN NAME BUFFER!");
error_exit(pbs, 2);
}
/*
* store the function name
*/
*pPercent = '\0';
strcpy(pbs->pSymNam, pStartNam);
*pPercent = '%';
return TRUE;
}
void
GetSymNameBS(PBLOCKSTORE pbs, int iType)
{
if (iType == CONV_DEFINE) {
GetDefinedNameBS(pbs);
} else {
GetFnNameBS(pbs);
}
}
BOOL
WriteRedefinedTypeNamesBS(PBLOCKSTORE pbs)
{
PSZ pFirstName = NULL;
PSZ pToken;
PSZ pPercent;
BOOL fSkipFirst;
if (fDebug) {
fprintf(stderr, "WriteRedefinedTypeNamesBS(%p)\n", pbs);
}
ASSERT(pbs, pbs->pszInternal);
if (pbs->p1stParen && (*(pbs->p1stParen) == '{')) {
/*
* Scan backwards for the closing brace
*/
for (pToken = pbs->pEnd; *pToken != '}'; pToken--) {
if (pToken <= pbs->pStart) {
/*
* No closing brace found!?
*/
fprintf(stderr, "CLOSING BRACE ???");
error_exit(pbs, 2);
}
}
pToken++;
fSkipFirst = FALSE;
} else {
/*
* skip past "typedef"
*/
pToken = pbs->pStart + 7;
/*
* Skip the first name
*/
fSkipFirst = TRUE;
}
/*
* UNICODE pass
*/
fprintf(stdout, "#ifdef UNICODE%s\n", pbs->pszInternal);
while (pToken = strtok(pToken, ",; \t*\n\r")) {
if (fDebug) {
fprintf(stderr, "token: \"%s\"\n", pToken);
}
/*
* Write out the #define for UNICODE, excluding "NEAR" & "FAR"
*/
if ( (_stricmp(pToken, "NEAR") == 0)
|| (_stricmp(pToken, "FAR") == 0)) {
goto NextUnicodeToken;
}
if (fSkipFirst) {
fSkipFirst = FALSE;
goto NextUnicodeToken;
} else if (pFirstName == NULL) {
pFirstName = pToken;
}
pPercent = pToken + strlen(pToken) - 1;
if (*pPercent == '%') {
fprintf(stdout, "typedef ");
PrintSubstitute(pbs, pToken, pPercent, UNIC, FALSE);
fputs(" ", stdout);
PrintSubstitute(pbs, pToken, pPercent, NEUT, FALSE);
fprintf(stdout, ";%s\n", pbs->pszInternal);
}
NextUnicodeToken:
pToken = NULL;
}
if (pFirstName == NULL) {
fprintf(stderr, "TYPE NAME ???");
error_exit(pbs, 2);
}
fprintf(stdout, "#else%s\n", pbs->pszInternal);
if (fDebug) {
fprintf(stderr, "FirstName = %s\n", pFirstName);
}
/*
* ANSI pass
*/
pToken = pFirstName;
while ((pToken += strspn(pToken, "%,; \t*\n\r")) < pbs->pEnd) {
/*
* Write out the #define for ANSI, excluding "NEAR" and "FAR"
*/
if ( (_stricmp(pToken, "NEAR") == 0)
|| (_stricmp(pToken, "FAR") == 0)) {
goto NextAnsiToken;
}
pPercent = pToken + strlen(pToken) - 1;
if (*pPercent == '%') {
fprintf(stdout, "typedef ");
PrintSubstitute(pbs, pToken, pPercent, ANSI, FALSE);
fputs(" ", stdout);
PrintSubstitute(pbs, pToken, pPercent, NEUT, FALSE);
fprintf(stdout, ";%s\n", pbs->pszInternal);
}
NextAnsiToken:
while (*pToken++) {
;
}
}
fprintf(stdout, "#endif // UNICODE%s\n", pbs->pszInternal);
return TRUE;
}
void
WriteConvertBS(PBLOCKSTORE pbs, int Type, int fVertAlign)
{
PSZ p = pbs->pStart;
PSZ pPercent;
if (fDebug) {
fprintf(stderr, "WriteConvertBS(%p, %d, %d)\n", pbs, Type, fVertAlign);
}
while ((pPercent = strchr(p, '%')) != NULL) {
if (isalnum(pPercent[1])) {
goto ContinueSearch;
}
/*
* print the substitution
*/
PrintSubstitute(pbs, p, pPercent, Type, fVertAlign);
/*
* Advance beyond the %
*/
ContinueSearch:
p = pPercent+1;
}
/*
* Print remainder of store
*/
fputs(p, stdout);
}
void
EmptyBS(PBLOCKSTORE pbs)
{
if (fDebug) {
fprintf(stderr, "EmptyBS(%p)\n", pbs);
}
pbs->pEnd = pbs->pStart;
pbs->pLastLine = pbs->pStart;
pbs->cbFree = pbs->cbSize;
if (pbs->pStart) {
*(pbs->pStart) = '\0';
}
pbs->iType = CONV_NONE;
pbs->p1stParen = NULL;
pbs->pLastParen = NULL;
pbs->nParen = 0;
if (pbs->pSymNam) {
*(pbs->pSymNam) = '\0';
}
}
void
InitBS(PBLOCKSTORE pbs) {
pbs->line = 0;
pbs->pStart = malloc(INITIAL_STORE_SIZE);
ASSERT(pbs, pbs->pStart != NULL);
pbs->pLastLine = pbs->pStart;
pbs->pEnd = pbs->pStart;
*(pbs->pStart) = '\0';
pbs->iType = CONV_NONE;
pbs->p1stParen = NULL;
pbs->pLastParen = NULL;
pbs->nParen = 0;
pbs->pszInternal = 0;
pbs->cbSize = INITIAL_STORE_SIZE;
pbs->cbFree = INITIAL_STORE_SIZE;
pbs->pSymNam = malloc(FN_NAME_SIZE);
ASSERT(pbs, pbs->pSymNam != NULL);
pbs->cbSymNam = FN_NAME_SIZE;
*(pbs->pSymNam) = '\0';
}
void
SetInternalnessBS(PBLOCKSTORE pbs) {
if (strstr(pbs->pStart, ";internal")) {
pbs->pszInternal = "\t// ;internal";
} else {
pbs->pszInternal = "";
}
}
void
AssertFail(
PSZ pszfnam,
int lineno,
PBLOCKSTORE pbs,
PSZ pszExp)
{
fprintf(stderr, "ASSERT failed: file %s, line %d:\n", pszfnam, lineno);
fprintf(stderr, "input line %d: \"%s\"\n", pbs->line, pszExp);
}
void
ArgProcess(
int argc,
PSZ argv[])
{
int ArgIndex;
PSZ pszArg;
for (ArgIndex = 1; ArgIndex < argc; ArgIndex++) {
pszArg = argv[ArgIndex];
if ((*pszArg == '-') || (*pszArg == '/')) {
switch (pszArg[1]) {
case '?':
fprintf(stderr, "%s\n", Version);
Usage();
exit(0);
case 'd':
case 'D':
fDebug = TRUE;
break;
default:
fprintf(stderr, "%s Invalid switch: %s\n", Version, pszArg);
Usage();
exit(1);
}
}
}
}
void Usage(void)
{
fprintf(stderr, "usage: WCSHDR [-?] display this message\n");
fprintf(stderr, " [-d] debug (to stderr)\n");
fprintf(stderr, " reads stdin, writes to stdout\n");
}
void
DECLSPEC_NORETURN
error_exit(PBLOCKSTORE pbs, int exitval) {
fprintf(stderr, " (line %d)\n", pbs->line);
exit(exitval);
}
/*
* Substitutions performed on strings ending '%'
*
*/
typedef struct {
int cchTemplate;
PSZ pszTemplate;
PSZ apszSub[3];
} SUBSTR, *PSUBSTR;
/*
* Strings that are replaced:
* BCHAR%
* TCHAR%
* LPTCH%
* LPTSTR%
* LPTSTR2%
* LPTSTRID%
* LPTSTRNULL%
* %
*
* "%" MUST comes last (before the null terminator)
*
* The other strings must be ordered from sensibly:
* if FRED% came before BIGFRED% in Substrs[], then the Substitute()
* procedure would match input BIGFRED% to FRED%, not BIGFRED%. The
* simplest way to avoid this is to arrange strings in descending lengths.
*/
SUBSTR Substrs[] = {
{ 10, "LPTSTRNULL%", "LPTSTRNULL", "LPSTRNULL", "LPWSTRNULL" },
{ 8, "LPTSTRID%", "LPTSTRID", "LPSTRID", "LPWSTRID" },
{ 7, "LPTSTR2%", "LPTSTR2", "LPSTR2", "LPWSTR2" },
{ 7, "LPCTSTR%", "LPCTSTR", "LPCSTR", "LPCWSTR" },
{ 6, "LPTSTR%", "LPTSTR", "LPSTR", "LPWSTR" },
{ 5, "TCHAR%", "TCHAR", "CHAR", "WCHAR" },
{ 5, "BCHAR%", "BCHAR", "BYTE", "WCHAR" },
{ 5, "LPTCH%", "LPTCH", "LPCH", "LPWCH" },
{ 0, "%", "", "A", "W" },
{ 0, NULL, NULL, NULL, NULL }
};
PSZ special_pad[] = {
" ", // Neutral
" ", // ANSI
" " // UNICODE
};
PSZ normal_pad[] = {
" ", // Neutral
"", // ANSI
"" // UNICODE
};
void PrintSubstitute(
PBLOCKSTORE pbs, // just for error reporting
PSZ pStart, // where to start substitution
PSZ pPercent, // ptr to '%' at end of input string
int Type, // NEUT, ANSI or UNIC
BOOL fVertAlign) // attempt to maintain vertical alignment?
{
PSUBSTR pSub;
char chTmp;
PSZ pChangedPart = NULL;
if (fDebug) {
fprintf(stderr, "PrintSubstitute(%p, %p, %p, %d, %d)\n",
pbs, pStart, pPercent, Type, fVertAlign);
}
for (pSub = Substrs; pSub->pszTemplate; pSub++) {
int cch = pSub->cchTemplate;
if ((pPercent - cch) < pStart) {
continue;
}
if (strncmp(pPercent - cch, pSub->pszTemplate, cch+1) == 0) {
pChangedPart = pPercent-cch;
/*
* print out unaltered bit
*/
chTmp = *pChangedPart;
*pChangedPart = '\0';
fputs(pStart, stdout);
*pChangedPart = chTmp;
/*
* print out replacement bit
*/
fputs(pSub->apszSub[Type], stdout);
break;
}
}
if (pChangedPart == NULL) {
/*
* NO match was found in Substrs[] !!!
*/
fprintf(stderr, "Can't substitute");
error_exit(pbs, 2);
}
/*
* preserve alignment if required.
* (not for function prototypes, and only if whitespace follows
*/
if (!fVertAlign &&
((pPercent[1] == ' ') || (pPercent[1] == '\t'))) {
if (pChangedPart != pPercent) {
fputs(special_pad[Type], stdout);
} else {
fputs(normal_pad[Type], stdout);
}
}
}