/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* retab.c - source for task retab */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* / Copyright (c) 1989 Paul Klink. All Rights Reserved. */ /* / / This Program must not be distributed or used without the */ /* / / / permission of the authors: */ /* / / / */ /* / / / Paul Klink P.O. Box 169 */ /* / / Woori-Yallock 3139 */ /* Australia */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* */ /* HISTORY */ /* */ /* Ed. Date Author Comments */ /* --- --------- ----------- ------------------------------------------ */ /* 001 06-Mar-90 Paul Klink Initial Development */ /* 002 19-Apr-90 Paul Klink Release 1.00 */ /* 003 05-May-90 Paul Klink modified so if -i=0 && -o=0, no retabbing */ /* made default for -l = 256 */ /* 05-May-90 Paul Klink Release 1.01 */ /* 004 06-May-90 Paul Klink removed the -l=0 option. Release 1.02 */ /* 005 08-May-90 Paul Klink Handled comments for -c option. Rel 1.03 */ /* 006 28-Jun-90 Paul Klink Fixed bug so \\" handled ok. Rel 1.04 */ /* 007 12-Nov-90 Paul Klink Handles C++ // comments in -c opt. 1.05 */ /* 008 15-Feb-92 Paul Klink IBM Version 1.06 */ /* */ /* Tab = 3 */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define RELEASE "Release 1.06" /* */ /* */ /* The task retab adjusts the hard tabs and spaces within a source file */ /* to allow for that document to be used for other editors, printers, or */ /* to allow for peoples' preferences. */ /* */ /* The term 'hard tab' refers to the tab character. That is '\t' or '\x09 */ /* This terminology comes from word processors. */ /* */ /* It is started with the following command line */ /* */ /* retab [opts] [opts] [] [opts] */ /* */ /* The square brackets denote optional parameters. */ /* */ /* specifies the source file that is to be */ /* retabbed. */ /* Use the -i option or the ReTabInTab */ /* environmental variable to specify what spacing */ /* hard tabs in the source file represent. */ /* */ /* specifies where the file in which the retabbed */ /* data is to go. If it is not specified, the */ /* source file is replaced with the new data. */ /* Use the -o option or the ReTabOutTab */ /* environmental variable to specify what spacing */ /* hard tabs in the destination file represent. */ /* */ /* The options can be one of the following: */ /* */ /* -i [=] */ /* specifies how big the source file hard tabs are */ /* (in nr. of characters). This integer can be in */ /* the range 0 to 32676. */ /* 0 = convert as much white space to tabs as */ /* possible. Any tabs in source file have */ /* the size as specified by the destination */ /* tab size. */ /* 1 to 32676 specifies a size in characters. */ /* The default value for this option is 8 unless */ /* it is specified by the ReTabInTab environmental */ /* variable. */ /* */ /* -o [=] */ /* specifies what tab size the hard tabs should */ /* represent in the output file (in nr. of */ /* characters). This integer can be in the range */ /* 0 to 32767. */ /* 0 = convert hard tab to space characters. */ /* 1 to 32767 specifies a size in characters. */ /* The default value for this option is 0 unless */ /* it is specified by the ReTabOutTab environmental*/ /* variable. */ /* */ /* -t specifies that all lines should be trimmed of */ /* white space at their end. This option can also */ /* be specified by the ReTabTrim environmental */ /* variable. Note: The terminating CRLF or LF */ /* will not be trimmed from the line. */ /* */ /* -p [[=] ] */ /* specifies that all space that exists between */ /* pairs of s should not be */ /* retabbed. The default is a */ /* " (double quote). This option can also be */ /* specified by the ReTabProtect environmental */ /* variable. If it is defined, then the option is */ /* active, and the first char in the environmental */ /* variable is the . If the */ /* environmental variable is a NULL string, then */ /* the default is used. */ /* The -c option has a higher priority than the -p */ /* option. */ /* */ /* -c is similar to the -p=" option. */ /* It will not retab any white space within a pair */ /* of double quote characters. It will also: */ /* - detect a C escaped double quote (\") and it */ /* will not consider it as a one of the */ /* s. */ /* - detect if a double quote is within a comment */ /* field. These will not be considered as */ /* protect characters. */ /* - It will be active if the environmental */ /* variable ReTabCQuote is defined. */ /* The -c option has priority over the -p option */ /* */ /* -l [=] [] */ /* -l [=] */ /* specifies at what line length the program warns */ /* the user about the line being too long. This */ /* integer can have any value from 1 to 32767. */ /* 1 to 32767 = the line length (in nr of char) */ /* If this option is not specified then it defaults*/ /* to */ /* -l=256 */ /* */ /* -n specifys that the the environmental variables */ /* should not be used. If either the -i or -o */ /* option are not specified, then the default */ /* should be used, even if the relevant */ /* environmental variable(s) exist. */ /* */ /* -? This option causes a help message to be printed */ /* to the screen. If this option is specified, */ /* no other processing will be done by the task. */ /* It will immediately exit with the exit code 0. */ /* */ /* All the options can be specified with either upper or lower case */ /* letters. The equal sign character is optional. */ /* */ /* The task makes use of these environmental variables if they exist. */ /* These are: */ /* ReTabInTab This should hold an ascii number string that */ /* specifies what size tab a hard tab character */ /* represents in the source file. */ /* ReTabOutTab This should hold an ascii number string that */ /* specifies what size tab a hard tab character */ /* should represent in the destination file. */ /* ReTabTrim If this environmental variable is defined, */ /* then the files retabbed will be trimmed even if */ /* the -t option was not specified. */ /* ReTabProtect Holds default for -p option */ /* ReTabCQuote -c option is active if this env. var. defined. */ /* */ /* The task searches for the hard tab, tab size specification by: */ /* 1) seeing if its specified in the command line. */ /* 2) if the -n option was not specified, it checks to see if the */ /* size is specified in the relevant environmental variable. */ /* 3) using the default for the option. */ /* */ /* The task returns the exit code 0 if it was successful. */ /* It returns the exit code 1 if it was not successful. */ /* */ /***************************************************************************/ #include #include #include #include #include #include #include #include "pkc.h" /*defines GetNextOpt() prototype*/ #define FILENAME_MAXLEN MAXPATH /*maximum length of filenames*/ #define DEFAULT_OPTL 256 /*default val for -l option*/ #define RETABINTAB "RETABINTAB" /*env var name for -i Opt*/ #define RETABOUTTAB "RETABOUTTAB" /*env var name for -o Opt*/ #define RETABTRIM "RETABTRIM" /*env var for -t Opt*/ #define RETABPROTECT "RETABPROTECT" /*env var for white sp prot*/ #define RETABCQUOTE "RETABCQUOTE" /*env var for C white sp prot*/ #define DEFAULT_OPTI 8 /*default for option -i*/ #define DEFAULT_OPTO 0 /*default for option -o*/ /*global static variables used*/ static char srcnam[FILENAME_MAXLEN+1]=""; /*where source file name is kept*/ static FILE *srcfp = NULL; /*src file file pointer*/ static char tempnam[FILENAME_MAXLEN+1]="";/*where temp file name is kept*/ static FILE *tempfp = NULL; /*temp file file pointer*/ static char destnam[FILENAME_MAXLEN+1]="";/*where dest file name is kept*/ static FILE *destfp = NULL; /*dest file file pointer*/ static int srctab; /*hard tab, tab size in src*/ static int desttab; /*hard tab, tab size in dest*/ static struct Opt { int i : 1; /*flag if -i option specified*/ int o : 1; /*flag if -o option specified*/ int t : 1; /*flag if -t option specified*/ int p : 1; /*flag if check for protected white space*/ int c : 1; /*flag if C escaped quotes should be handled*/ int l : 1; /*flag if -l option specified*/ int n : 1; /*flag if -n option specified*/ int Help : 1; /*flag if -? option specified*/ int Temp : 1; /*flag if temporary required*/ } Opt; static int ProtChar; /*holds protect char if Opt.p is specified*/ static int Comment = 0; /*determines if text is within a C comment. This is used if the -c option is specified. = 0 if not in comment = 1 if not in comment but last character was a / = 2 if in a comment = 3 if in a comment but last character was a * */ static int LineLength; /*Max line length before inter*/ static char *LBuf = NULL; /*used to point to buffer mem*/ /*text used in this task*/ static char *t[] = { "A File path name is too long, Limit is %d char", /*T_FILENAMETOOLONG*/ "Too many files specified", /*T_TOOMANYFILENAMES*/ "Invalid Number used in -i option", /*T_INVALIDOPTI*/ "Invalid Number used in -o option", /*T_INVALIDOPTO*/ "Invalid Number used in -l option", /*T_INVALIDOPTL*/ "Invalid Number specified in ReTabInTab environmental variable", /*T_BADENVVARI*/ "Invalid Number specified in ReTabOutTab environmental variable", /*T_BADENVVARO*/ "Task requires a source/input file name", /*T_NOINPUTFILE*/ "Error opening input file ", /*T_NOTOPENSRCFILE*/ "Invalid Option specified", /*T_INVALIDOPT*/ "Error opening output file ", /*T_NOTOPENDESTFILE*/ "Error in closing files ", /*T_ERRORCLOSING*/ "Temporary filename path to long! (node has 12)", /*T_TEMPNAMLONG*/ "Could not create temporary file", /*T_CREATETEMPERR*/ "Too many retab temporary files exist!", /*T_NOTEMPNAMEFND*/ "Could not obtain required memory!", /*T_NOTGETMEM*/ "Error while retabbing line %d", /*T_ERRINLINE*/ "%d lines successfully retabbed\n", /*T_NRLINESRETAB*/ "Error in reading source file * ", /*T_GETERROR*/ "Error in writing to file * ", /*T_ERRLINEWRITE*/ "Error in deleting source file * ", /*T_SRCFILENOTDEL*/ "Temporary file \"%s\" holds data : please examine", /*T_TEMPEXISTS*/ "Error in renaming temporary file * ", /*T_NOTRENAMETEMP*/ "Error in deleting temporary file * ", /*T_TEMPFILENOTDEL*/ "Error in deleting destination file * ", /*T_DESTNOTDEL*/ "Line %d too long! May be binary file. See -l option", /*T_LINETOOLONG*/ }; enum T_ { T_FILENAMETOOLONG, T_TOOMANYFILENAMES, T_INVALIDOPTI, T_INVALIDOPTO, T_INVALIDOPTL, T_BADENVVARI, T_BADENVVARO, T_NOINPUTFILE, T_NOTOPENSRCFILE, T_INVALIDOPT, T_NOTOPENDESTFILE, T_ERRORCLOSING, T_TEMPNAMLONG, T_CREATETEMPERR, T_NOTEMPNAMEFND, T_NOTGETMEM, T_ERRINLINE, T_NRLINESRETAB, T_GETERROR, T_ERRLINEWRITE, T_SRCFILENOTDEL, T_TEMPEXISTS, T_NOTRENAMETEMP, T_TEMPFILENOTDEL, T_DESTNOTDEL, T_LINETOOLONG, }; /*prototypes needed in this source*/ /* Prototypes for functions defined in retab.c */ int main(int argc, char **argv); static int CmdLine(int argc, char **argv); static int DoFileName(char *FileName); static int DoOption(int option, char *OptStr, int OptPrm); static void EnsureOpt(void); static int OpenFiles(void); static void CloseFiles(void); static FILE *MakeTempFile(void); static void *GetLineBuffer(void); static int RetabFile(void); static int RetabLine(int LineNr); static int TrimLine(int bufpos); static int SaveLine(int bufpos); static int HandleTempFile(void); static int DelOutFile(void); static int GetPosNr(char *str); static void PrErr(char *mes); static void PrErrI1(char *mes, int ival); static void PrErrS1(char *mes, char *istr); static void DoHelp(void); main (int argc, char *argv[]) { int ExitCode, Cmdaok; ExitCode = 1; /*assume an error will occur*/ Cmdaok = CmdLine(argc, argv); /*Decode command line*/ if (Cmdaok == 2 || Cmdaok == 0) /*error or help specified?*/ { if (Cmdaok == 2) ExitCode = 0; /*no error if help specified*/ } else { if ( OpenFiles() ) /*Open reqd files & get Memory*/ { if ( !RetabFile() ) /*Retab the file*/ { /*Retab was not successful*/ CloseFiles(); /*close opened files*/ DelOutFile(); /*Delete Dest/Temp file*/ } else { /*Retab was successful*/ CloseFiles(); /*Close opened files*/ if ( HandleTempFile() ) /*rename Temp file if reqd*/ { /*Task was successful*/ ExitCode = 0; /*flag success*/ } } } } exit(ExitCode); return; } /*===================================*/ /* CmdLine() analyses the command line and sets up various global variables accordingly. It also examines the environmental variables if required. If no filename is included, then help message is printed by default and it will return with an error code. On Entry: argc number of arguments in command line argv pointer array to command line arguments On Exit: 1 is returned if successful and processing shoud continue. 2 is returned if successful but no more processing 0 is returned if invalid command line. The following global variables are set accordingly: srcnam name of source file srctab hard tab, tab size in source file destnam name of destination file if specified desttab hard tab, tab size in destination file LineLength maximum line length before task is aborted ProtChar White Space Protection character Opt.i if the -i option is specified Opt.o if the -o option is specified Opt.t if the -t option is specified Opt.p if -p or -c option is specified Opt.c if the -c option is specified Opt.l if the -l option is specified Opt.n if the -n option is specified Opt.Help if the Help option was specified Note: If the -? option is specified, then the routine will immediately exit with the return code 2. This specifies that no more processing is required. */ static int CmdLine (int argc, char *argv[]) { int aok, action, option, OptPrm; char *OptStr; void DoHelp(void); int DoFileName(char *FileName); int DoOption(int option, char *OptStr, int OptPrm); void EnsureOpt(void); aok = 0; /*assume that an error will occur*/ action = 0; /*indicate to scan line from start*/ do { /*return first option or arg*/ option = GetNextOpt(action, &OptStr, &OptPrm, argc, argv); /*If it was a cmd arg. process by the */ /*DoFileName() ftn. */ /*action indicates the following: */ /* action = 1, no error occured */ /* action = -1, error occured */ if (option == 0) action = DoFileName(OptStr); else { /*If it was an option, process by the */ /*DoOption() ftn. */ /*action indicates one of the following: */ /* action = 1, no error, Opt str used */ /* action = 2, no error, Opt str not */ /* used */ /* action = -1, error occured */ if (option > 0) action = DoOption(option, OptStr, OptPrm); else { /* all cmd line args examined */ /* if no filename specified then print */ /* the help message and exit with error. */ /* Otherwise ensure that the required */ /* options have values. */ EnsureOpt(); /*see that all options have val*/ aok = 1; /*flag cmd line ok*/ } } } while (option>=0 && action>=0); /*end of do loop, exit loop when either all cmd args done or when an error occurs*/ if (Opt.Help) /*is help required?*/ { DoHelp(); /*yes, print help message*/ aok = 2; /* and no processing return*/ } return(aok); /*return error code to main*/ } /*------------------------------------*/ /* DoFileName() sets up the filename in the in the relevant global variables. It places the first name in the srcnam[] array and the second in the destnam[] array. An error occurs if more than two names are specified. An error will also occur if a filename is too long. On Entry: FileName points to string holding filename On Exit: action = 1 if successful = -1 if an error occured */ static int DoFileName(char *FileName) { int aok; char *sptr; aok = -1; /*assume an error will occur*/ /*see if name is too long, if so, error*/ if ( strlen(FileName) > FILENAME_MAXLEN ) { PrErrI1( t[T_FILENAMETOOLONG], FILENAME_MAXLEN ); } else { /*name is not too long. */ /*Determine in which variable the name */ /*should be placed. */ sptr = srcnam; if (*sptr != '\0') sptr = destnam; /*If both variables are already used, */ /*then an error occured, otherwise, */ /*copy File name to variable. */ if (*sptr != '\0') { PrErr ( t[T_TOOMANYFILENAMES] ); } else { strcpy ( sptr, FileName ); aok = 1; /*flag success*/ } } return(aok); } /*-------------------------------------*/ /* DoOption() examines the option and determines what kind of action code should be returned. If the option code is valid, then the respective global variable(s) are set accordingly. On Entry: option = option code OptStr = option string determined by GetNextOpt() On Exit: action = 1 valid option and option string used. = 2 valid option and option string not used = -1 invalid option If a valid option was specified then, specific global variables will be set up accordingly. */ static int DoOption(int option, char *OptStr, int OptPrm) { int action, Nr; action = -1; /*assume an error will occur*/ switch (tolower(option)) { case 'i': /*src file tab size specified*/ if ( (Nr=GetPosNr(OptStr)) >= 0 ) /*valid Nr Specified?*/ { srctab = Nr; /*yes, set up src tab size*/ Opt.i = 1; /*and flag that it is gotten*/ action = 1; /*flag string used*/ } else { PrErr (t[T_INVALIDOPTI]); /*Invalid, send Message*/ } break; case 'o': /*dest file tab size specified*/ if ( (Nr=GetPosNr(OptStr)) >= 0 ) /*valid Nr Specified?*/ { desttab = Nr; /*yes, set up dest tab size*/ Opt.o = 1; /*and flag that it is gotten*/ action = 1; /*flag string used*/ } else { PrErr (t[T_INVALIDOPTO]); /*Invalid, send Message*/ } break; case 't': /*flag that file should be trimmed*/ Opt.t = 1; /*flag this*/ action = 2; /*indicate that str not used*/ break; case 'p': /*protected white space in src*/ if (Opt.c==0) /*can not do if -c already spec*/ { Opt.p = 1; /*flag this*/ /*see if default should be used?*/ if (!OptPrm && *OptStr && *OptStr!=' ' && *OptStr!='\t') { /*no, use specified char*/ ProtChar = (int) (*OptStr) & 0xff; /*ensure not sign extended*/ action = 1; /*flag option string used*/ } else { ProtChar = (int) '\"' & 0xff; /*use default*/ action = 2; /*flag string not used*/ } } break; case 'c': /*C protected white space in src*/ Opt.p = 1; /*flag protected white space*/ ProtChar = '\"'; /*prot char is double quote*/ Opt.c = 1; /*handle C escaped dble quotes*/ action = 2; /*flag string not used*/ break; case 'l': /*Max line length option*/ if (( Nr=GetPosNr(OptStr)) > 0 ) /*legal value specified?*/ { LineLength = Nr; /*yes, set up Line Length*/ Opt.l = 1; /*flag that it is gotten*/ action = 1; /*flag string used*/ } else { /*illegal or not in this param*/ PrErr (t[T_INVALIDOPTL]); /*Invalid, send Message*/ } break; case 'n': /*Do not use env var. option*/ Opt.n = 1; /*flag it*/ action = 2; /*option string not used*/ break; case '?': /*help option*/ Opt.Help = 1; /*flag it*/ action = 2; /*option string not used*/ break; default: /*invalid option*/ PrErr(t[T_INVALIDOPT]); /*indicate bad option*/ } return (action); /*return to CmdLine()*/ } /*-------------------------------------*/ /* EnsureOpt() ensures that all options are correctly set up. If an option's value has not been specified in the command line, then EnsureOpt() sees if it is defined in an Environmental variable. If not, it is given a default if so required. On Entry: - On Exit: - */ static void EnsureOpt(void) { int Nr; char *ev; /*check -i option*/ if (!Opt.i) /*src tab size defined?*/ { if (!Opt.n) /*no, allowed to used env var?*/ { ev = getenv(RETABINTAB); /*yes, get reqd env var*/ if (ev) /*was it defined?*/ { if ( (Nr=GetPosNr(ev)) >= 0 ) /*yes, valid Nr Specified?*/ { srctab = Nr; /*yes, use it for -i Opt val*/ Opt.i = 1; /*flag option defined*/ } else { PrErr(t[T_BADENVVARI]); /*bad Env var value, send mes*/ } } } if (!Opt.i) /*still not defined?*/ { srctab = DEFAULT_OPTI; /*use default value*/ Opt.i = 1; /*defined now!*/ } } /*check -o option*/ if (!Opt.o) /*dest tab size defined?*/ { if (!Opt.n) /*no, allowed to use env var?*/ { ev = getenv(RETABOUTTAB); /*yes,get reqd env var*/ if (ev) /*was it defined?*/ { if ( (Nr=GetPosNr(ev)) >= 0 ) /*yes, valid Nr Specified?*/ { desttab = Nr; /*yes, use it for -o Opt val*/ Opt.o = 1; /*flag option defined*/ } else { PrErr(t[T_BADENVVARO]); /*bad Env var value, send mes*/ } } } if (!Opt.o) /*still not defined?*/ { desttab = DEFAULT_OPTO; /*use default value*/ Opt.o = 1; /*defined now!*/ } } /*check -l option*/ if (!Opt.l) /*Option l already defined?*/ { LineLength = DEFAULT_OPTL; /*use default value*/ Opt.l = 1; /*flag that it is defined*/ } /*check -t option*/ if (!Opt.t) /*trim option specified?*/ { if (!Opt.n) /*no, allowed to use env var?*/ { ev = getenv(RETABTRIM); /*yes, get reqd env var*/ if (ev) /*was it defined?*/ { Opt.t = 1; /*yes, trim the file*/ } } } /*check -c option This has to be checked before -p*/ if (!Opt.c) /*C quote option specified?*/ { if (!Opt.n) /*no, allowed to use env var?*/ { ev = getenv(RETABCQUOTE); /*yes, get reqd env var*/ if (ev) /*was it defined?*/ { /*yes*/ Opt.c = 1; /*handle C escaped dbl quotes*/ Opt.p = 1; /*yes, protect quoted white sp*/ ProtChar = (int) '\"' & 0xff; /*dbl quote protects white sp*/ } } } /*check -p option This has to be checked after -c*/ if (!Opt.p) /*prot white space specified?*/ { if (!Opt.n) /*no, allowed to use env var?*/ { ev = getenv(RETABPROTECT); /*yes, get reqd env var*/ if (ev) /*was it defined?*/ { Opt.p = 1; /*yes, protect relev. white sp*/ /*determine protect character*/ ProtChar = ((int) ((ev) ? *ev : '\"')) & 0xff; } } } } /*=====================================*/ /* OpenFiles() opens all the required files. It also obtains the memory buffer required for the line rettabing. It first of all checks that at least the source file was specified. If it wasn't, an error is flagged, the help message is printed to the screen and the task aborts. Otherwise it opens the source file. It then checks to see if an output file was specified. If so, it opens that file, overwriting any previous file. If not, it assumes that the output file is to overwrite the input file. In this case, it tries to create a temporary file, 'retab_01.tmp', in the current directory. If this file already exists, it tries for 'retab_02.tmp' and so on. If the task successfully retabbed the input file into the temporary file, it then deletes the input file and renames the temporary file to the name of the input file. If the task was not successful, the temporary file is deleted and the input file is only closed. On Entry: Expect that the following task global variables be set up. srcnam - holds name of source file destnam - holds name of destination file if specified if not specified, holds a NULL string On Exit: returns: 1 if successful. 0 if an error occured. The following task variables are set up srcfp destfp tempfp Opt.Temp LBuf */ static int OpenFiles(void) { int aok; aok = 0; /*assume an error will occur*/ /*get memory for line buffer*/ if ( (LBuf = (char *) GetLineBuffer())!=0 ) /*get mem, cont if success*/ { /*open source file if specified*/ if (*srcnam == '\0') /*was input file specified?*/ { PrErr(t[T_NOINPUTFILE]); /*no, tell user must have it*/ DoHelp(); /*and print help message*/ } else { srcfp = fopen(srcnam, "r"); /*open source file*/ if (srcfp == NULL) /*successfully opened?*/ { perror(t[T_NOTOPENSRCFILE]); /*no, print error message*/ } else { /*open destination file if specified */ /*otherwise open a temporary file */ if (*destnam == '\0') /*was an output file specified?*/ { Opt.Temp=1; /*no, a temporary file reqd*/ tempfp = MakeTempFile(); /*open it*/ if (tempfp) /*got temp file?*/ { aok = 1; /*flag success*/ } } else /*an output file was specified*/ { Opt.Temp = 0; /*no temporary file needed*/ destfp = fopen(destnam, "w"); /*create destination file*/ if (destfp == NULL) /*successfully created?*/ { perror(t[T_NOTOPENDESTFILE]); /*no, print error message*/ } else { aok = 1; /*yes, flag success*/ } } } } } /*Close all files and get rid of memory if any err*/ if (aok == 0) /*did an error occur?*/ { CloseFiles(); /*yes, return resources*/ } return(aok); } /*-------------------------------------*/ /* CloseFiles() closes all opened files and returns the memory obtained for the line buffer. On Entry: void Uses the task variables srcfp, destfp tempfp and LBuf to see which resources have been claimed and should be returned. On Exit: - */ static void CloseFiles(void) { errno = 0; /*ensure that no error flagged*/ if (srcfp) fclose(srcfp); /*yes, close all opened files*/ if (destfp) fclose(destfp); if (tempfp) fclose(tempfp); if (errno) perror(t[T_ERRORCLOSING]); /*print if err in closing*/ if (LBuf) /*need to return mem?*/ { free( (void *) LBuf); /*yes, do so*/ } } /*-------------------------------------*/ /* MakeTempFile() creates a temporary file of the name retab_??.tmp where ?? is a two digit integer. It will not overwrite any existing file. It will return the file pointer if it is successful. The temporary file is placed in the same directory as the source file was in. Note: This function is not ANSI compatible On Entry: - On Exit: fp = file pointer to file created if successful. In this case the file name is placed in the variable tempnam . = NULL if an error occured */ static FILE *MakeTempFile(void) { FILE *fp; int CrErr, cnt; char drive[MAXDRIVE]; char dir[MAXDIR]; char file[MAXFILE]; char ext[MAXEXT]; fp = NULL; /*assume an error will occur*/ CrErr = 0; /*set to 1 if create error occur*/ fnsplit(srcnam,drive,dir,file,ext); /*split the string to separate elems*/ strcpy(ext,"tmp"); /*set up required extension" cnt = 0; /*try file retab_00.tmp first*/ do { sprintf(file, "retab_%02.2d", cnt); /*create node name*/ fnmerge(tempnam,drive,dir,file,ext);/*merge everything into one string*/ /*create a temporary file. Make sure file */ /*is non existent and ensure noone else can */ /*grab it. */ // Forbid(); /*do not allow any other task*/ // /*amiga specific*/ fp = fopen(tempnam, "r"); /*see if file exists*/ if (fp) /*already exists?*/ { fclose(fp); /*yes, close file*/ *file = '\0'; /*flag already exists*/ } else { fp = fopen(tempnam, "w"); /*no, use this name*/ if (fp == NULL) /*error in creating this file?*/ { CrErr = 1; /*yes, flag so, do not print as multitasking is forbidden*/ } } // Permit(); /*reallow multitasking*/ // /*amiga specific*/ if (CrErr) /*did an error occur?*/ { PrErr(t[T_CREATETEMPERR]); /*yes send message*/ } cnt += 1; /*increment file name if neces*/ } while (!*file && cnt<=99); /*end of do loop*/ if (cnt==100 && !fp) /*suitable name found?*/ { PrErr(t[T_NOTEMPNAMEFND]); /*no, send error message*/ } return(fp); } /*-------------------------------------*/ /* GetLineBuffer() gets the required amount of memory needed to buffer the new line built while retabbing. The size of the buffer is calculated by the formula: if desttab == 0 then destab = 1 if srctab == 0 size = (LineLength+1) * desttab + 5 else size = (LineLength+1) * srctab + 5 srctab and desttab are not changed! This allows for any possibilities. On Entry: Expect that variables, LineLength, srctab, desttab, are set up. On Exit: mptr - points to memory buffer if successfully gotten - NULL if an error occured. Error message will have been sent. */ static void *GetLineBuffer( void ) { int size; void *mptr; if (srctab==0) /*calculate buffer size needed*/ { size = (LineLength + 1) * (desttab ? desttab : 1) + 5; } else { size = (LineLength + 1) * srctab + 5; /*size of buffer*/ } mptr = malloc(size); /*get mem for buffer*/ if (mptr == NULL) /*did an error occur?*/ { PrErr( t[ T_NOTGETMEM ] ); /*yes send message*/ } return(mptr); } /*=====================================*/ /* RetabFile() retabs all the lines within the file. It will continue until all are retabbed or an error occurs On Entry: Expect all task static variables to be set up On Exit: returns 1 if successful 0 if not successful */ static int RetabFile(void) { int rl, lines; lines = 0; /*Nr of lines examined*/ do { rl = RetabLine(lines+1); /*retab a line*/ if (rl!=-1 && rl&1) lines++; /*count number of lines retabbed*/ } while (rl == 1); /*continue till EOF or error*/ if (rl == -1) /*did an error occur?*/ { PrErrI1( t[T_ERRINLINE],lines+1 ); /*yes, send mes*/ rl = 0; /*flag failure*/ } else { printf( t[T_NRLINESRETAB],lines ); /*no, say successful*/ rl = 1; /*flag success*/ } return (rl); /*show main if error occured*/ } /*-------------------------------------*/ /* RetabLine() retabs the current line. It assumes that the next char to be drawn from the source file is the first character in a line. It will detect the end of a line if it encounters one of the following sequences, LF ( \x0a ) EOF ( flagged by getchar() function ) The ftn does not require that the last line be terminated by a LF. RetabLine() will replace any tab by its corresponding number of spaces. It will then examine the sets of consecutative spaces that previously held a tab in them. It will try and place as many tabs as it can it that section of spaces where each tab represents the number of spaces as specified by desttab. If desttab is set to 0 then spaces are not converted to tabs. If srctab is set to 0, then all source tabs represent the size specified by desttab, unless desttab==0, then they have a size of 1. In this case, RetabLine() will try to convert as many spaces to tabs as it possibly can, even if the group of spaces did not hold a tab in it in the source file. The following rule is followed: Each group of spaces must be able to hold at least one full destination tab before a tab will be placed in that group of spaces. The exception being if a hard tab was in the group in the src. NOTE: Be careful with spaces in C strings with this feature. An error will occur if: Get returns an error except for EOF. Put returns an error The line char count exceeds the limit specified by the LineLength task global variable. A message will be sent describing the cause of the error On Entry: LineNr = line nr being retabbed Expect that files be opened, and task global variables be set up On Exit: aok = 3 if line was successfully tabbed and EOF was encountered = 2 if an EOF was encounterred = 1 if line was successfully tabbed = -1 if an error was encountered Note: Bit 0 indicates if a line was tabbed & bit 1 indicated if EOF */ static int RetabLine(int LineNr) { int c, aok, bufpos, LinePos, TabStart, LineTabStart, DoTab; int i, ll, OldLinePos, NextLineTabStart, intab; int protect, backslash; ll = 0; /*Nr of char. counted*/ bufpos = 0; /*index in buffer*/ LinePos = 0; /*position in line*/ TabStart = 0; /*char at whic tab starts*/ LineTabStart = 0; /*pos in line at which tabb st*/ DoTab = 0; /*set to 1 when tabbing to occur*/ protect = 0; /*set to 1 when space protected*/ backslash = 0; /*set to 1 when prev char was \*/ intab = (srctab) ? srctab : desttab; /*work out size represented by tabs. intab=0 means no retab*/ errno = 0; /*reset error flag*/ aok = 4; /*changed when loop is to exit*/ do { c = fgetc(srcfp); /*get a char from src*/ switch (c) { case EOF: if (!errno) /*at EOF?*/ { aok = 2; /*flag that EOF was encountered*/ } else { PrErr( t[ T_GETERROR ] ); /*error, send message*/ aok = -1; /*flag error*/ } break; case '\x0a': /*line feed encountered*/ LBuf [ bufpos++ ] = c; /*copy into buffer*/ aok = 1; /*flag line should be saved*/ break; case '\t': /*tab encountered*/ OldLinePos = LinePos; /*record old line position*/ /*work out new position*/ LinePos = (intab) ? (LinePos/intab + 1) * intab : LinePos+1; if (protect || !intab) /*is white space protected */ { /* or not to be retabbed?*/ LBuf [ bufpos++ ] = c; /*yes, place tab in buffer*/ } else /*not protected*/ { for (i=OldLinePos; i1))) /* yes, is protect char escaped or in comment?*/ { protect = !protect; /*no, toggle protect state*/ Comment = 0; /*just in case it was 1*/ } } } break; } /*See if backslash (may escape a double quote)*/ backslash = (c=='\\') ? !backslash : 0; /*flag if backslash escape*/ /*see if we are in a C comment*/ if (Opt.c && !protect) /*in C mode and not protected?*/ { switch (Comment) /*yes, see if in a C(++) comment*/ { case 0 : /*Not in one previously*/ if (c=='/') Comment = 1; /*if /, next char may start comm*/ break; case 1 : /*Not within comm, last char / */ switch (c) { case '*' : Comment = 2; /*Now within C comment*/ break; case '/' : Comment = 4; /*Now within C++ comment*/ break; default : Comment = 0; /*Not in a comment*/ } break; case 2 : /*within C comment previously*/ if (c=='*') Comment = 3; /*if *, next char may end comm*/ break; case 3 : /*within C comment, last char * */ if (c=='/') Comment = 0; /*if /, out of comment*/ else Comment = 2; /*otherwise still in comment*/ break; case 4 : /*C++ comment.*/ if (c=='\x0a') Comment = 0; /*Comment ends at end of line*/ break; } } /* INSERT TABS IF APPROPRIATE */ if (aok != -1) /*continue if no error occured*/ { if (srctab==0 && desttab) /*see if we can tab a group of spaces in this mode?*/ { if ((LinePos/desttab)*desttab-LineTabStart >= desttab) if (!protect) DoTab = 1; } if (DoTab && desttab) /*tab section?*/ /*desttab==0 means leave as spaces (or, as is, if -i=0)*/ { do /*loop to add reqd nr of tabs*/ { /*work out start of next tab*/ NextLineTabStart = (LineTabStart/desttab + 1) * desttab; if (LinePos >= NextLineTabStart) { LBuf [TabStart++] = '\t'; /*yes, include it*/ bufpos = bufpos - (NextLineTabStart-LineTabStart) + 1; /*adjust bufpos*/ /*Note: char between TabStart & bufpos are all spaces*/ LineTabStart = NextLineTabStart; /*new tab start pos*/ } } while (LinePos >= NextLineTabStart); } } /*Check if maximum line input length is exceeded*/ if (LineLength && ++ll > LineLength) /*Active & line too long?*/ { PrErrI1(t[T_LINETOOLONG], LineNr); /*yes, send error message*/ aok = -1; /*and flag error*/ } } while (aok == 4); /*end of DO-WHILE loop*/ if (aok != -1) /*continue if no error*/ { if (Opt.t && bufpos>0) bufpos = TrimLine(bufpos); /*trim line if*/ aok = SaveLine(bufpos) | aok; /*save line (OR to indicate EOF or line saved)*/ } return (aok); } /*-------------------------------------*/ /* TrimLine() trims the line of trailing whitespace. It does not remove the trailing LF or CRLF codes. On Entry: bufpos length of line in buffer (includeing CRLF orLF codes) On Exit: bufpos new length of line */ static int TrimLine(int bufpos) { int crlf; crlf = 0; /*yes, assume line is not term*/ bufpos -= 1; /*point to last char in line*/ if (LBuf[bufpos] == '\x0a') /*line terminated with lf?*/ { crlf = 1; /*yes flag so*/ bufpos -= 1; /*step back*/ } if (bufpos>0 && LBuf[bufpos] == '\x0d')/*is line term by crlf?*/ { crlf = 2; /*yes, flag so*/ bufpos -= 1; /*step back in line*/ } /*loop and remove all EOL space*/ while (bufpos>=0 && (LBuf[bufpos]==' ' || LBuf[bufpos]=='\t')) { bufpos -= 1; /*yes*/ } bufpos += 1; /*point to next free pos*/ if (crlf==2) /*yes, terminated with CRLF?*/ { LBuf[bufpos++] = '\x0d'; /*yes, add the CR code*/ crlf = 1; /*do this so that LF will be add*/ } if (crlf==1) /*terminated with LF?*/ { LBuf[bufpos++] = '\x0a'; /*yes, add the LF code*/ } return(bufpos); } /*-------------------------------------*/ /* SaveLine() saves a retabbed line to the destination file. On Entry: bufpos = nr. of bytes to be written to file LBuf points to buffer holding bytes On Exit: aokerr = 1 if successful & data was saved = 0 if successful but no data was saved = -1 if an error occured */ static int SaveLine (int bufpos) { int aokerr; /*write line to output, error?*/ if (fwrite(LBuf, bufpos, 1, (Opt.Temp ? tempfp : destfp)) == 0) { perror(t[T_ERRLINEWRITE]); /*yes, send message*/ aokerr = -1; } else { aokerr = (bufpos > 0) ? 1 : 0; /*indicate if data saved*/ } return(aokerr); } /*=====================================*/ /* HandleTempFile() deletes the source file and renames the temporary file to that of the previous source file. On Entry: void Expect names of both files are set up and both files are closed On Exit: aok = 1 if successful 0 if an error occured */ static int HandleTempFile(void) { int aok; aok = 1; /*assume success*/ if (Opt.Temp) /*only reqd if Temp file exist*/ { if (remove(srcnam)) /*del src file. Error?*/ { perror(t[T_SRCFILENOTDEL]); /*yes, send message*/ PrErrS1(t[T_TEMPEXISTS],tempnam);/*say that temp exists*/ aok = 0; /*flag error*/ } else { if (rename(tempnam,srcnam) == -1)/*rename temp to src. Error?*/ { perror(t[T_NOTRENAMETEMP]); /*yes, send message*/ PrErrS1(t[T_TEMPEXISTS],tempnam); /*say that temp exists*/ aok = 0; /*flag error*/ } else { aok = 1; /*success*/ } } } return(aok); } /*-------------------------------------*/ /* DelOutFile() deletes the output file (ie either the destination or temporary file). Opt.Temp specifies which is to be deleted. On Entry: void On Exit: aok = 1 if successful 0 if error */ static int DelOutFile(void) { int aok; aok = 1; /*assume no error will occur*/ if (Opt.Temp) /*delete temporary file?*/ { if (remove(tempnam)) /*yes. delete it. Error?*/ { perror(t[T_TEMPFILENOTDEL]); /*yes, send message*/ PrErrS1(t[T_TEMPEXISTS],tempnam);/*say that temp exists*/ aok = 0; /*flag error*/ } } else { if (remove(destnam)) /*delete destination. Error?*/ { perror(t[T_DESTNOTDEL]); /*yes, send message*/ aok = 0; /*flag error*/ } } return(aok); } /*=====================================*/ /* GetPosNr() extacts an integer from the string. The string has to hold a valid positive integer or else -1 is returned. On Entry: str points to staring On Exit: nr = number from string if successful = -1 if not */ static int GetPosNr(char *str) { long r; int nr, i; char *np; r = strtol(str, &np, 10); /*convert to value*/ i = strspn(np, " \t"); /*skip over trailing spaces*/ if (np[i]) /*anything else after nr?*/ { nr = -1; /*yes. Error*/ } else { if (r<0 || r>32767) /*is r in range?*/ { nr = -1; /*no, Error*/ } else { nr = (int) r; /*yes, success*/ } } return(nr); } /*-------------------------------------*/ /* PrErr() prints an error to the standard error output On Entry: mes points to error message On Exit: void */ static void PrErr(char *mes) { fprintf(stderr, mes); fprintf(stderr, "\n"); } /* PrErrI1() prints an error to the standard error output and with it, one integer value. On Entry: mes points to error message ival integer value On Exit: void */ static void PrErrI1(char *mes, int ival) { fprintf(stderr, mes, ival); fprintf(stderr, "\n"); } /* PrErrS1() prints an error to the standard error output and with it, one string. On Entry: mes points to error message istr string to be printed On Exit: void */ static void PrErrS1(char *mes, char *istr) { fprintf(stderr, mes, istr); fprintf(stderr, "\n"); } /*=====================================*/ /* DoHelp() prints the help message to the stdout On Entry: void On Exit: void */ static void DoHelp(void) { printf("\n"); printf("Syntax: Retab [opts] [opts] [] " "[opts]\n\n"); printf("Function: To reformat a file so that the tab characters " "represent a\n"); printf(" different tab size. If no Destination file is " "specified, the\n"); printf(" source file is overwritten.\n"); printf("\n"); printf("Options: -i[=]\n"); printf(" Size tabs represent in the source file.\n"); printf(" If not specified, size obtained from " "environmental variable\n"); printf(" ReTabInTab. Otherwise defaults to %d.\n", DEFAULT_OPTI); printf(" -o[=]\n"); printf(" Size tabs represent in the destination file.\n"); printf(" If not specified, size obtained from " "environmental variable\n"); printf(" ReTabOutTab. Otherwise defaults to %d. (Tabs " "become spaces)\n", DEFAULT_OPTO); printf(" -t Trim Lines of trailing white space. Active if " "environmental\n"); printf(" variable ReTabTrim is defined.\n"); printf(" -p[[=]] (Default " " = \")\n"); printf(" Do not retab white space enclosed within a pair " "of\n"); printf(" . Active if Env. Var. " "ReTabProtect defined\n"); printf(" -c Do not retab white space enclosed within double " "quote chars.\n"); printf(" C escaped quotes handled. Active if ReTabCQuote " "defined.\n"); printf(" -n Do not use environmental variables.\n"); printf(" -l[=]\n"); printf(" Retab will abort if line longer than this. " "Default = %d.\n", DEFAULT_OPTL); printf(" -? Print this message. " RELEASE "\n\n"); printf("Author: Paul Klink, PO. Box 169, Woori-Yallock 3139, " "Australia\n"); printf("\nShareWare!! Distribute freely!! Any feedback welcome. " "Any CASH welcome.\n\n"); } /*======== End of Source file ========*/