#!/bin/csh -f #----------------------------------------------------------------------- # cmunge: a tool for encrypting and compacting C source code. # # Usage: cmunge [options] files... # # Options: # -I include-dir -- Search directory `include-dir' for #include files. # -d output-dir -- Write the output file(s) to directory # `output-dir'. (Default: current directory.) # -f output-file-rootname # -- Output files have names starting with the # string `output-file-rootname' followed by ".c" # if there is one output file, or "1.c", "2.c", # etc, if there is more than one. E.g. `-f f' # causes the output files to be named "f.c" # or "f1.c", "f2.c", etc. (Default: f). # -l min-output-linelen # -- Make lines of the output file(s) at least # `min-output-linelen' characters long. # (Default: 90.) # -p prefix-letter -- Give the translated C identifiers names # consisting of the letter `prefix-letter' # followed by a number. E.g. `-p l' causes # the translated identifiers to be called # l1, l2, etc. (Default: l). # -v version-string -- Insert string `version-string' as a C comment # in the first line of each output file. # (Default: no comment is written.) # - -- Other arguments are passed directly to the # C pre-processor, `cpp'. E.g. argument # `-Dname=def' is passed directly to `cpp'. # files... -- The input C source file(s) to be `munged'. # # Spaces are optional between option letters and their accompanying arguments. # # 'cmunge' calls 2 programs, 'proc-incl' and 'ctran', whose arguments are: # # proc-incl [-I include-dir]... [-D include-dict-file] [file] # ctran [-D id-dict-file] [-f output-file-rootname] # [-l min-output-linelen] [-p prefix-letter] [-v version-string] # file... # # with an intervening call to 'cpp', the C-preprocessor. # # # Author: John Merlin, Dept of Electronics & Comp Sci, Univ of Southampton. # Developed: Dec 1993. # Released on WWW: Aug 1998. # This software can be downloaded from URL: # http://www.vcpc.univie.ac.at/~jhm/cmunge/. # Present contact details: # John Merlin, VCPC, University of Vienna. # Email: jhm@vcpc.univie.ac.at # #----------------------------------------------------------------------- # ** STILL TO DO: # -- get script to check exit status of 'proc-incl' & 'ctran', and # exit & tidy-up if != 0. # -- in o/p info (at end) only o/p stuff about user & system incl files # if there are any; otherwise say, e.g. "No user-defined include files.". # -- try to limit number of vars -- unset as soon as poss. # -- abbreviate the info! # -- get it to work with dir names, rootnames, etc, with blanks, #----------------------------------------------------------------------- #===============================================================# # Substitute the absolute pathname of `cmunge's `bin' directory # # (containing the programs `ctran' and `proc-incl') here... # # # # N.B. If things haven't been moved around, this is # # ${cmunger}/bin, where ${cmunger} is the absolute pathname # # of the `cmunger' directory. # #===============================================================# set cmunger_bin_dir = /vcpc_users/jhm/bin/cmunger/bin #===============================================================# # Substitute the pathname of the C pre-processor, `cpp', here...# #===============================================================# set cpp = /usr/ccs/lib/cpp set proc_incl = ${cmunger_bin_dir}/proc-incl set ctran = ${cmunger_bin_dir}/ctran set nonomatch # ...so can use 'tmp_*' to find if there are "tmp_" files #---------------# # defaults... # #---------------# set outdir = set outroot = f #----------------------------------------# # process command options (starting '-') # #----------------------------------------# set proc_incl_args = () set cpp_args = () set ctran_args = () if (! $#argv) goto no_input_files while ("$argv[1]" =~ -*) switch ($argv[1]) case -I*: #------------------------------------------------# # -I include-dir -- passed to 'proc-incl' # #------------------------------------------------# set proc_incl_args = ($proc_incl_args $argv[1]) if ("$argv[1]" == "-I") then shift if (! $#argv) then echo "cmunge: expected directory name after '-I'" goto usage endif set proc_incl_args = ($proc_incl_args "$argv[1]") endif breaksw case -d*: #-------------------------------------------------------# # -d output-dir -- used here ($outdir), and appended # # to the front of '$outroot' (after all the options # # have been read). # #-------------------------------------------------------# if ("$argv[1]" == "-d") then shift if (! $#argv) then echo "cmunge: expected directory name after '-d'" goto usage endif set outdir = "$argv[1]" else set outdir = `sed 's/^-d//' < 1) then set munged_files = "${outroot}1.c - ${outroot}${#argv}.c" else set munged_files = "${outroot}.c" endif set outroot = "${outdir}$outroot" set ctran_args = ($ctran_args -f "$outroot") # always pass in, in case # '$outdir' appended. #-----------------------------------------------------------------# # Check that there are no 'tmp_' files that might be overwritten! # #-----------------------------------------------------------------# set tmp_files = "${outdir}"tmp_* if ( $#tmp_files > 1 || "$tmp_files" != "${outdir}tmp_*" ) then echo -n "cmunge: please delete or rename the files" if ("$outdir" != "") echo -n "in '${outdir}'" echo "whose names start 'tmp_'" exit (1) endif unset tmp_files #---------------------------------------------------------------# # delete other special files that may remain from a previous # # run of 'cmunge'. # #---------------------------------------------------------------# set file_dict = "${outdir}File.dict" set id2val = "${outdir}Id2val" set val2id = "${outdir}Val2id" if (-e "$file_dict") then echo rm $file_dict; rm "$file_dict" endif if (-e "$id2val") then echo rm $id2val; rm "$id2val" endif if (-e "$val2id") then echo rm $val2id; rm "$val2id" endif set tmp_file = "${outdir}tmp_file" # general purpose temp file! set incl_dict = "${outdir}tmp_incl_dict" if ("$outdir" != "") set proc_incl_args = ($proc_incl_args -D "$incl_dict") #------------------------------------------------------------------- # Pass the 'input files' through filter 'proc-incl', which: # -- replaces each comment by ' ' and splices '\'-terminated lines; # -- in: #include <...> # #include token-sequence # #include "filename" -- 'filename' in "/usr/include" # replaces "#include" by "@" (to protect the 'include' file from # replacement by 'cpp' later on); # -- in: #include "filename" -- 'filename' *not* in "/usr/include" # replaces 'filename' by a new temporary filename ("tmp_h.."). These # files (assumed to be 'user-defined') will be 'expanded' by 'cpp' later. # -- does some simple textual reduction (e.g. replacing each sequence # of whitespace by a single ' '). # # The output is written to temporary files called "tmp_c..". # File 'tmp_incl_dict' is created to store the replacement filenames # for 'include' files (or rather their numeric suffixes, since they # all start "tmp_h.."). # # N.B. We use a 'while' rather than a 'foreach' loop in case any of # the filenames in '$argv' contain embedded whitespace! #------------------------------------------------------------------- echo "" echo "Processing '#include' directives in source files..." set tmp_c_files = () set final_files = () set num = 0 set i = 1 while ($i <= $#argv) set c_file = "$argv[$i]"; @ i++ @ num++ set out_file = "${outdir}tmp_c$num" set tmp_c_files = ($tmp_c_files "$out_file") if ($#argv > 1) then cat >> "$file_dict" <> "$file_dict" < "$out_file" end echo -n "Continue (y/n) ?: " switch ($<) case n: exit (0); endsw #------------------------------------------------------------------- # Now file 'tmp_incl_dict' contains a list of user-defined # files that are included by the C files. These files are processed # as above for the C files. # This may generate further new 'include' files, which are listed # in 'tmp_incl_dict', so this procedure is iterated until no more new # 'include' files are found. # N.B. The following 'set incl_files' command should be robust even # for 'include' filenames containing whitespace!! (Aren't I clever!!) #------------------------------------------------------------------- echo "" echo "Processing '#include' directives in user-defined 'include' files..." set incl_files = "`grep '^u' $incl_dict | sed 's/^u //'`" set num = 0 while ($#incl_files) #-------------------------------------------------------# # change 'u' -> 'p' in the records of '$incl_dict', as # # the files therein are now being 'processed'... # #-------------------------------------------------------# sed 's/^u/p/' "$incl_dict" > "$tmp_file"; mv "$tmp_file" "$incl_dict" #-------------------------------------------------------# # use 'while' rather than 'foreach' in case a filename # # in 'incl_files' contain embedded whitespace!... # #-------------------------------------------------------# set i = 1 while ($i <= $#incl_files) set incl_file = "$incl_files[$i]"; @ i++ @ num++ set out_file = "${outdir}tmp_h$num" echo " '$incl_file' -- output to '$out_file'" $proc_incl $proc_incl_args "$incl_file" > "$out_file" end set incl_files = "`grep '^u' $incl_dict | sed 's/^u //'`" echo -n "Continue (y/n) ?: " switch ($<) case n: exit (0); endsw end #---------------------------------------------------------------# # Pass input files through C preprocessor # # # # N.B. As before, we use a 'while' rather than a 'foreach' loop # # in case any of the filenames in '$tmp_c_files' contain # # embedded whitespace! # #---------------------------------------------------------------# echo "" echo "Passing 'tmp_c..' files through C preprocessor (which 'expands' above 'include' files):" set i = 1 while ($i <= $#tmp_c_files) set c_file = "$tmp_c_files[$i]"; @ i++ if ($cpp_args == "") then echo " $cpp -P $c_file > $tmp_file; mv $tmp_file $c_file" $cpp -P "$c_file" > "$tmp_file"; mv "$tmp_file" "$c_file" else echo " $cpp -P $cpp_args $c_file > $tmp_file; mv $tmp_file $c_file" $cpp -P "$cpp_args" "$c_file" > "$tmp_file"; mv "$tmp_file" "$c_file" endif end echo -n "Continue (y/n) ?: " switch ($<) case n: exit (0); endsw #-------------------------------# # Munge C files (using 'ctran') # #-------------------------------# set id_dict = "${outdir}Id.dict" if ("$outdir" != "") set ctran_args = ($ctran_args -D "$id_dict") echo "" echo "'Munging' 'tmp_c..' files..." if ($?version_str) then echo " ctran" $ctran_args -v \""$version_str"\" $tmp_c_files $ctran $ctran_args -v "$version_str" $tmp_c_files else echo " ctran" $ctran_args $tmp_c_files $ctran $ctran_args $tmp_c_files endif echo -n "Continue (y/n) ?: " switch ($<) case n: exit (0); endsw #-------------------------------------------------------# # Make sorted dictionary files, 'Id2val' and 'Val2id' # #-------------------------------------------------------# echo "" echo "Munging complete." echo "" echo "Making sorted ID dictionaries '$id2val' and '$val2id'." sed 1d "$id_dict" | sed '/^$/d' | sort +1 -df -o "$id2val" sed 1d "$id_dict" | sed '/^$/d' | sort -n -o "$val2id" #-------------------------------------------------# # Write general information to file 'Cmunge.info' # #-------------------------------------------------# cat > ${outdir}Cmunge.readme <; or -- put an '@' character at the start of the relevant '#include' lines, which will make 'cmunge' copy them out unchanged (as described in the final section). -- Delete the file 'Id.dict' (generated by 'cmunge') and the 'munged' output files. -- Re-run 'cmunge'. The following 'include' files: Unexpanded include files: ------------------------- `grep -h '^#include' $final_files | sed 's/^#include[ ]*/ /' | sort -u` *aren't* expanded in the 'munged' C source code, i.e. their '#include' directives are preserved. 'cmunge' contains a dictionary of the 'standard library' identifiers defined in Appendix B of Kernighan & Ritchie's 'ANSI C' -- basically, those in the following files: Standard library include files: ------------------------------- These identifiers aren't renamed in the 'munged' output and hence remain consistent with the 'standard library'. If all of the above 'unexpanded include files' are in the list of 'standard library include files', and no use is made of external functions from 'system' libraries that are not part of the 'standard library', then all should be OK, so skip the rest of this message. By default, all other identifiers in the C source code are renamed in the 'munged' output. Therefore, if any of the 'unexpanded include files' are *not* in the 'standard library' list, or any external functions are used from a 'system' library that is not part of the 'standard library', 'cmunge' will rename the corresponding identifiers in the source code, thus making them inconsistent with the names in the 'system' files or libraries. Two possible remedies are: (i) If any of the 'unexpanded include files' are user-defined rather than 'system' files, then: -- Change their filename delimiters in the '#include' statements from <...> to "...", or remove the initial '@' from their '#include' lines (if '@' was added to protect the lines from being 'munged' as indicated earlier). -- Delete the 'munged' output files. -- Re-run 'cmunge'. This will cause those 'include' files to be expanded in the 'munged' source code. -- OR -- (ii) Prevent renaming of the identifiers concerned as follows: -- Find the improperly renamed identifiers. (Perhaps the easiest way to do this is to compile the 'munged' output to find which identifiers aren't declared or defined, using file 'Val2id' to translate from new to old names). -- In file 'Id.dict', find the entry for each identifier concerned, and replace its first field (a number) by '-'. -- Delete the 'munged' output files. -- Re-run 'cmunge'. N.B. When 'cmunge' is re-run ignore this warning (as names already marked with '-' in 'Id.dict' aren't renamed). How to protect lines of the original source code from being 'munged' =================================================================== If is possible to protect any line of the source code (including blank lines, comments and pre-processor directives) from being 'munged' by prefixing an '@' character as the first character on the line. This will cause 'cmunge' to copy out the line unchanged, apart from removing its initial '@' character. One possible use of this is to preserve conditional compilation directives in the 'munged' output, e.g. lines like: #ifdef __STDC___ ... #else ... #endif If 'protected' lines contain identifiers that also appear in 'unprotected' lines (i.e. lines that will be 'munged'), then it is necessary to prevent those identifiers from being renamed in the 'munged' lines. This can be done by modifying their entries in the file 'Id.dict' as described in (ii) above, and re-running 'cmunge'. INFO #---------------------------------------------------------------# # Cleanup and exit! # # N.B. We know that there actually *are* some 'tmp_.." files # # (e.g. 'tmp_file', 'tmp_incl_dict' and some 'tmp_c' files). # # If there weren't, 'tmp_*' would just expand to itself and # # 'rm' would give an errmsg! # #---------------------------------------------------------------# echo "" echo "rm ${outdir}tmp_*" rm ${outdir}tmp_* echo "" echo "*****************************************************************************" echo "* SEE FILE '${outdir}Cmunge.readme' FOR IMPORTANT INFORMATION ABOUT THIS RUN" echo "*****************************************************************************" exit (0) #-----------------# # error messages # #-----------------# no_input_files: echo 'cmunge: no input files specified' goto usage usage: echo "" echo "Usage: cmunge [options] files..." echo "" echo "Options:" echo " -I include-dir -- Search directory 'include-dir' for #include files." echo " -d output-dir -- Write the output file(s) to directory" echo " 'output-dir'. (Default: current directory.)" echo " -f output-file-rootname -- Output files have names starting with the" echo " string 'output-file-rootname' followed by '.c'" echo " if there is one output file, or '1.c', '2.c'," echo " etc, if there is more than one. (Default: f)." echo " -l min-output-linelen -- Make lines of the output file(s) at least" echo " 'min-output-linelen' characters long." echo " (Default: 90.)" echo " -p prefix-letter -- Give the translated C identifiers names" echo " consisting of the letter 'prefix-letter'" echo " followed by a number. (Default: l)." echo " -v version-string -- Insert string 'version-string' as a C comment" echo " in the first line of each output file." echo " (Default: no comment is written.)" echo " - -- Other arguments are passed directly to the" echo " C pre-processor, 'cpp'." echo " files... -- The input C source file(s) to be 'munged'." echo "" echo "Spaces are optional between option letters and their accompanying arguments." echo "" # echo "usage: cmunge [-I include-dir]... [-d output-dir] [-f output-file-rootname]" # echo " [-l min-output-linelen] [-p prefix-letter] [-v version-string]" # echo " [-] files..." exit (1)