/**************************** x390tab.c *******************************/ /***** *****/ /***** This program was originally written by David Bond. It has *****/ /***** been placed into the public domain and thus may be freely *****/ /***** modified and redistributed with no restrictions whatsoever.*****/ /***** *****/ /***** THERE IS NO WARRENTY FOR THIS PROGRAM, EITHER EXPRESS *****/ /***** OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *****/ /***** WARRENTIES OF MERCHANTABILITY AND FITNESS FOR A *****/ /***** PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY *****/ /***** AND PERFORMANCE OF THS PROGRAM IS WITH YOU. SHOULD THE *****/ /***** PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL *****/ /***** NECESSARY SERVICING, REPAIR OR CORRECTION. *****/ /***** *****/ /**********************************************************************/ /**********************************************************************/ /* */ /* This program should compile cleanly using most available ANSI C */ /* compilers. It has been compiled using several different UNIX */ /* compilers as well as IBM's C Set++ and Visual Age C++ for OS/2 */ /* and Microsoft's Visual C++. */ /* */ /* This program uses the "access" function, which is part of UNIX */ /* and is implemented by many non-UNIX compiler libraries. If */ /* your compiler does not support the "access" function, define */ /* the NOACCESS macro. */ /* */ /* Two other macros may need to be defined: */ /* UNIX - if the program is being compiled for any UNIX flavor. */ /* MSDOS - if the program is targeted for the MS-DOS or 16-bit */ /* OS/2 or Windows environments. It should not be */ /* defined for 32-bit OS/2 or Windows. */ /* */ /**********************************************************************/ #if defined(__IBMC__) #pragma strings(readonly) #endif #include #include #include #include #include #ifdef UNIX #define DIR_SEP '/' #define EOL_DEFAULT "\n" #include #else #define DIR_SEP '\\' #define EOL_DEFAULT "\r\n" #ifndef NOACCESS #include #ifndef R_OK #define R_OK 04 #define W_OK 02 #endif #endif #endif /* Options */ static unsigned uTestMode = 0; /* 0 or 1 */ static unsigned uTabIn = 8; /* 4 or 8 */ static unsigned uTabOut = 8; /* 4 or 8 */ static unsigned uCompress = 1; /* 0 or 1 */ static unsigned uLineMax = 999; /* 1 - 999 */ static char szEol[3] = EOL_DEFAULT; /* "\n" or "\r\n" */ static char cRename = '\0'; /* 'U', 'L', or '\0' */ /* Stuff derived from the options. */ static unsigned uEolSize = 0; static unsigned uTabInMask = 0; static unsigned uTabOutMask = 0; static unsigned uMaxName = 4; /* Statistics */ static unsigned uTotalFiles = 0; static unsigned long ulTotalLines = 0; static unsigned long ulTotalBytesIn = 0; static unsigned long ulTotalBytesOut = 0; /* Current file */ static unsigned uLineLength = 0; static unsigned long ulFileLines = 0; static unsigned long ulFileBytesIn = 0; static unsigned long ulFileBytesOut = 0; static FILE * pFileIn = NULL; static FILE * pFileOut = NULL; static const char * pszFileIn = NULL; static char szFileOut[FILENAME_MAX]; static char acLine[1001]; /************************************************************************/ /* */ /* iInitialize: handle the command line parameters */ /* */ /* passed: command line argument count and parameters. */ /* returns: 0 (OK) or 1 (error) */ /* */ /************************************************************************/ static int iInitialize( int argc, const char * const argv[]) { int iError = 0; int arg; const char * pszArg; static const char szUsage[] = "Usage: x390tab [options] files\n\n" "options: [-c | -x ] compresses or expands the files using tab characters.\n" " [-d | -u ] sets the end-of-line to DOS or UNIX standards.\n" " [-i4 | -i8] sets the input tab stops to every 4 or 8 characters.\n" " [-o4 | -o8] sets the output tab stops to every 4 or 8 characters.\n" #ifndef MSDOS " [-rl | -ru] renames the files to lower or upper case names.\n" #endif " [-lnnn] sets the maximum line length to nnn (0-999).\n" " [-t] sets test-only mode.\n" #ifdef UNIX " default: -c -u -i8 -o8 -l999\n" "files: the files to be compressed or expanded.\n"; #else " default: -c -d -i8 -o8 -l999\n" "files: the files to be compressed or expanded (wild cards allowed).\n"; #endif for (arg = 1; arg < argc; ++arg) { pszArg = argv[arg]; if (pszArg[0] != '-') { unsigned uLength; #ifndef NOACCESS if (access(pszArg, R_OK | W_OK) != 0) { perror(pszArg); return 1; } #endif uLength = strlen(pszArg); if (uMaxName < uLength) uMaxName = uLength; ++uTotalFiles; continue; } switch (toupper(pszArg[1])) { case 'C': if (pszArg[2] != '\0') iError = 1; else uCompress = 1; break; case 'D': if (pszArg[2] != '\0') iError = 1; else memcpy(szEol, "\r\n", 3); break; case 'H': fputs(szUsage, stderr); return 1; case 'I': if (((pszArg[2] != '4') && (pszArg[2] != '8')) || (pszArg[3] != '\0')) iError = 1; else uTabIn = pszArg[2] - '0'; break; case 'L': if ((pszArg[2] == '\0') || (strlen(pszArg) > 5)) iError = 1; else { uLineMax = 0; for (pszArg += 2; *pszArg != '\0'; ++pszArg) if (!isdigit(*pszArg)) { iError = 1; break; } else uLineMax = uLineMax * 10 + (*pszArg - '0'); pszArg = argv[arg]; } break; case 'O': if (((pszArg[2] != '4') && (pszArg[2] != '8')) || (pszArg[3] != '\0')) iError = 1; else uTabOut = pszArg[2] - '0'; break; #ifndef MSDOS case 'R': if (((toupper(pszArg[2]) != 'L') && (toupper(pszArg[2]) != 'U')) || (pszArg[3] != '\0')) iError = 1; else cRename = toupper(pszArg[2]); break; #endif case 'T': if (pszArg[2] != '\0') iError = 1; else uTestMode = 1; break; case 'U': if (pszArg[2] != '\0') iError = 1; else memcpy(szEol, "\n", 2); break; case 'X': if (pszArg[2] != '\0') iError = 1; else uCompress = 0; break; default: iError = 1; } if (iError) { fprintf(stderr, "\"%s\" is not a valid command line option!\n", pszArg); break; } } uEolSize = strlen(szEol); uTabInMask = 4096 - uTabIn; uTabOutMask = 4096 - uTabOut; if (!iError && (uTotalFiles == 0)) { fputs("No files were specified!\n", stderr); iError = 1; } if (iError) fputs(szUsage, stderr); else { unsigned u; fputs("File", stdout); if ((uTotalFiles > 1) && (uMaxName < 13)) uMaxName = 13; for (u = 4; u < uMaxName; ++u) putc(' ', stdout); fputs(" Lines Bytes in Bytes out\n", stdout); } return iError; } /* iInitialize */ /************************************************************************/ /* */ /* iReadLine: read the next line from the input file. */ /* */ /* passed: nothing */ /* returns: 0 (OK), -1 (error), or 1 (eof) */ /* */ /************************************************************************/ static int iReadLine(void) { int c; if (feof(pFileIn)) return 1; uLineLength = 0; memset(acLine, ' ', uLineMax); while ((c = getc(pFileIn)) != EOF) { ++ulFileBytesIn; switch (c) { case '\r': if ((c = getc(pFileIn)) == EOF) { if (feof(pFileIn)) return 0; break; } if (c != '\n') ungetc(c, pFileIn); else ++ulFileBytesIn; return 0; case '\n': return 0; case '\t': uLineLength = (uLineLength + uTabIn) & uTabInMask; if (uLineLength > uLineMax) uLineLength = uLineMax; break; case 26: if ((c = getc(pFileIn)) == EOF) break; ungetc(c, pFileIn); c = 26; /* fall through */ default: if (uLineLength < uLineMax) { acLine[uLineLength] = (char)c; ++uLineLength; } } if (c == EOF) break; } if (feof(pFileIn)) return (uLineLength == 0) ? 1 : 0; perror(pszFileIn); return -1; } /* iReadLine */ /************************************************************************/ /* */ /* iWriteLine: write the next line to the output file. */ /* */ /* passed: nothing */ /* returns: 0 (OK) or 1 (error) */ /* */ /************************************************************************/ static int iWriteLine(void) { ++ulFileLines; while ((uLineLength != 0) && (acLine[uLineLength-1] == ' ')) --uLineLength; if ((uCompress) && (uLineLength > uTabOut)) { unsigned uIn = 0; unsigned uOut = 0; unsigned uTab; unsigned uPos; do { if (acLine[uIn] == ' ') { uTab = (uIn + uTabOut) & uTabOutMask; if (uTab > uLineLength) uTab = uLineLength; if ((uTab - uIn) > 1) { for (uPos = uIn+1; uPos < uTab; ++uPos) if (acLine[uPos] != ' ') break; if (uPos == uTab) { acLine[uOut++] = '\t'; uIn = uTab; continue; } } } acLine[uOut++] = acLine[uIn++]; } while (uIn < uLineLength); uLineLength = uOut; } memcpy(acLine + uLineLength, szEol, uEolSize); uLineLength += uEolSize; if (!uTestMode && (fwrite(acLine, 1, uLineLength, pFileOut) != uLineLength)) { perror(szFileOut); return 1; } ulFileBytesOut += uLineLength; return 0; } /* iWriteLine */ /************************************************************************/ /* */ /* iProcessFile: process a file. */ /* */ /* passed: name of file to be processed */ /* returns: 0 (OK) or 1 (error) */ /* */ /************************************************************************/ static int iProcessFile( const char * pszArg) { int iError = 0; unsigned uPathLen = 0; /* Ignore command line parameters. */ if (pszArg[0] == '-') return 0; pszFileIn = pszArg; ulFileBytesOut = ulFileBytesIn = ulFileLines = 0; /* Open the input and output files. */ if ((pFileIn = fopen(pszFileIn, "rb")) == NULL) { perror(pszFileIn); return 1; } if (uTestMode) pFileOut = NULL; else { const char * p = strrchr(pszFileIn, DIR_SEP); if (p != NULL) { uPathLen = 1 + (p - pszFileIn); memcpy(szFileOut, pszFileIn, uPathLen); } strcpy(szFileOut + uPathLen, "X390TAB.TMP"); pFileOut = fopen(szFileOut, "wb"); if (pFileOut == NULL) { perror(szFileOut); fclose(pFileIn); return 1; } } /* (de)compress the file. */ do { if ((iError = iReadLine()) != 0) { iError = (iError < 0) ? 1 : 0; break; } iError = iWriteLine(); } while (!iError); if (!iError) { unsigned u; fputs(pszFileIn, stdout); for (u = strlen(pszFileIn); u < uMaxName; ++u) putc(' ', stdout); fprintf(stdout, "%11lu%11lu%11lu\n", ulFileLines, ulFileBytesIn, ulFileBytesOut); ulTotalLines += ulFileLines; ulTotalBytesIn += ulFileBytesIn; ulTotalBytesOut += ulFileBytesOut; } /* Clean up and exit */ fclose(pFileIn); if (!uTestMode) { #ifndef MSDOS char szFileNew[FILENAME_MAX]; strcpy(szFileNew, pszFileIn); if (cRename) { char * p; for (p = szFileNew + uPathLen; *p != '\0'; ++p) *p = (cRename == 'L') ? tolower(*p) : toupper(*p); } #else #define szFileNew pszFileIn #endif fclose(pFileOut); if (!iError && #ifdef UNIX (strcmp(szFileNew, pszFileIn) != 0) && #endif (remove(pszFileIn) != 0)) { fprintf(stderr, "Can't remove %s: %s\n", pszFileIn, strerror(errno)); iError = 1; } if (!iError && (rename(szFileOut, szFileNew) != 0)) { fprintf(stderr, "Can't rename %s to %s: %s\n", szFileOut, szFileNew, strerror(errno)); iError = 1; } if (iError) remove(szFileOut); } return iError; } /* iProcessFile */ /************************************************************************/ /* */ /* main: Entry point for the tab/detab utility */ /* */ /* passed: command line argument count and parameters. */ /* returns: highest return code. */ /* */ /************************************************************************/ int main( int argc, const char * const argv[]) { if (iInitialize(argc, argv) != 0) return 1; { int arg; for (arg = 1; arg < argc; ++arg) if (iProcessFile(argv[arg]) != 0) return 1; } if (uTotalFiles > 1) { unsigned u; fprintf(stdout, "\nTotal: %-6u", uTotalFiles); for (u = 13; u < uMaxName; ++u) putc(' ', stdout); fprintf(stdout, "%11lu%11lu%11lu\n", ulTotalLines, ulTotalBytesIn, ulTotalBytesOut); } return 0; } /* main */ /**************************** x390tab.c *******************************/