/* CRLF.C - Translates files for use in the QuickC editor. * * Copyright (c) 1988-1989, Microsoft Corporation * * A program list is not required to create CRLF from inside QC. No * special options are required. * * The QuickC editor has the following restrictions on the files it * can edit: * * - All lines must be terminated by a carriage return (CR) followed * by a line feed (LF)--ASCII codes 0x0D/0x0A. * * - Character 0 is not allowed. All other characters in the * range 1 to 255 are allowed. * * The editor specifically handles CR (0x0D), LF (0x0A), and HT (0x09). * Since other control characters may be undesirable, CRLF allows the * user to optionally change control characters to a specified character * or to simply remove them. The syntax is * * CRLF [/c] * * where is the file to be translated, is the new * translated file, and is the ASCII code for the character * to which control characters (except CR, LF, and HT) will be translated. * The ASCII code may be entered in decimal (ddd), octal (0ddd), or * hexadecimal (0xddd). * * For example, /c32 will translate all control characters to spaces. * If is given as 0, control characters will be removed rather * than translated. If /c is not given, the program will prompt for * translation characters. */ #include #include #include #include #include #include /* Constants */ #define NUM_CHARS 32 /* Total number of control characters */ #define HT 0x09 /* Horizontal tab character */ #define LF 0x0A /* Linefeed character */ #define CR 0x0D /* Carriage-return character */ #define SKIP 0 /* Filter out control characters */ #define PROMPT -1 /* Get translation from prompts */ /* Global variables. Note that the compiler initializes global arrays * to 0. The code in translate_char() depends on the following global * array being initialized to 0. */ int mapping[NUM_CHARS]; int global_trans = PROMPT; /* Function prototypes */ void translate_file( FILE *fdIn, FILE *fdOut ); int translate_char( int line, int c ); /* main - opens input and output files and translates them. * * Params: argc - count of arguments * argv - array of argument strings * * Return: None */ void main( int argc, char *argv[] ) { FILE *fdInput; FILE *fdOutput; int c; /* Check command line arguments for validity and print syntax * prompt if invalid. */ if( (argc != 3) && (argc != 4) ) { fprintf( stderr, "SYNTAX: crlf [/c]\n" ); exit( 1 ); } /* Check for the /c (or -c) option. If present, read in the * global translation value. */ if( argc == 4 ) { if( ((argv[3][0] == '/') || (argv[3][0] == '-')) && (toupper( argv[3][1] ) == 'C') ) { sscanf( &argv[3][2], "%i", &global_trans ); if( global_trans > 255 ) { fprintf( stderr, "crlf: unknown translation character\n" ); global_trans = PROMPT; } } else { fprintf( stderr, "crlf: fatal error: unknown option '%s'\n", argv[3] ); exit( 1 ); } } /* Try to open the input and output files in binary mode. */ if( (fdInput = fopen( argv[1], "rb" )) == NULL ) { fprintf( stderr, "crlf: fatal error: can't open '%s'\n", argv[1] ); exit( 1 ); } if( !access( argv[2], 0 ) ) { printf( "File exists. Overwrite? " ); c = getch(); if( c != 'y' && c != 'Y' ) exit( 1 ); printf( "\n" ); } if( (fdOutput = fopen( argv[2], "wb" )) == NULL ) { fclose( fdInput ); fprintf( stderr, "crlf: fatal error: can't open '%s'\n", argv[2] ); exit( 1 ); } /* Translate the input file to the output file. */ translate_file( fdInput, fdOutput ); /* Close the files and exit. */ fclose( fdInput ); fclose( fdOutput ); exit( 0 ); } /* translate_file - translates file for use in QuickC editor. Checks * each character to see if it is a control character, and if so, * handles appropriately. * * Params: fdIn - input file handle, as obtained from fopen() * fdOut - output file handle, as obtained from fopen() * * Return: None * * Uses: trans_mode - character to translate to */ void translate_file( FILE *fdIn, FILE *fdOut ) { int c; int i; int line = 1; /* Loop until the end of the file, reading each character from * the input file and writing it to the output file. Handle * control characters; pass other characters through unchanged. * Note * that a return value of EOF does NOT necessarily indicate * the end of the file--feof() must be called to verify that * the end of file has actually been reached. */ while( ((c = getc( fdIn )) != EOF) || (!feof( fdIn )) ) { /* If CR, check and handle the next character. */ if( c == CR ) { /* Get the next key. */ c = getc( fdIn ); /* If we've reached the end of file, write character and * break loop to terminate function. */ if( (c == EOF) && (feof( fdIn )) ) { putc( CR, fdOut ); break; } /* If it's a LF, write the CR/LF, increment the line count, * and use the continue statement to return to the top of * the loop for the next character. */ else if( c == LF ) { putc( CR, fdOut ); putc( LF, fdOut ); ++line; continue; } /* If it's a CR with no following LF, skip or translate it. */ else { if( global_trans != SKIP ) { putc( translate_char( line, CR ), fdOut ); } } } /* If it's LF with no preceeding CR (common in files uploaded * from Unix or Macintosh), write a CR/LF and increment the * line count. */ else if( c == LF ) { putc( CR, fdOut ); putc( LF, fdOut ); ++line; } /* If it's a control character that we haven't already handled, * skip or translate it (depending on value of global_trans). */ else if( (c != HT) && (c < NUM_CHARS) ) { if( global_trans != SKIP ) putc( translate_char( line, c ), fdOut ); } /* Otherwise, just write the character to the output file. */ else putc(c, fdOut); } } /* translate_char - translates a control character. If a translate * character was provided on the command line, it is used. Otherwise * the user is prompted for a translation character. This character * is put in the appropriate element of the mapping[] array for future * reference. * * For example, if we find character 3, and the user wants to map * this to character 42, we set mapping[3] = 42. Next time we encounter * character 3, we look at mapping[3] and substitute character 42 * without asking. * * Params: line - line number in which the character was found * c - character to be translated * * Return: Translated value of character (may be same as original * character if user didn't want to translate it) * * Uses: trans_mode - character to translate to * mapping - array of translatable characters */ int translate_char( int line, int c ) { int new_val; /* If command-line translation code was given, return it. */ if( global_trans != PROMPT ) return( global_trans ); /* Otherwise, check to see if we've already got a translated value * for this character. If so, return that value. */ if( mapping[c] != 0 ) return( mapping[c] ); /* Prompt for a translation value. We can't accept 0 or numbers * larger than 255, so loop until we get a valid translation * character. The for loop without any parameters will loop until * a break statement is encountered. The scanf %i specifier is given * so that the user can enter the ASCII code in decimal (ddd), * octal (0ddd), or hexadecimal (0xddd). * * A different input mechanism could be implemented here. * For example, you could use getche() to allow the user to * type in the replacement character directly rather than * entering an ASCII code. */ printf( "Control character %u in line %u: ", c, line ); for( ;; ) { printf( "Enter ASCII translation code: " ); scanf( "%i", &new_val ); if( (new_val == 0) || (new_val > 255) ) { printf( "Invalid translation value\n" ); fflush( stdin ); } else break; } /* Remember the translated value in mapping[c]. */ mapping[c] = new_val; return( new_val ); }