mirror of
https://github.com/ksherlock/gopher.git
synced 2024-12-26 21:30:12 +00:00
414 lines
7.5 KiB
Ruby
414 lines
7.5 KiB
Ruby
#!/bin/env ruby -w
|
|
|
|
require 'erb'
|
|
require 'optparse'
|
|
|
|
class Option
|
|
|
|
attr_reader :name
|
|
attr_reader :description
|
|
attr_reader :argument # nil, :, =, ?
|
|
|
|
|
|
# name of the field, in the struct
|
|
# will be null if virtual.
|
|
attr_reader :field
|
|
|
|
# array of custom code.
|
|
attr_accessor :code
|
|
|
|
attr_reader :modifiers
|
|
|
|
|
|
def _fieldName(name, modifiers)
|
|
return nil if modifiers[:virtual]
|
|
return modifiers[:field] if modifiers[:field]
|
|
return "_#{name}"
|
|
end
|
|
|
|
def initialize(name, argument, modifiers)
|
|
@code = []
|
|
|
|
modifiers ||= {}
|
|
@name = name
|
|
@field = _fieldName(name, modifiers)
|
|
@argument = argument
|
|
@modifiers = modifiers
|
|
end
|
|
|
|
def to_s()
|
|
return "Option: #{description}#{argument} #{field}"
|
|
end
|
|
|
|
# def addCode(x)
|
|
# @code.push(x)
|
|
# end
|
|
|
|
# def finishCode
|
|
# # remove all blank lines at the end
|
|
# while @code.length && @code.last =~ /^\s*$/
|
|
# @code.pop
|
|
# end
|
|
# end
|
|
|
|
end
|
|
|
|
class ShortOption < Option
|
|
def initialize(name, argument, modifiers)
|
|
super(name, argument, modifiers)
|
|
|
|
@description = "-#{name}"
|
|
end
|
|
|
|
|
|
def generateCase()
|
|
|
|
rv = []
|
|
indent = " " * 8
|
|
rv.push indent, "case '#{@name}':\n"
|
|
indent += " "
|
|
|
|
if (@argument)
|
|
#print indent, "GETOPTARG(#{@description})\n"
|
|
rv.push indent, "++j;\n"
|
|
rv.push indent, "if (cp[j]) {\n"
|
|
rv.push indent, " optarg = cp + j;\n"
|
|
rv.push indent, "} else {\n"
|
|
rv.push indent, " ++i;\n"
|
|
rv.push indent, " if (i < argc) optarg = argv[i];\n"
|
|
rv.push indent, "}\n"
|
|
rv.push indent, "if (!optarg) {\n"
|
|
rv.push indent, " fputs(\"#{@description} requires an argument\\n\", stderr);\n"
|
|
rv.push indent, " exit(1);\n"
|
|
rv.push indent, "}\n"
|
|
end
|
|
|
|
if @field
|
|
if @argument
|
|
rv.push indent, "options->#{@field} = optarg;\n"
|
|
else
|
|
if @modifiers[:increment]
|
|
rv.push indent, "options->#{@field} += 1;\n"
|
|
else
|
|
rv.push indent, "options->#{@field} = 1;\n"
|
|
end
|
|
end
|
|
end
|
|
|
|
@code.each {|x| rv.push indent, x }
|
|
rv.push indent,"break;\n"
|
|
|
|
end
|
|
|
|
def generateField()
|
|
# generate the field for the struct.
|
|
|
|
return "" unless @field
|
|
return " char *#{@field};\n" if @argument
|
|
return " unsigned #{@field};\n" if @modifiers[:increment]
|
|
return " unsigned #{@field}:1;\n"
|
|
end
|
|
end
|
|
|
|
|
|
def arrayTrim(x)
|
|
while x.length && x.last =~ /^\s*$/
|
|
x.pop
|
|
end
|
|
x
|
|
end
|
|
|
|
def parseModifiers(string)
|
|
|
|
# splits a key=value (, key=value)* string
|
|
# key is equivalent to key={true}
|
|
rv = {}
|
|
return rv unless string
|
|
args = string.split(',')
|
|
args.each{|x|
|
|
x.strip!
|
|
next unless x.length
|
|
|
|
key, value = x.split('=')
|
|
|
|
value ||= true # key == key={true}
|
|
key = key.intern
|
|
|
|
rv[key] = value
|
|
}
|
|
|
|
return rv;
|
|
end
|
|
|
|
|
|
@shortRE = /
|
|
^
|
|
-([\w]) # 1. letter
|
|
([:])? # 2. argument type?
|
|
\s*
|
|
(?:\[
|
|
([\w\s,=]*) # 3. modifiers?
|
|
\])?
|
|
\s*
|
|
(->)? # 4. code?
|
|
$
|
|
/x
|
|
|
|
@longRE = /
|
|
^
|
|
--([\w-]*) # 1. name
|
|
([=?])? # 2. argument type?
|
|
\s*
|
|
(?:\[
|
|
([\w\s,=]*) # 3. modifiers?
|
|
\])?
|
|
\s*
|
|
(->)? # 4. code?
|
|
$
|
|
/x
|
|
|
|
@headerTemplate = <<EOD
|
|
#ifndef __<%= config[:prefix] %>Options__
|
|
#define __<%= config[:prefix] %>Options__
|
|
|
|
typedef struct <%= config[:prefix] %>Options
|
|
{
|
|
<%= (config[:extra_fields] || []).join() %>
|
|
<%= (options.map {|x| x.generateField }).join() %>
|
|
} <%= config[:prefix] %>Options;
|
|
|
|
int Get<%= config[:prefix] %>Options(int argc, char **argv,
|
|
<%= config[:prefix] %>Options *options);
|
|
#endif
|
|
EOD
|
|
#
|
|
|
|
|
|
def stripExtension(path)
|
|
return $1 if path =~ /^(.*?)\.([^.\/]*)$/
|
|
return path
|
|
end
|
|
|
|
def process(infile, keepName)
|
|
|
|
opt = nil # current option being processed
|
|
tmp = [] # code block array
|
|
callback = nil # callback when code is finished.
|
|
|
|
|
|
options = [] # list of options
|
|
config = {}
|
|
|
|
infile.each_line { |line|
|
|
|
|
line.chomp!
|
|
|
|
line.rstrip!
|
|
|
|
if callback
|
|
if line == "" || line =~ /^\s+/
|
|
tmp.push(line + "\n")
|
|
next
|
|
end
|
|
# store...
|
|
|
|
callback.call(arrayTrim(tmp));
|
|
callback = nil
|
|
tmp = []
|
|
code = false
|
|
end
|
|
|
|
next if line =~ /^\s*#/
|
|
|
|
if line =~ /^%/
|
|
|
|
if m = line.match(/^%(\w+)=(\w+)$/)
|
|
key = m[1].intern
|
|
config[key] = m[2]
|
|
next
|
|
end
|
|
|
|
if m = line.match(/^%(\w+)$/)
|
|
key = m[1].intern
|
|
config[key] = true
|
|
next
|
|
end
|
|
|
|
if m = line.match(/^%(\w+)\s*->$/)
|
|
key = m[1].intern
|
|
callback = Proc.new {|code| config[key] = code }
|
|
|
|
next
|
|
end
|
|
|
|
$stderr.puts "Not supported: #{line}"
|
|
next
|
|
end
|
|
|
|
if m = line.match(@shortRE)
|
|
tmp = []
|
|
flag = m[1]
|
|
arg = m[2]
|
|
modifiers = m[3]
|
|
modifiers = parseModifiers(modifiers)
|
|
opt = ShortOption.new(flag, arg, modifiers)
|
|
options.push(opt)
|
|
|
|
# any code?
|
|
if m[4]
|
|
callback = Proc.new {|code| opt.code = code }
|
|
end
|
|
|
|
end
|
|
|
|
|
|
#
|
|
}
|
|
|
|
# if it ended within a code block...
|
|
if callback
|
|
callback.call(arrayTrim(tmp))
|
|
callback = nil
|
|
tmp = []
|
|
end
|
|
|
|
options.sort! {|a, b| a.name <=> b.name }
|
|
|
|
|
|
keepName = nil if keepName == ""
|
|
keepBase = stripExtension(keepName)
|
|
|
|
headerName = (config[:prefix] || '') + 'Options.h'
|
|
headerName = keepBase + '.h' if keepBase
|
|
|
|
io = $stdout
|
|
io = File.open(keepName, "w") if keepName
|
|
|
|
b = binding
|
|
erb = ERB.new(DATA.read(), 0, "%<>")
|
|
|
|
io.write(erb.result(b))
|
|
io.close unless io == $stdout
|
|
|
|
|
|
# header file.
|
|
io = $stdout
|
|
io = File.open(headerName, "w") if headerName && keepName
|
|
erb = ERB.new(@headerTemplate, 0, "%<>")
|
|
io.write(erb.result(b))
|
|
io.close unless io == $stdout
|
|
|
|
end
|
|
|
|
keepFile = nil
|
|
|
|
op = OptionParser.new {|opts|
|
|
opts.banner = "Usage: options.rb [options] [infile]"
|
|
|
|
opts.on("-o", "--keep OUTFILE", "Specify output file name") do |filename|
|
|
keepFile = filename
|
|
end
|
|
}
|
|
|
|
op.parse!
|
|
|
|
|
|
keepFile = nil if keepFile == ""
|
|
|
|
case ARGV.length
|
|
when 0
|
|
process($stdin, keepFile)
|
|
when 1
|
|
file = ARGV[0]
|
|
io = $stdin
|
|
if file != '-'
|
|
keepFile = stripExtension(file) + '.c' if keepFile == nil
|
|
io = File.open(file, "r")
|
|
end
|
|
process(io, keepFile)
|
|
io.close unless io == $stdin
|
|
else
|
|
op.help
|
|
exit(1)
|
|
end
|
|
|
|
|
|
|
|
exit(0)
|
|
|
|
__END__
|
|
#ifdef __ORCAC__
|
|
#pragma optimize 79
|
|
#pragma noroot
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "<%= File.basename(headerName) %>"
|
|
|
|
<%= (config[:extra_includes] || []).join() %>
|
|
|
|
int Get<%= config[:prefix] %>Options(int argc, char **argv,
|
|
struct <%= config[:prefix] %>Options *options)
|
|
{
|
|
int i, j;
|
|
int eof = 0;
|
|
int mindex = 1; /* mutation index */
|
|
|
|
for (i = 1; i < argc; ++i)
|
|
{
|
|
char *cp = argv[i];
|
|
char c = cp[0];
|
|
|
|
<% if config[:posixly_correct] %>
|
|
// stop processing at first non-opt.
|
|
if (c != '-')
|
|
{
|
|
eof = 1;
|
|
if (i == mindex) return argc;
|
|
argv[mindex] = argv[i];
|
|
++mindex;
|
|
continue;
|
|
}
|
|
<% end %>
|
|
|
|
if (eof || c != '-')
|
|
{
|
|
if (mindex != i) argv[mindex] = argv[i];
|
|
++mindex;
|
|
continue;
|
|
}
|
|
|
|
// long opt check would go here...
|
|
if (cp[1] == '-' && cp[2] == 0)
|
|
{
|
|
eof = 1;
|
|
continue;
|
|
}
|
|
|
|
// special case for '-'
|
|
j = 0;
|
|
if (cp[1] != 0) j = 1;
|
|
|
|
for (; ; ++j)
|
|
{
|
|
char *optarg = 0;
|
|
|
|
c = cp[j];
|
|
if (!c) break;
|
|
switch(c)
|
|
{
|
|
<%= options.map{|o| o.generateCase() }.join() %>
|
|
default:
|
|
fprintf(stderr, "-%c : invalid option\n", c);
|
|
exit(1);
|
|
break;
|
|
}
|
|
// could optimize out if no options have flags.
|
|
if (optarg) break;
|
|
}
|
|
}
|
|
return mindex;
|
|
}
|