Skip to content

Commit

Permalink
getopt replacement.
Browse files Browse the repository at this point in the history
  • Loading branch information
ksherlock committed Sep 10, 2012
1 parent ab26d38 commit d1436d4
Show file tree
Hide file tree
Showing 2 changed files with 334 additions and 0 deletions.
313 changes: 313 additions & 0 deletions flags.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
#!/usr/bin/env ruby -w

# process the flags.yaml file
# and generate a flags.h and flags.c file.
#

#
# todo -- support for long-options (--longoption, --longoption=value, etc)
#
#

require 'erb'
require 'yaml'

header_preamble = <<EOF
#ifndef __flags_h__
#define __flags_h__
typedef struct Flags {
EOF

header_postamble = <<EOF
} Flags;
extern struct Flags flags;
int FlagsParse(int argc, char **argv);
void FlagsHelp(void);
#endif
EOF


class Option
@@map = {
# some of these are a bad idea but whatever...
'>' => 'gt',
'<' => 'lt',
',' => 'comma',
'.' => 'period',
'/' => 'forward_slash',
'\\' => 'back_slash',
'?' => 'question',
'|' => 'pipe',
'~' => 'tilde',
'`' => 'grave',
'!' => 'bang',
'@' => 'at',
'#' => 'hash',
'$' => 'dollar',
'%' => 'percent',
'^' => 'caret',
'&' => 'ampersand',
'*' => 'star',
'(' => 'left_paren',
')' => 'right_paren',
'-' => 'minus',
'+' => 'plus',
'=' => 'equal',
'[' => 'left_bracket',
']' => 'right_bracket',
'{' => 'left_brace',
'}' => 'right_brace',
':' => 'colon',
';' => 'semi_colon',
'\'' => 'apostrophe',
'"' => 'quote'
}

def initialize(hash)

@char = hash['char'].to_s
@argument = hash['argument'] || false

@flag_name = hash['flag_name']
@flag_name = @flag_name.to_s if @flag_name

@xor = hash['xor'] || []
@xor = case @xor
when Array
@xor
when Integer, String
[ @xor ]
else
raise "Invalid xor type: #{@xor}"
end

@xor.map! { |x| x.to_s }
end

attr_reader :char, :xor, :argument

def flag_name
return @flag_name if @flagname
return self.class.flag_name(@char)
end

def self.flag_name(char)
return '_' + @@map[char] if @@map[char]
return '_' + char
end


end

# better ARGF.
def argf_each

if ARGV.count > 0

ARGV.each {|file|

File.open(file, "r") {|io|
yield file, io
}
}

else
yield nil, $stdin
end

end


def escape_cstr(x)

# escape a c string

x.gsub(/([\\"])/, "\\\\1")
end


code = ERB.new(DATA.read(), 0, "%<>")

argf_each {|filename, file|


data = YAML.load(file)

help = data['help']
options = data['options']

# options is an array of items which may be hashes, strings, or numbers.
# normalize them.

options = options.map {|opt|

opt = case opt
when String, Integer
{ 'char' => opt }
when Hash
# {'o' => { ... }}
# or
# {'char' => , ... }
if opt['char']
opt
else
opt = opt.first
opt[1].merge({ 'char' => opt[0] })
end
else
raise "Unexpected data type: #{opt}"
end

Option.new(opt)
}

#data[options] = options
# check for help?

basename = filename
basename = $1 if filename && filename =~ /^(.*)./

b = binding # bind help, options for ERB.

io = basename ? File.open(basename + ".c", "w") : $stdout
io.write(code.result(b))

io.close unless io == $stdout


io = basename ? File.open(basename + ".h", "w") : $stdout
io.write(header_preamble)
# two passes - one with arguments, one without.
options.each {|opt|
if opt.argument
io.printf(" char *%s;\n", opt.flag_name)
end
}
io.puts()
options.each {|opt|
if !opt.argument
io.printf(" unsigned %s:1;\n", opt.flag_name)
end
}
io.puts

io.write(header_postamble)
io.close unless io == $stdout


# #puts options.to_yaml
# puts code.result(binding())

}


__END__

#ifdef __ORCAC__
#pragma optimize 79
#pragma noroot
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "flags.h"

void FlagsHelp(void)
{
% help.each do |h|
fputs("<%= escape_cstr(h) %>\n", stdout);
% end
fputs("\n", stdout);
exit(0);
}

int FlagsParse(int argc, char **argv)
{
char *cp;
char c;
int i;
int j;

memset(&flags, 0, sizeof(flags));

for (i = 1; i < argc; ++i)
{
cp = argv[i];
c = cp[0];

if (c != '-')
return i;

// -- = end of options.
if (cp[1] == '-' && cp[2] == 0)
return i + 1;

// now scan all the flags in the string...
for (j = 1; ; ++j)
{
int skip = 0;

c = cp[j];
if (c == 0) break;

switch (c)
{
% if help && !options.find_index {|x| x.char == 'h' }
case 'h':
FlagsHelp();
break;
% end
% #
% options.each do |opt|
case '<%= escape_cstr(opt.char) %>':
% # check for an argument.
% flag_name = 'flags.' + opt.flag_name
% #
% if opt.argument
// -xarg or -x arg
skip = 1;
if (cp[j + 1])
{
<%= flag_name %> = cp + j + 1;
}
else
{
if (++i >= argc)
{
fprintf(stderr, "option requires an argument -- %c\n", c);
return -1;
}
<%= flag_name %> = argv[i];
}
% else # no argument.
<%= flag_name %> = 1;
% end # if no argument.
% #
% # unset any exclusive or values
% opt.xor.each do |xor_opt|
flags.<%= Option.flag_name(xor_opt) %> = 0;
%end
break;
% end # options.each

default:
fprintf(stderr, "illegal option -- %c\n", c);
return -1;
}

if (skip) break;
}
}

return i;
}
21 changes: 21 additions & 0 deletions flags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---

options:
- o: { argument : true, xor: 'O' }
- i
- I
- v
- O
- 0: { xor: '1'}
- 1: { xor: '0' }

help:
- gopher [options] url
- -h display help information
- -V display version information
- -i display http headers
- -I http HEAD
- -O write output to file
- -o <file> write output to <file> instead of stdout
- -0 use HTTP 1.0
- -9 use HTTP 0.9

0 comments on commit d1436d4

Please sign in to comment.