]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: defined user interface
[libucw.git] / ucw / opt.c
1 /*
2  *      UCW Library -- Parsing of command line options
3  *
4  *      (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/opt.h>
12 #include <ucw/stkstring.h>
13
14 #include <alloca.h>
15
16 struct opt_section * opt_section_root;
17
18 void opt_help_noexit_internal(struct opt_section * help) {
19   uns first_column = 0;
20   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
21     if (item->flags | OPT_NO_HELP) continue;
22     if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue;
23     if (item->cls == OPT_CL_SECTION) continue;
24     
25     uns linelen = 0;
26     if (item->cls == OPT_CL_HELP) { // two-column help line
27       if (first_column < strlen(item->help))
28         first_column = strlen(item->help);
29       continue;
30     }
31     
32     if (item->letter) { // will write sth like "-x, --exclusive"
33       linelen = strlen("-x, --") + strlen(item->name);
34     } else { // will write sth like "--exclusive"
35       linelen = strlen("--") + strlen(item->name);
36     }
37
38     if (item->flags | OPT_REQUIRED_VALUE) {
39       linelen += strlen("=value");
40     } else if (!(item->flags | OPT_NO_VALUE)) {
41       linelen += strlen("(=value)");
42     }
43
44     if (linelen > first_column)
45       first_column = linelen;
46   }
47
48   char * spaces = alloca(first_column + 1);
49   char * buf = alloca(first_column + 1);
50   for (uns i=0;i<first_column;i++)
51     spaces[i] = ' ';
52
53   spaces[first_column] = 0;
54
55 #define VAL(it) ((it->flags | OPT_REQUIRED_VALUE) ? "=value" : ((it->flags | OPT_NO_VALUE) ? "" : "(=value)"))
56   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
57     if (item->flags | OPT_NO_HELP) continue;
58     
59     if (item->cls == OPT_CL_HELP) {
60       fprintf(stderr, "%s", item->help);
61       if (item->u.help2 == NULL)
62         fprintf(stderr, "\n");
63       else
64         fprintf(stderr, "%s %s\n", spaces + strlen(item->help), item->u.help2);
65     } else if (item->cls == OPT_CL_SECTION) {
66       opt_help_noexit_internal(item->u.section);
67     } else if (item->letter) {
68       sprintf(buf, "-%c, --%s%s", item->letter, item->name, VAL(item));
69       fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
70     } else {
71       sprintf(buf, "--%s%s", item->name, VAL(item));
72       fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
73     }
74   }
75 }
76
77 void opt_parse(struct opt_section * options) {
78   opt_section_root = options;
79   opt_help();
80 }
81
82 #ifdef TEST
83
84 static enum TEAPOT_TYPE {
85   TEAPOT_STANDARD = 0,
86   TEAPOT_EXCLUSIVE,
87   TEAPOT_GLASS,
88   TEAPOT_HANDS,
89   TEAPOT_UNDEFINED = -1
90 } set = TEAPOT_UNDEFINED;
91
92 static int english = 0;
93 static char * name = NULL;
94 static uns sugar = 0;
95 static uns verbose = 1;
96 static int with_gas = 0;
97 static uns black_magic = 0;
98 static int pray = 0;
99 static uns water_amount = 0;
100
101 static struct teapot_temperature {
102   enum {
103     TEMP_CELSIUS,
104     TEMP_FAHRENHEIT,
105     TEMP_KELVIN,
106     TEMP_REAUMUR
107   } scale;
108   int value;
109 } temperature;
110
111 static int parse_temperature(const char * param, void * target) {
112   return 1;
113 }
114
115 static struct opt_section water_options = {
116   OPT_ITEMS {
117     OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
118     OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
119     OPT_END
120   }
121 };
122
123 static struct opt_section help = {
124   OPT_ITEMS {
125     OPT_HELP("A simple tea boiling console."),
126     OPT_HELP("Options:"),
127     OPT_SHOW_HELP(0),
128     OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"),
129     OPT_STRING('n', "name", name, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Name of the tea to be prepared"),
130     OPT_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
131     OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"),
132     OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"),
133     OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"),
134     OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"),
135     OPT_USER('t', "temperature", temperature, parse_temperature, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"),
136     OPT_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"),
137     OPT_HELP2("", "                  Kelvin [350K], Rankine [600R] and Reaumur [50Re]"),
138     OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"),
139     OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"),
140     OPT_UNS('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
141     OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"),
142     OPT_HELP(""),
143     OPT_HELP("Water options:"),
144     OPT_SECTION(water_options),
145     OPT_END
146   }
147 };
148
149 int main(int argc, char ** argv)
150 {
151   opt_parse(&help);
152 }
153
154 #endif