Script to merge C source files

JCW: Here's a script which takes an input file, copies all of it to the output file, but with a twist: the remaining arguments are scanned for files (globbing works, you can also specify an entire dir) and expanded for matching #include <somefile> lines. Though not perfect, it goes a long way for me to merge source files for certain deployment scenarios.

For example, on Unix see what happens with something like:

  cd tcl8.3*/generic/
  onesrc -v tcl.h out_tcl.h .

Note that there's a dot at the end of that second line.


  # onesrc -- merge source file includes into a single one
  # 02/02/2001 [email protected]
 
  set verbose 0
  if {[lindex $argv 0] == "-v"} {
    set verbose 1
    set argv [lrange $argv 1 end]
  }
 
  set inf [lindex $argv 0]
  set outf [lindex $argv 1]
  set paths [lrange $argv 2 end]
 
  if {$outf == "" || ![file exists $inf]} {
    puts stderr "Usage: onesrc ?-v? infile outfile ?paths?"
    exit 1
  }
 
  set ofd [open $outf w]
 
  array set files {}
  foreach x $paths {
    if {[file isdir $x]} {
      set x [file join $x *.*]
    }
    foreach f [glob $x] {
      set files([file tail $f]) $f
    }
  }
 
  set total 0
  array set seen {}
  array set remain {}
 
  proc emit {s {n ""}} {
    puts $::ofd $s
    incr ::total
    if {$n != ""} {
      upvar $n i
      incr i
    }
  }
 
  emit "/* $outf - generated from $inf by onesrc"
  emit " * paths: $paths"
  emit " * [clock format [clock seconds]]"
  emit " */"
  emit ""
 
  proc expand {fn} {
    set fd [open $fn]
    set n 0
    while {[gets $fd line] >= 0} {
      if {[regexp {^\s*#\s*include\s*[<"]([^"]+)[>"]} $line - path]} { ;# "
        set f [file tail $path]
        if {[info exists ::files($f)]} {
          if {[info exists ::seen($f)]} {
            emit "/* skipped: $path - see $::seen($f) */" n
          } else {
            emit "/* include: $path */" n
            set ::seen($f) $fn
            expand $::files($f)
            emit "/* resumed: $fn */" n
          }
          continue
        }
        lappend ::remain($f) $fn
      }
      emit $line n
    }
    if {$::verbose} {
      puts [format "%6d\t%s" $n $fn]
    }
  }
 
  if {$verbose} {
    puts "files merged:"
  }
 
  expand $inf
 
  if {$::verbose} {
    puts "includes left:"
    foreach f [lsort [array names remain]] {
      puts "  $f"
    }
  }
 
  set i [array size seen]
  puts "$outf: $total lines, [incr i] files merged,\
                          [array size remain] includes left"