]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: interface ready for review
[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 #include <ucw/strtonum.h>
14
15 #include <alloca.h>
16
17 struct opt_section * opt_section_root;
18
19 void opt_help_noexit_internal(struct opt_section * help) {
20   uns first_column = 0;
21
22   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
23     if (item->flags & OPT_NO_HELP) continue;
24     if (item->cls == OPT_CL_HELP && item->u.help2 == NULL) continue;
25     if (item->cls == OPT_CL_SECTION) continue;
26     
27     uns linelen = 0;
28     if (item->cls == OPT_CL_HELP) { // two-column help line
29       if (first_column < strlen(item->help))
30         first_column = strlen(item->help);
31       continue;
32     }
33     
34     if (item->letter) { // will write sth like "-x, --exclusive"
35       linelen = strlen("-x, --") + strlen(item->name);
36     } else { // will write sth like "--exclusive"
37       linelen = strlen("--") + strlen(item->name);
38     }
39
40     ASSERT(item->flags & OPT_VALUE_FLAGS);
41
42     if (item->flags & OPT_REQUIRED_VALUE) {
43       linelen += strlen("=value");
44     } else if (item->flags & OPT_MAYBE_VALUE) {
45       linelen += strlen("(=value)");
46     }
47
48     if (linelen > first_column)
49       first_column = linelen;
50   }
51
52   char * spaces = alloca(first_column + 1);
53   char * buf = alloca(first_column + 1);
54   for (uns i=0;i<first_column;i++)
55     spaces[i] = ' ';
56
57   spaces[first_column] = 0;
58
59 #define VAL(it) ((it->flags & OPT_REQUIRED_VALUE) ? "=value" : ((it->flags & OPT_NO_VALUE) ? "" : "(=value)"))
60   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
61     if (item->flags & OPT_NO_HELP) continue;
62     
63     if (item->cls == OPT_CL_HELP) {
64       fprintf(stderr, "%s", item->help);
65       if (item->u.help2 == NULL)
66         fprintf(stderr, "\n");
67       else
68         fprintf(stderr, "%s %s\n", spaces + strlen(item->help), item->u.help2);
69     } else if (item->cls == OPT_CL_SECTION) {
70       opt_help_noexit_internal(item->u.section);
71     } else if (item->letter) {
72       sprintf(buf, "-%c, --%s%s", item->letter, item->name, VAL(item));
73       fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
74     } else {
75       sprintf(buf, "--%s%s", item->name, VAL(item));
76       fprintf(stderr, "%s%s %s\n", buf, spaces + strlen(buf), item->help);
77     }
78   }
79 }
80
81 void opt_init(struct opt_section * options) {
82   opt_section_root = options;
83   for (struct opt_item * item = options->opt; item->cls != OPT_CL_END; item++)
84     if (!(item->flags & OPT_VALUE_FLAGS)) {
85       if (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER) {
86         fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name);
87         exit(2);
88       } else
89         item->flags |= opt_default_value_flags[item->cls];
90     }
91 }
92
93 int opt_parse(char ** argv, char *** posargs) {
94   // Temporary. To be fixed.
95   static char ** d;
96   d = xmalloc(sizeof (*d) * 2);
97   d[0] = "darjeeling";
98   d[1] = "puerh";
99   *posargs = d;
100   return 2;
101 }
102
103 #ifdef TEST
104 #include <ucw/fastbuf.h>
105
106 static int show_version(const char ** param UNUSED) {
107   printf("This is a simple tea boiling console v0.1.\n");
108   exit(0);
109 }
110
111 struct teapot_temperature {
112   enum {
113     TEMP_CELSIUS = 0,
114     TEMP_FAHRENHEIT,
115     TEMP_KELVIN,
116     TEMP_REAUMUR,
117     TEMP_RANKINE
118   } scale;
119   int value;
120 } temperature;
121
122 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
123
124 static enum TEAPOT_TYPE {
125   TEAPOT_STANDARD = 0,
126   TEAPOT_EXCLUSIVE,
127   TEAPOT_GLASS,
128   TEAPOT_HANDS,
129   TEAPOT_UNDEFINED = -1
130 } set = TEAPOT_UNDEFINED;
131
132 static int english = 0;
133 static char * name = NULL;
134 static uns sugar = 0;
135 static uns verbose = 1;
136 static int with_gas = 0;
137 static uns black_magic = 0;
138 static int pray = 0;
139 static uns water_amount = 0;
140
141 static const char * teapot_temperature_parser(char * in, void * ptr) {
142   struct teapot_temperature * temp = ptr;
143   const char * next;
144   const char * err = str_to_int(&temp->value, in, &next, 0);
145   if (err)
146     return err;
147   if (!strcmp("C", next))
148     temp->scale = TEMP_CELSIUS;
149   else if (!strcmp("F", next))
150     temp->scale = TEMP_FAHRENHEIT;
151   else if (!strcmp("K", next))
152     temp->scale = TEMP_KELVIN;
153   else if (!strcmp("R", next))
154     temp->scale = TEMP_RANKINE;
155   else if (!strcmp("Re", next))
156     temp->scale = TEMP_REAUMUR;
157   else {
158     fprintf(stderr, "Unknown scale: %s\n", next);
159     exit(1);
160   }
161   return next + strlen(next);
162 }
163
164 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
165   struct teapot_temperature * temp = ptr;
166   bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
167 }
168
169 static struct cf_user_type teapot_temperature_t = {
170   .size = 2*sizeof(int),
171   .name = "teapot_temperature_t",
172   .parser = (cf_parser1*) teapot_temperature_parser,
173   .dumper = (cf_dumper1*) teapot_temperature_dumper
174 };
175
176 static struct opt_section water_options = {
177   OPT_ITEMS {
178     OPT_UNS('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "Amount of water (in mls)"),
179     OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "Use water with gas"),
180     OPT_END
181   }
182 };
183
184 static struct opt_section help = {
185   OPT_ITEMS {
186     OPT_HELP("A simple tea boiling console."),
187     OPT_HELP("Usage: teapot [options] name-of-the-tea"),
188     OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
189     OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
190     OPT_HELP(""),
191     OPT_HELP("Options:"),
192     OPT_SHOW_HELP,
193     OPT_CALL('V', "version", show_version, OPT_NO_VALUE, "Show the version"),
194     OPT_HELP(""),
195     OPT_BOOL('e', "english-style", english, 0, "English style (with milk)"),
196     OPT_UNS('s', "sugar", sugar, OPT_REQUIRED_VALUE, "Amount of sugar (in teaspoons)"),
197     OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "Standard teapot"),
198     OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "Exclusive teapot"),
199     OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "Transparent glass teapot"),
200     OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "Use user's hands as a teapot (a bit dangerous)"),
201     OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE, "Wanted final temperature of the tea to be served"),
202     OPT_HELP2("", "Supported scales: Celsius [60C], Fahrenheit [140F],"),
203     OPT_HELP2("", "                  Kelvin [350K], Rankine [600R] and Reaumur [50Re]"),
204     OPT_HELP2("", "Only integer values allowed."),
205     OPT_INC('v', "verbose", verbose, 0, "Verbose (the more -v, the more verbose)"),
206     OPT_INC('q', "quiet", verbose, OPT_DECREMENT, "Quiet (the more -q, the more quiet)"),
207     OPT_UNS('b', "black-magic", black_magic, 0, "Use black magic to make the tea extraordinary delicious"),
208     OPT_BOOL('p', "pray", pray, 0, "Pray before boiling"),
209     OPT_HELP(""),
210     OPT_HELP("Water options:"),
211     OPT_SECTION(water_options),
212     OPT_END
213   }
214 };
215
216 static void boil_tea(const char * name) {
217   printf("Boiling a tea: %s\n", name);
218 }
219
220 int main(int argc, char ** argv)
221 {
222   char ** teas;
223   int teas_num;
224
225   opt_init(&help);
226   teas_num = opt_parse(argv, &teas);
227
228   for (int i=0; i<teas_num; i++)
229     boil_tea(teas[i]);
230
231   printf("Everything OK. Bye.\n");
232 }
233
234 #endif