Skip to content

Instantly share code, notes, and snippets.

@AlanQuatermain
Created January 6, 2013 17:27
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlanQuatermain/4468801 to your computer and use it in GitHub Desktop.
Save AlanQuatermain/4468801 to your computer and use it in GitHub Desktop.
I needed to go through a large number of Xcode-generated source-code files the other day, replacing the copyright line with a license header statement. Since there were lots of files in a nested structure I decided to use a script, and I added in a glob pattern to exclude certain files/folders— because there was some public domain source in ther…
#!/usr/bin/ruby
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License can be found at
# <http://www.gnu.org/licenses/gpl-3.0.html>.
require 'optparse'
require 'fileutils'
require 'rubygems'
@@Verbose = 0
@@DryRun = false
@@Exclude = ''
@@Template = nil
OptionParser.new do |opts|
opts.banner = "Usage: ChangeLicenses [OPTIONS] [-e exclusion_regex] [-c copyright_owner] -t template_file"
opts.separator ""
opts.separator "Scans recursively through the current folder (using an optional exclusion glob pattern) to"
opts.separator "locate all C/C++ header and source files, and then modifies their Xcode-style source"
opts.separator "headers to include the text from a named input file or stdin."
opts.separator ""
opts.separator "The appended text does not need to be prefixed with commenting characters; the program"
opts.separator "will do that for you, matching Xcode's standard double-spaced indents."
opts.separator ""
opts.separator "Options:"
opts.on_tail( "-h", "--help", "Show this message.", :NONE) do
puts opts
exit
end
opts.on("-v", "--verbose",
"Print information about the program's operation. Specify",
"up to three times, with each one increasing the amount",
"of data output.", :NONE) do
@@Verbose += 1
end
opts.on("-d", "--dry-run", "Don't modify any files. Best used",
"with -vvv which will print out all new files. Optional.", :NONE) do |v|
@@DryRun = v
end
opts.on("-e", "--exclude", "Specifies a glob pattern. All files",
"whose sub-paths are matched by the pattern are ignored.",
"Optional.", :REQUIRED) do |v|
@@Exclude = v unless v == ''
end
opts.on("-t", "--template", "Specifies the path to a plain-",
"text file containing the template for the text to",
"insert into the document's leading header. If not",
"specified, the template text is read from stdin.", :REQUIRED) do |v|
fail "Error: File '#{arg}' not found." unless File.exists?(v)
fail "Error: File '#{arg}' unreadable." unless File.readable?(v)
@@Template = File.read(v, nil)
end
end.parse!
if @@Template.nil?
@@Template = STDIN.read
end
fail "Error: No template supplied." if @@Template == ''
@@CopyrightLineRegex = %r{^(// Copyright \(c\) \d+ ).*?(\. All rights reserved.)$}
# glob ALL THE THINGS
Dir.glob("**/*.{h,c,cpp}") do |filename|
# skip anything matched by the exclusion glob
if File.fnmatch(@@Exclude, filename) # File.fnmatch('something', '') always returns false
puts "* Ignoring #{filename}" if @@Verbose > 0
next
end
puts "#{filename}" if @@Verbose > 0
tmpname = "#{filename}.tmp"
File.open(tmpname, "w") do |tmpfile|
# after writing the copyright/license block, we ignore all further comments lines
# until we see a non-comment line
skipCommentLines = false
File.foreach(filename) do |line|
if line =~ @@CopyrightLineRegex
# this is the copyright line, which will be replaced with the license
# the license is expected to contain a copyright line already
# For each line of the license template, prefix it and write it out
@@Template.each_line do |template_line|
tmpfile.puts "// #{template_line}"
end
tmpfile.puts "//"
# skip any non-empty comment lines now
skipCommentLines = true
elsif line !~ %r{^//} || !skipCommentLines
skipCommentLines = false
# all remaining lines are output verbatim
tmpfile.puts line
end
end
end
if @@Verbose > 2
File.foreach(tmpname) do |line|
puts line
end
end
unless @@DryRun
# move the original file to the side, and move the new file in, then delete old file
oldname = "#{filename}.old"
FileUtils.mv(filename, oldname)
FileUtils.mv(tmpname, filename)
FileUtils.rm(oldname)
else
# dry-run mode: delete the temp file
FileUtils.rm(tmpname)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment