#!/usr/bin/awk -f # This software is under the GPL # inljoin 0.1: this script performs the reverse of inlsplit # Usage: inljoin [file2.cc file2.h ...] # # This is a script to merge a C++ implementation and interface into a # single .inl file. It takes as input a .cc and .h file, and yields a .inl # file. # What is an .inl file? I'm not sure because I stole the extension, but # for the purposes of this program, it is basically a .cc and .h joined # into 1 file. Functions whose definition belong in the .cc file are # denoted by suffixing a "@" to the prototype. The declaration will then # be put in the .h file, and the definition in the .cc file. An optional # @source directive specifies that all that follows should go in the .cc # file only. # # Example Class.inl file: # class Class { # int func1(char c)@ # { return b; } # int func2stayinline(int b) # { return b; } # }; # @source # #include "class.h" # # func1's definition will be placed in the .cc file, while # func2stayinline's definition stays in the .h file. BEGIN { progname = "inljoin" extension = ".inl" outfile = "" cfile_regex = "\.(c|cc|cpp|cxx|C)$" hfile_regex = "\.(h|hh|hpp|hxx|H)$" sort_args() } # make sure each .cc file comes before its .h file # easily done by putting all .cc files before all .h files function sort_args( i, ci, cfiles, hi, hfiles) { ci = 1; hi = 1; for (i=1; i < ARGC; i++) { if (ARGV[i] ~ cfile_regex) cfiles[ci++] = ARGV[i]; else if (ARGV[i] ~ hfile_regex) hfiles[hi++] = ARGV[i]; else { print ARGV[i] ": error: files must have a C++ source or header suffix" exit 1 } } i=1 for (ci in cfiles) ARGV[i++] = cfiles[ci] for (hi in hfiles) ARGV[i++] = hfiles[hi] } # we keep track of function bodies with a 3d array. they're spit out again # when we read the header file and reach the prototype. # special case: functions[file, "", ""] contains non-class source, and # should go after the @source directive function append_source(str) { functions[outfile, classname, funcname] = \ functions[outfile, classname, funcname] str "\n" } function output(str) { data[outfile] = data[outfile] str "\n" } # if FNR==1 then we started a new file FNR==1 { source = match(FILENAME, cfile_regex); header = !source basename = FILENAME sub(/\.[A-Za-z]+$/, "", basename) outfile = basename extension classname = "" funcname = "" brace = 0 printf("%s: %s -> %s.inl\n", progname, FILENAME, basename) } # get the class name header && /(class|struct)[ \t]+[A-Za-z0-9_]+/ { for (i=1; i <= NF; i++) { if ($i == "class" || $i == "struct") { classname = $(i+1) break } } } # a hackish way to check for a function prototype header && classname != "" && /[A-Za-z0-9_]+[ \t~]*\(.*;[ \t]*$/ { tmp = $0 sub(/;/, "", tmp) match(tmp, /[A-Za-z0-9_]+[ \t]*\(/) funcname = substr(tmp, RSTART, RLENGTH) sub(/[ \t]*\(/, "", funcname) # only mark it @ if it's a valid function if ((outfile, classname, funcname) in functions) { output(tmp "@") output(functions[outfile, classname, funcname]) } else { output($0) } next } # a hackish way to check for a function definition header source && brace==0 && /([A-Za-z0-9_]+[ \t]*::)?[ \t~]*[A-Za-z0-9_]+[ \t]*\(/ { if (match($0, /[A-Za-z0-9_]+[ \t]*::[ \t~]*[A-Za-z0-9_]+/)) { # we have a class::func type deal split(substr($0, RSTART, RLENGTH), names, /[ \t]*::[ \t]*/) classname = names[1] funcname = names[2] } # FIXME: this is an ugly hack. this is supposed to check for a # non-class function header, but it really only checks that it has some # form "funcname(" and that the line begins with a letter. this will # miss on any function that does not start at the beginning of the # line. I do this to prevent matching with comments and things like # " : Class(param)". else if (match($0, /^[A-Za-z_]/)) { classname = "" funcname = "" append_source($0) } else { append_source($0) } next } # keep track of braces so we know when our function ends /{/ || /}/ { tmp = $0 while (tmp ~ /{/) { brace++ sub(/{/, "", tmp) } while (tmp ~ /}/) { brace-- sub(/}/, "", tmp) } } # everything else in the source file, add to the current function source {append_source($0);} # everything else in the header, just print to the .inl file header {output($0)} END { for (file in data) { print data[file] > file print "@source" > file print functions[file, "", ""] > file } }