optgen 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/optgen +729 -0
- data/lib/liboptgen.rb +1 -0
- metadata +48 -0
data/bin/optgen
ADDED
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2015-2016 Jyri J. Virkki <jyri@virkki.com>
|
|
5
|
+
#
|
|
6
|
+
# This file is part of optgen.
|
|
7
|
+
#
|
|
8
|
+
# optgen is free software: you can redistribute it and/or modify it
|
|
9
|
+
# under the terms of the GNU General Public License as published by
|
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
# (at your option) any later version.
|
|
12
|
+
#
|
|
13
|
+
# optgen is distributed in the hope that it will be useful, but
|
|
14
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
16
|
+
# General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the GNU General Public License
|
|
19
|
+
# along with optgen. If not, see <http://www.gnu.org/licenses/>.
|
|
20
|
+
#
|
|
21
|
+
|
|
22
|
+
$VERSION = '0.3
|
|
23
|
+
'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#-----------------------------------------------------------------------------
|
|
27
|
+
# Represents one option as read from the config file.
|
|
28
|
+
#
|
|
29
|
+
class OneOption
|
|
30
|
+
|
|
31
|
+
# type of option (required, optional, hidden)
|
|
32
|
+
attr_reader :option_type
|
|
33
|
+
|
|
34
|
+
# the name of this option (normalized from longarg)
|
|
35
|
+
attr_reader :name
|
|
36
|
+
|
|
37
|
+
# single-character form of this option (may be nil, is not required)
|
|
38
|
+
attr_reader :shortarg
|
|
39
|
+
|
|
40
|
+
# long form of this option (required, always present)
|
|
41
|
+
attr_reader :longarg
|
|
42
|
+
|
|
43
|
+
# parameter list needed by this option
|
|
44
|
+
attr_reader :paramlist
|
|
45
|
+
|
|
46
|
+
# callback function to call when this option processed
|
|
47
|
+
attr_reader :callback
|
|
48
|
+
|
|
49
|
+
# human readable documentation as provided in config file
|
|
50
|
+
attr_reader :description
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
#---------------------------------------------------------------------------
|
|
54
|
+
# Parse option line from config to create self.
|
|
55
|
+
#
|
|
56
|
+
def initialize(line, vars)
|
|
57
|
+
|
|
58
|
+
# First perform variable expansion
|
|
59
|
+
if (line =~ /\$\$\$(\w+)\$\$\$/)
|
|
60
|
+
name = $1
|
|
61
|
+
value = vars[name]
|
|
62
|
+
if (!value)
|
|
63
|
+
puts "error: #{name} undefined"
|
|
64
|
+
exit(1)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
line.sub!(/\$\$\$#{name}\$\$\$/, value)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
line =~ /(.):(.*):(.*):(.*):(.*)/;
|
|
71
|
+
|
|
72
|
+
case $1
|
|
73
|
+
when "R"
|
|
74
|
+
@option_type = "required"
|
|
75
|
+
when "O"
|
|
76
|
+
@option_type = "optional"
|
|
77
|
+
when "H"
|
|
78
|
+
@option_type = "hidden"
|
|
79
|
+
else
|
|
80
|
+
puts "error: option type '#{$1}' unknown"
|
|
81
|
+
exit(1)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
arglist = $2
|
|
85
|
+
params = $3
|
|
86
|
+
@callback = $4
|
|
87
|
+
@description = $5
|
|
88
|
+
|
|
89
|
+
(@shortarg, @longarg) = arglist.split(',') if (arglist)
|
|
90
|
+
@shortarg = nil if (@shortarg == "")
|
|
91
|
+
|
|
92
|
+
if (@longarg == nil || @longarg == "")
|
|
93
|
+
puts "error: long arg name always required"
|
|
94
|
+
exit(1)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
@paramlist = nil
|
|
98
|
+
@paramlist = params.split(',') if (params.length > 0)
|
|
99
|
+
@name = @longarg.gsub(/-/, '_')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def display_len
|
|
103
|
+
len = 2 + @longarg.length
|
|
104
|
+
len = len + 2 + @shortarg.length if (@shortarg != nil)
|
|
105
|
+
if (@paramlist)
|
|
106
|
+
@paramlist.each { |value|
|
|
107
|
+
len = len + value.length + 1
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
len
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def to_s
|
|
114
|
+
line = @name
|
|
115
|
+
if (@shortarg != nil)
|
|
116
|
+
line = line + " (-#{@shortarg},--#{@longarg})"
|
|
117
|
+
else
|
|
118
|
+
line = line + " (--#{@longarg})"
|
|
119
|
+
end
|
|
120
|
+
if (@paramlist != nil)
|
|
121
|
+
@paramlist.each { |name|
|
|
122
|
+
line = line + " #{name}"
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
line = line + " : #{@description}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
#-----------------------------------------------------------------------------
|
|
132
|
+
# Represents one command.
|
|
133
|
+
#
|
|
134
|
+
class Command
|
|
135
|
+
|
|
136
|
+
# name of the command as given in the config file
|
|
137
|
+
attr_reader :name
|
|
138
|
+
|
|
139
|
+
# human readable description as given in the config file
|
|
140
|
+
attr_reader :description
|
|
141
|
+
|
|
142
|
+
# array of OneOption objects for each option allowed by this command
|
|
143
|
+
attr_reader :options
|
|
144
|
+
|
|
145
|
+
# string length of longest option this command supports
|
|
146
|
+
attr_reader :longest_option
|
|
147
|
+
|
|
148
|
+
#---------------------------------------------------------------------------
|
|
149
|
+
# Parse command line from config to create self.
|
|
150
|
+
#
|
|
151
|
+
def initialize(line, vars)
|
|
152
|
+
line =~ /\[([^\]]+)\](.*)/
|
|
153
|
+
@name = $1.strip
|
|
154
|
+
@description = $2.strip if ($2)
|
|
155
|
+
@options = Array.new()
|
|
156
|
+
@longest_option = 0
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
#---------------------------------------------------------------------------
|
|
160
|
+
# Add the given option (OneOption obj) to my set of allowed options
|
|
161
|
+
def addopt(option)
|
|
162
|
+
@options.push(option)
|
|
163
|
+
if (option.display_len() > @longest_option)
|
|
164
|
+
@longest_option = option.display_len()
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
#-----------------------------------------------------------------------------
|
|
172
|
+
# Represents the full optgen configuration for a given config file.
|
|
173
|
+
#
|
|
174
|
+
class OptGen
|
|
175
|
+
|
|
176
|
+
#---------------------------------------------------------------------------
|
|
177
|
+
# Parse an optgen config file.
|
|
178
|
+
#
|
|
179
|
+
def initialize(optgenfile)
|
|
180
|
+
|
|
181
|
+
@configfile = optgenfile
|
|
182
|
+
@commands = Array.new()
|
|
183
|
+
@options = Hash.new()
|
|
184
|
+
@options_array = Array.new()
|
|
185
|
+
@include = Array.new()
|
|
186
|
+
@longest_command = 0
|
|
187
|
+
@allargs = Hash.new()
|
|
188
|
+
|
|
189
|
+
command = nil
|
|
190
|
+
vars = Hash.new()
|
|
191
|
+
saw_global = false
|
|
192
|
+
|
|
193
|
+
fin = File.open(optgenfile)
|
|
194
|
+
fin.each_line { |line|
|
|
195
|
+
line.chomp!
|
|
196
|
+
line.strip!
|
|
197
|
+
next if (line == "")
|
|
198
|
+
# depending on the ruby version, char from string may return a char or an int... sigh...
|
|
199
|
+
next if (line[0] == '#' || line[0] == 35)
|
|
200
|
+
|
|
201
|
+
if (line[0] == '[' || line[0] == 91)
|
|
202
|
+
if (saw_global)
|
|
203
|
+
puts "error: GLOBAL section must be last"
|
|
204
|
+
exit(1)
|
|
205
|
+
end
|
|
206
|
+
command = Command.new(line, vars)
|
|
207
|
+
@commands.push(command)
|
|
208
|
+
if (command.name.length > @longest_command)
|
|
209
|
+
@longest_command = command.name.length
|
|
210
|
+
end
|
|
211
|
+
if (command.name == "GLOBAL")
|
|
212
|
+
saw_global = true
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
elsif (line =~ /(\w+)=(.+)/)
|
|
216
|
+
name = $1
|
|
217
|
+
value = $2
|
|
218
|
+
vars[name] = value
|
|
219
|
+
|
|
220
|
+
elsif (line[0] == "." || line[0] == 46)
|
|
221
|
+
directive(line)
|
|
222
|
+
|
|
223
|
+
else
|
|
224
|
+
o = OneOption.new(line, vars)
|
|
225
|
+
command.addopt(o)
|
|
226
|
+
if (@options[o.name] == nil)
|
|
227
|
+
@options[o.name] = o
|
|
228
|
+
@options_array.push(o.name)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
if (o.shortarg != nil)
|
|
232
|
+
prev = @allargs[o.shortarg]
|
|
233
|
+
if (prev != nil && prev.longarg != o.longarg)
|
|
234
|
+
puts "error: mismatch: [#{o}] : [#{prev}]"
|
|
235
|
+
exit(1)
|
|
236
|
+
end
|
|
237
|
+
@allargs[o.shortarg] = o
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
prev = @allargs[o.longarg]
|
|
241
|
+
if (prev != nil && prev.shortarg != o.shortarg)
|
|
242
|
+
puts "error: mismatch: [#{o}] : [#{prev}]"
|
|
243
|
+
exit(1)
|
|
244
|
+
end
|
|
245
|
+
@allargs[o.longarg] = o
|
|
246
|
+
end
|
|
247
|
+
}
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
#---------------------------------------------------------------------------
|
|
251
|
+
# Handle per-config directives (lines starting with a dot in config file)
|
|
252
|
+
#
|
|
253
|
+
def directive(line)
|
|
254
|
+
|
|
255
|
+
@require_command = true if (line == ".require_command")
|
|
256
|
+
@strict_options = true if (line == ".strict_options")
|
|
257
|
+
|
|
258
|
+
if (line =~ /\.include (.*)/)
|
|
259
|
+
@include.push($1)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
#---------------------------------------------------------------------------
|
|
265
|
+
# Create optgen handling functions for C code.
|
|
266
|
+
#
|
|
267
|
+
def gen_C
|
|
268
|
+
|
|
269
|
+
header = <<-ENDHEADER
|
|
270
|
+
|
|
271
|
+
/*
|
|
272
|
+
* DO NOT EDIT THIS FILE BY HAND!
|
|
273
|
+
*
|
|
274
|
+
* All changes will be lost when file gets regenerated.
|
|
275
|
+
*
|
|
276
|
+
* Generated by optgen #{$VERSION} from config file "#{@configfile}"
|
|
277
|
+
*
|
|
278
|
+
* Copyright (c) #{Time.now.year}, Jyri J. Virkki
|
|
279
|
+
* All rights reserved.
|
|
280
|
+
*
|
|
281
|
+
* Redistribution and use in source and binary forms, with or without
|
|
282
|
+
* modification, are permitted provided that the following conditions are
|
|
283
|
+
* met:
|
|
284
|
+
*
|
|
285
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
286
|
+
* notice, this list of conditions and the following disclaimer.
|
|
287
|
+
*
|
|
288
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
289
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
290
|
+
* documentation and/or other materials provided with the distribution.
|
|
291
|
+
*
|
|
292
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
293
|
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
294
|
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
295
|
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
296
|
+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
297
|
+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
298
|
+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
299
|
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
300
|
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
301
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
302
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
303
|
+
*
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
ENDHEADER
|
|
307
|
+
|
|
308
|
+
#----- Generate header file optgen.h -----
|
|
309
|
+
|
|
310
|
+
o = File.open("optgen.h", 'w')
|
|
311
|
+
|
|
312
|
+
o.puts(header)
|
|
313
|
+
o.puts "#ifndef OPTGEN_H"
|
|
314
|
+
o.puts "#define OPTGEN_H"
|
|
315
|
+
o.puts
|
|
316
|
+
o.puts "#include <stdlib.h>"
|
|
317
|
+
o.puts
|
|
318
|
+
|
|
319
|
+
# Define constants OPT_* for each known option
|
|
320
|
+
n = 0
|
|
321
|
+
o.puts "#define COUNT_OPTIONS #{@options.size}"
|
|
322
|
+
@options_array.each { |i|
|
|
323
|
+
op = @options[i]
|
|
324
|
+
o.puts
|
|
325
|
+
sa = "-#{op.shortarg} " if (op.shortarg)
|
|
326
|
+
o.puts "// #{op.to_s}"
|
|
327
|
+
o.puts "#define OPT_#{op.name} #{n}"
|
|
328
|
+
n = n + 1
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Define constants COMMAND_* for each known command
|
|
332
|
+
n = 1
|
|
333
|
+
global = nil
|
|
334
|
+
@commands.each { |command|
|
|
335
|
+
if (command.name == "GLOBAL")
|
|
336
|
+
global = command
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
o.puts
|
|
340
|
+
o.puts "// #{command.name}: #{command.description}"
|
|
341
|
+
o.puts "#define COMMAND_#{command.name} #{n}"
|
|
342
|
+
n = n + 1
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
# Function prototypes
|
|
346
|
+
o.puts
|
|
347
|
+
o.puts '/**'
|
|
348
|
+
o.puts ' * Function to parse the arguments.'
|
|
349
|
+
o.puts ' *'
|
|
350
|
+
o.puts ' * Parameters:'
|
|
351
|
+
o.puts ' * argc - argv size (as passed to main)'
|
|
352
|
+
o.puts ' * argv - Arguments (as passed to main)'
|
|
353
|
+
o.puts ' * command - Command (if present) will be stored here'
|
|
354
|
+
o.puts ' * options - Caller-allocated array where option values will be stored'
|
|
355
|
+
o.puts ' *'
|
|
356
|
+
o.puts ' * Return:'
|
|
357
|
+
o.puts ' * OPTGEN_OK on success'
|
|
358
|
+
o.puts ' * OPTGEN_NONE if no arguments seen'
|
|
359
|
+
o.puts ' * command will be set to OPTGEN_NO_COMMAND if no command given, or'
|
|
360
|
+
o.puts ' * one of the COMMAND_* constants above.'
|
|
361
|
+
o.puts ' * options array has an entry for each OPT_* index for each known option.'
|
|
362
|
+
o.puts ' * The value of each OPT_* entry is one of:'
|
|
363
|
+
o.puts ' * NULL if the option was not seen'
|
|
364
|
+
o.puts ' * string value if the option had a value'
|
|
365
|
+
o.puts ' * for options with no value, a string value of an integer'
|
|
366
|
+
o.puts ' *'
|
|
367
|
+
o.puts ' * Sample call:'
|
|
368
|
+
o.puts ' *'
|
|
369
|
+
o.puts ' * char * options[COUNT_OPTIONS];'
|
|
370
|
+
o.puts ' * int command;'
|
|
371
|
+
o.puts ' *'
|
|
372
|
+
o.puts ' * int rv = optgen_parse(argc, argv, &command, options);'
|
|
373
|
+
o.puts ' *'
|
|
374
|
+
o.puts ' */'
|
|
375
|
+
o.puts 'int optgen_parse(int argc, char * argv[], int * command, char * options[]);'
|
|
376
|
+
o.puts '#define OPTGEN_OK 0'
|
|
377
|
+
o.puts '#define OPTGEN_NONE 1'
|
|
378
|
+
o.puts '#define OPTGEN_NO_COMMAND -1'
|
|
379
|
+
o.puts
|
|
380
|
+
|
|
381
|
+
o.puts '/**'
|
|
382
|
+
o.puts ' * Convenience function to get count of times an option was specified.'
|
|
383
|
+
o.puts ' * For options which take NO arguments but can be repeated multiple times,'
|
|
384
|
+
o.puts ' * this function returns the number of times it was seen'
|
|
385
|
+
o.puts ' *'
|
|
386
|
+
o.puts ' * Parameters:'
|
|
387
|
+
o.puts ' * str - A value from options array'
|
|
388
|
+
o.puts ' *'
|
|
389
|
+
o.puts ' * Return:'
|
|
390
|
+
o.puts ' *'
|
|
391
|
+
o.puts ' * Number of times an option was seen, or zero if none'
|
|
392
|
+
o.puts ' *'
|
|
393
|
+
o.puts ' */'
|
|
394
|
+
o.puts 'int opt_count(char * str);'
|
|
395
|
+
o.puts
|
|
396
|
+
|
|
397
|
+
o.puts '/**'
|
|
398
|
+
o.puts ' * Convenience function to return integer value of an option'
|
|
399
|
+
o.puts ' *'
|
|
400
|
+
o.puts ' * Parameters:'
|
|
401
|
+
o.puts ' * str - A value from options array'
|
|
402
|
+
o.puts ' * def - Default value if none given'
|
|
403
|
+
o.puts ' *'
|
|
404
|
+
o.puts ' * Return:'
|
|
405
|
+
o.puts ' *'
|
|
406
|
+
o.puts ' * Value of option as integer. If NULL, returns default def.'
|
|
407
|
+
o.puts ' *'
|
|
408
|
+
o.puts ' */'
|
|
409
|
+
o.puts 'int opt_int(char * str, int def);'
|
|
410
|
+
o.puts
|
|
411
|
+
|
|
412
|
+
o.puts '/**'
|
|
413
|
+
o.puts ' * Convenience function to single char value of an option'
|
|
414
|
+
o.puts ' *'
|
|
415
|
+
o.puts ' * Parameters:'
|
|
416
|
+
o.puts ' * str - A value from options array'
|
|
417
|
+
o.puts ' * def - Default value if none given'
|
|
418
|
+
o.puts ' *'
|
|
419
|
+
o.puts ' * Return:'
|
|
420
|
+
o.puts ' *'
|
|
421
|
+
o.puts ' * Value of option as char. If NULL, returns default def.'
|
|
422
|
+
o.puts ' *'
|
|
423
|
+
o.puts ' */'
|
|
424
|
+
o.puts 'char opt_char(char * str, char def);'
|
|
425
|
+
o.puts
|
|
426
|
+
|
|
427
|
+
o.puts '/**'
|
|
428
|
+
o.puts ' * Show help based on command and option descriptions.'
|
|
429
|
+
o.puts ' *'
|
|
430
|
+
o.puts ' */'
|
|
431
|
+
o.puts 'void opt_show_help();'
|
|
432
|
+
o.puts
|
|
433
|
+
|
|
434
|
+
o.puts '// Callbacks need to return one of these values'
|
|
435
|
+
o.puts '#define OPTGEN_CALLBACK_OK 0'
|
|
436
|
+
o.puts '#define OPTGEN_CALLBACK_FAIL 1'
|
|
437
|
+
o.puts
|
|
438
|
+
|
|
439
|
+
o.puts "#endif"
|
|
440
|
+
o.close()
|
|
441
|
+
|
|
442
|
+
#----- Generate code file optgen.c -----
|
|
443
|
+
|
|
444
|
+
o = File.open("optgen.c", 'w')
|
|
445
|
+
|
|
446
|
+
o.puts header
|
|
447
|
+
o.puts '#include <stdlib.h>'
|
|
448
|
+
o.puts '#include <stdio.h>'
|
|
449
|
+
o.puts '#include <string.h>'
|
|
450
|
+
o.puts '#include <sys/ioctl.h>'
|
|
451
|
+
o.puts '#include <stdio.h>'
|
|
452
|
+
o.puts '#include <unistd.h>'
|
|
453
|
+
o.puts '#include "optgen.h"'
|
|
454
|
+
@include.each { |inc|
|
|
455
|
+
o.puts "#include \"#{inc}\""
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
o.puts
|
|
459
|
+
o.puts '// LCOV_EXCL_START'
|
|
460
|
+
o.puts
|
|
461
|
+
o.puts 'char * numstring[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };'
|
|
462
|
+
o.puts
|
|
463
|
+
|
|
464
|
+
# Define the options allowed by each command
|
|
465
|
+
o.puts '// For each option, list the commands which accept it'
|
|
466
|
+
@options_array.each { |optname|
|
|
467
|
+
|
|
468
|
+
o.print "int option_#{optname}[] = {"
|
|
469
|
+
c = 1
|
|
470
|
+
comma = ""
|
|
471
|
+
|
|
472
|
+
@commands.each { |command|
|
|
473
|
+
command.options.each { |allowed|
|
|
474
|
+
if (optname == allowed.name)
|
|
475
|
+
o.print "#{comma} #{c}"
|
|
476
|
+
comma = ","
|
|
477
|
+
end
|
|
478
|
+
}
|
|
479
|
+
c = c + 1
|
|
480
|
+
}
|
|
481
|
+
o.puts " };"
|
|
482
|
+
}
|
|
483
|
+
o.puts
|
|
484
|
+
|
|
485
|
+
o.puts 'int optgen_parse(int argc, char * argv[], int * command, char * options[])'
|
|
486
|
+
o.puts '{'
|
|
487
|
+
o.puts ' int i;'
|
|
488
|
+
o.puts ' int pos;'
|
|
489
|
+
o.puts
|
|
490
|
+
o.puts ' *command = OPTGEN_NO_COMMAND;'
|
|
491
|
+
o.puts
|
|
492
|
+
o.puts ' for (i = 0; i < COUNT_OPTIONS; i++) {'
|
|
493
|
+
o.puts ' options[i] = NULL;'
|
|
494
|
+
o.puts ' }'
|
|
495
|
+
o.puts
|
|
496
|
+
o.puts ' if (argc < 2) {'
|
|
497
|
+
o.puts ' return OPTGEN_NONE;'
|
|
498
|
+
o.puts ' }'
|
|
499
|
+
o.puts
|
|
500
|
+
o.puts ' // Check if the first argument is a recognized command'
|
|
501
|
+
o.puts ' int l = strlen(argv[1]);'
|
|
502
|
+
o.puts
|
|
503
|
+
|
|
504
|
+
# Go through commands to see if user gave a known one
|
|
505
|
+
command_num = 1
|
|
506
|
+
@commands.each { |command|
|
|
507
|
+
next if command.name == "GLOBAL"
|
|
508
|
+
len = command.name.length
|
|
509
|
+
o.puts " if (l == #{len} && !strncmp(\"#{command.name}\", argv[1], #{len})) {"
|
|
510
|
+
o.puts " *command = #{command_num};"
|
|
511
|
+
o.puts ' goto OPTS;'
|
|
512
|
+
o.puts ' }'
|
|
513
|
+
command_num = command_num + 1
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (@require_command)
|
|
517
|
+
o.puts
|
|
518
|
+
o.puts ' // A command is required but none found'
|
|
519
|
+
o.puts ' if (*command == OPTGEN_NO_COMMAND) {'
|
|
520
|
+
o.puts ' printf("error: Command required but none given\n");'
|
|
521
|
+
o.puts ' exit(1);'
|
|
522
|
+
o.puts ' }'
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Go through all remaining options
|
|
526
|
+
o.puts
|
|
527
|
+
o.puts ' OPTS:'
|
|
528
|
+
o.puts
|
|
529
|
+
o.puts ' pos = *command == OPTGEN_NO_COMMAND ? 1 : 2;'
|
|
530
|
+
o.puts
|
|
531
|
+
o.puts ' while (pos < argc) {'
|
|
532
|
+
o.puts ' l = strlen(argv[pos]);'
|
|
533
|
+
o.puts
|
|
534
|
+
|
|
535
|
+
n = 0
|
|
536
|
+
@options_array.each { |i|
|
|
537
|
+
op = @options[i]
|
|
538
|
+
sa = nil
|
|
539
|
+
sa = "-#{op.shortarg}" if (op.shortarg)
|
|
540
|
+
sal = sa.length if sa
|
|
541
|
+
la = "--#{op.longarg}"
|
|
542
|
+
lal = la.length
|
|
543
|
+
|
|
544
|
+
o.print " if ((l == #{lal} && !strncmp(\"#{la}\", argv[pos], #{lal}))"
|
|
545
|
+
if (sa == nil)
|
|
546
|
+
o.puts ') {'
|
|
547
|
+
else
|
|
548
|
+
o.puts '||'
|
|
549
|
+
o.puts " (l == #{sal} && !strncmp(\"#{sa}\", argv[pos], #{sal}))) {"
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
if (op.paramlist != nil)
|
|
553
|
+
o.puts ' if (argv[pos+1] == NULL) {'
|
|
554
|
+
o.puts " printf(\"error: no value for arg #{la}\\n\");"
|
|
555
|
+
o.puts ' exit(1);'
|
|
556
|
+
o.puts ' }'
|
|
557
|
+
|
|
558
|
+
if (op.paramlist[0] == 'ABSPATH')
|
|
559
|
+
o.puts ' // ABSPATH: Must start with /'
|
|
560
|
+
o.puts " if (argv[pos+1][0] != '/') {"
|
|
561
|
+
o.puts " printf(\"error: #{la} must be an absolute path\\n\");"
|
|
562
|
+
o.puts ' exit(1);'
|
|
563
|
+
o.puts ' }'
|
|
564
|
+
elsif (op.paramlist[0] == 'CHAR')
|
|
565
|
+
o.puts ' // CHAR: Must be a single character'
|
|
566
|
+
o.puts " if (strlen(argv[pos+1]) != 1) {"
|
|
567
|
+
o.puts " printf(\"error: #{la} must be a single character\\n\");"
|
|
568
|
+
o.puts ' exit(1);'
|
|
569
|
+
o.puts ' }'
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
o.puts " options[#{n}] = argv[pos+1];"
|
|
573
|
+
o.puts ' pos += 2;'
|
|
574
|
+
else
|
|
575
|
+
o.puts " if (options[#{n}] == NULL) {"
|
|
576
|
+
o.puts " options[#{n}] = numstring[0];"
|
|
577
|
+
o.puts ' } else {'
|
|
578
|
+
o.puts " options[#{n}] = numstring[atoi(options[#{n}])];"
|
|
579
|
+
o.puts ' }'
|
|
580
|
+
o.puts ' pos++;'
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
if (@strict_options)
|
|
584
|
+
o.puts " // strict_options: is #{@options_array[n]} allowed?"
|
|
585
|
+
name = @options_array[n]
|
|
586
|
+
|
|
587
|
+
o.puts ' int ok = 0;'
|
|
588
|
+
o.puts ' unsigned int cc;'
|
|
589
|
+
o.puts " unsigned int len = sizeof(option_#{name}) / sizeof(option_#{name})[0];"
|
|
590
|
+
o.puts ' for (cc = 0; cc < len; cc++) {'
|
|
591
|
+
o.puts " if (option_#{name}[cc] == *command) { ok = 1; }"
|
|
592
|
+
o.puts " if (option_#{name}[cc] == COMMAND_GLOBAL) { ok = 1; }"
|
|
593
|
+
o.puts ' }'
|
|
594
|
+
|
|
595
|
+
o.puts ' if (!ok) {'
|
|
596
|
+
o.puts " printf(\"error: option '#{name}' not compatible with given command\\n\");"
|
|
597
|
+
o.puts ' exit(1);'
|
|
598
|
+
o.puts ' }'
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
if (op.callback != nil && op.callback != "")
|
|
602
|
+
o.puts ' // callback configured for this option'
|
|
603
|
+
o.puts " int rv = #{op.callback}(options[#{n}], *command);"
|
|
604
|
+
o.puts ' if (rv != OPTGEN_CALLBACK_OK) {'
|
|
605
|
+
o.puts " printf(\"error: problem handling option '#{name}'\\n\");"
|
|
606
|
+
o.puts ' exit(1);'
|
|
607
|
+
o.puts ' }'
|
|
608
|
+
o.puts
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
o.puts ' continue;'
|
|
612
|
+
o.puts ' }'
|
|
613
|
+
|
|
614
|
+
n = n + 1
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
o.puts
|
|
618
|
+
o.puts ' printf("error: unknown argument: [%s]\n", argv[pos]);'
|
|
619
|
+
o.puts ' exit(1);'
|
|
620
|
+
o.puts ' }'
|
|
621
|
+
o.puts
|
|
622
|
+
|
|
623
|
+
o.puts ' return OPTGEN_OK;'
|
|
624
|
+
o.puts '}'
|
|
625
|
+
o.puts
|
|
626
|
+
|
|
627
|
+
o.puts 'int opt_count(char * str)'
|
|
628
|
+
o.puts '{'
|
|
629
|
+
o.puts ' if (str == NULL) {'
|
|
630
|
+
o.puts ' return 0;'
|
|
631
|
+
o.puts ' } else {'
|
|
632
|
+
o.puts ' return atoi(str);'
|
|
633
|
+
o.puts ' }'
|
|
634
|
+
o.puts '}'
|
|
635
|
+
o.puts
|
|
636
|
+
|
|
637
|
+
o.puts 'int opt_int(char * str, int def)'
|
|
638
|
+
o.puts '{'
|
|
639
|
+
o.puts ' if (str == NULL) {'
|
|
640
|
+
o.puts ' return def;'
|
|
641
|
+
o.puts ' } else {'
|
|
642
|
+
o.puts ' return atoi(str);'
|
|
643
|
+
o.puts ' }'
|
|
644
|
+
o.puts '}'
|
|
645
|
+
o.puts
|
|
646
|
+
|
|
647
|
+
o.puts 'char opt_char(char * str, char def)'
|
|
648
|
+
o.puts '{'
|
|
649
|
+
o.puts ' if (str == NULL) {'
|
|
650
|
+
o.puts ' return def;'
|
|
651
|
+
o.puts ' } else {'
|
|
652
|
+
o.puts ' return str[0];'
|
|
653
|
+
o.puts ' }'
|
|
654
|
+
o.puts '}'
|
|
655
|
+
o.puts
|
|
656
|
+
|
|
657
|
+
o.puts 'void opt_show_help()'
|
|
658
|
+
o.puts '{'
|
|
659
|
+
|
|
660
|
+
@commands.each { |command|
|
|
661
|
+
next if (command.name == "GLOBAL")
|
|
662
|
+
|
|
663
|
+
line = command.name
|
|
664
|
+
line = line + " " * (2 + @longest_command - command.name.length)
|
|
665
|
+
line = line + command.description
|
|
666
|
+
o.puts " printf(\"#{line}\\n\");"
|
|
667
|
+
|
|
668
|
+
command.options.each { |opt|
|
|
669
|
+
if (opt.option_type != "hidden")
|
|
670
|
+
line = optline(opt, command.longest_option)
|
|
671
|
+
o.puts " printf(\"#{line}\\n\");"
|
|
672
|
+
end
|
|
673
|
+
}
|
|
674
|
+
o.puts ' printf("\n");'
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (global != nil)
|
|
678
|
+
o.puts ' printf("General options:\n");'
|
|
679
|
+
global.options.each { |opt|
|
|
680
|
+
if (opt.option_type != "hidden")
|
|
681
|
+
line = optline(opt, global.longest_option)
|
|
682
|
+
o.puts " printf(\"#{line}\\n\");"
|
|
683
|
+
end
|
|
684
|
+
}
|
|
685
|
+
end
|
|
686
|
+
o.puts ' printf("\n");'
|
|
687
|
+
o.puts '}'
|
|
688
|
+
o.puts '// LCOV_EXCL_STOP'
|
|
689
|
+
|
|
690
|
+
o.close()
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
#---------------------------------------------------------------------------
|
|
694
|
+
# Generate doc line for one option
|
|
695
|
+
#
|
|
696
|
+
def optline(opt, longest)
|
|
697
|
+
line = " "
|
|
698
|
+
if (opt.shortarg != nil && opt.shortarg != "")
|
|
699
|
+
line = line + "-#{opt.shortarg} "
|
|
700
|
+
else
|
|
701
|
+
line = line + " "
|
|
702
|
+
end
|
|
703
|
+
line = line + "--#{opt.longarg} "
|
|
704
|
+
if (opt.paramlist != nil)
|
|
705
|
+
opt.paramlist.each { |value|
|
|
706
|
+
line = line + "#{value} "
|
|
707
|
+
}
|
|
708
|
+
end
|
|
709
|
+
line = line + " " * (7 + longest - line.length)
|
|
710
|
+
line = line + opt.description
|
|
711
|
+
|
|
712
|
+
line
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
#-----------------------------------------------------------------------------
|
|
719
|
+
# main
|
|
720
|
+
#
|
|
721
|
+
genfile = ARGV.shift
|
|
722
|
+
if (!genfile)
|
|
723
|
+
puts "error: no optgen file!"
|
|
724
|
+
exit(1)
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
optgen = OptGen.new(genfile)
|
|
728
|
+
|
|
729
|
+
optgen.gen_C()
|
data/lib/liboptgen.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# This is just here to quiet warnings.
|
metadata
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: optgen
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '0.3'
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jyri J. Virkki
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2016-02-13 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: Generate C code to parse command line options
|
|
15
|
+
email: jyri@virkki.com
|
|
16
|
+
executables:
|
|
17
|
+
- optgen
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/liboptgen.rb
|
|
22
|
+
- bin/optgen
|
|
23
|
+
homepage: https://github.com/jvirkki/optgen
|
|
24
|
+
licenses:
|
|
25
|
+
- GPLv3
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
none: false
|
|
32
|
+
requirements:
|
|
33
|
+
- - ! '>='
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '0'
|
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
|
+
none: false
|
|
38
|
+
requirements:
|
|
39
|
+
- - ! '>='
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
requirements: []
|
|
43
|
+
rubyforge_project: nowarning
|
|
44
|
+
rubygems_version: 1.8.23
|
|
45
|
+
signing_key:
|
|
46
|
+
specification_version: 3
|
|
47
|
+
summary: Generate code to parse command line options
|
|
48
|
+
test_files: []
|