1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * @f monet_options
11 * @a N.J. Nes
12 * @* A simple option handling library
13 * @T
14 * The monet server and clients make use of command line options and a (possibly)
15 * shared config file. With this library a set (represented by set,setlen) of
16 * options is created. An option is stored as name and value strings with a
17 * special flag indicating the origin of the options, (builtin, system config
18 * file, special config file or command line option).
19 *
20 */
21#include "monetdb_config.h"
22#include "monet_options.h"
23#ifndef HAVE_GETOPT_LONG
24# include "monet_getopt.h"
25#else
26# ifdef HAVE_GETOPT_H
27# include "getopt.h"
28# endif
29#endif
30#include <string.h>
31#include <ctype.h>
32
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37#ifndef HAVE_GETOPT_LONG
38# include "getopt.c"
39# include "getopt1.c"
40#endif
41
42#ifdef NATIVE_WIN32
43#define getpid _getpid
44#endif
45
46/* these two are used of the set parameter passed into functions is NULL */
47static int default_setlen = 0;
48static opt *default_set = NULL;
49
50static int
51mo_default_set(opt **Set, int setlen)
52{
53 if (*Set == NULL) {
54 if (default_set == NULL) {
55 default_setlen = mo_builtin_settings(&default_set);
56 default_setlen = mo_system_config(&default_set, default_setlen);
57 }
58 *Set = default_set;
59 setlen = default_setlen;
60 }
61 return setlen;
62}
63
64void
65mo_print_options(opt *set, int setlen)
66{
67 int i = 0;
68
69 setlen = mo_default_set(&set, setlen);
70 for (i = 0; i < setlen; i++) {
71 if (set[i].kind == opt_builtin) {
72 fprintf(stderr, "# builtin opt \t%s = %s\n", set[i].name, set[i].value);
73 }
74 }
75 for (i = 0; i < setlen; i++) {
76 if (set[i].kind == opt_config) {
77 fprintf(stderr, "# config opt \t%s = %s\n", set[i].name, set[i].value);
78 }
79 }
80 for (i = 0; i < setlen; i++) {
81 if (set[i].kind == opt_cmdline) {
82 fprintf(stderr, "# cmdline opt \t%s = %s\n", set[i].name, set[i].value);
83 }
84 }
85}
86
87
88char *
89mo_find_option(opt *set, int setlen, const char *name)
90{
91 opt *o = NULL;
92 int i;
93
94 setlen = mo_default_set(&set, setlen);
95 for (i = 0; i < setlen; i++) {
96 if (strcmp(set[i].name, name) == 0)
97 if (!o || o->kind < set[i].kind)
98 o = set + i;
99 }
100 if (o)
101 return o->value;
102 return NULL;
103}
104
105static int
106mo_config_file(opt **Set, int setlen, char *file)
107{
108 char buf[BUFSIZ];
109 FILE *fd = NULL;
110 opt *set;
111
112 if (Set == NULL) {
113 if (default_set == NULL) {
114 set = NULL;
115 setlen = mo_default_set(&set, 0);
116 } else
117 setlen = default_setlen;
118 Set = &default_set;
119 }
120 set = *Set;
121 fd = fopen(file, "r");
122 if (fd == NULL) {
123 fprintf(stderr, "Could not open file %s\n", file);
124 return setlen;
125 }
126 while (fgets(buf, BUFSIZ, fd) != NULL) {
127 char *s, *t, *val;
128 int quote;
129
130 for (s = buf; *s && isspace((unsigned char) *s); s++)
131 ;
132 if (*s == '#')
133 continue; /* commentary */
134 if (*s == 0)
135 continue; /* empty line */
136
137 val = strchr(s, '=');
138 if (val == NULL) {
139 fprintf(stderr, "mo_config_file: syntax error in %s at %s\n", file, s);
140 fclose(fd);
141 exit(1);
142 }
143 *val = 0;
144
145 for (t = s; *t && !isspace((unsigned char) *t); t++)
146 ;
147 *t = 0;
148
149 /* skip any leading blanks in the value part */
150 for (val++; *val && isspace((unsigned char) *val); val++)
151 ;
152
153 /* search to unquoted # */
154 quote = 0;
155 for (t = val; *t; t++) {
156 if (*t == '"')
157 quote = !quote;
158 else if (!quote && *t == '#')
159 break;
160 }
161 if (quote) {
162 fprintf(stderr, "mo_config_file: wrong number of quotes in %s at %s\n", file, val);
163 fclose(fd);
164 exit(1);
165 }
166 /* remove trailing white space */
167 while (isspace((unsigned char) t[-1]))
168 t--;
169 *t++ = 0;
170
171 /* treat value as empty if it consists only of white space */
172 if (t <= val)
173 val = t - 1;
174
175 set = (opt *) realloc(set, (setlen + 1) * sizeof(opt));
176 set[setlen].kind = opt_config;
177 set[setlen].name = strdup(s);
178 set[setlen].value = malloc((size_t) (t - val));
179 for (t = val, s = set[setlen].value; *t; t++)
180 if (*t != '"')
181 *s++ = *t;
182 *s = 0;
183 setlen++;
184 }
185 (void) fclose(fd);
186 *Set = set;
187 return setlen;
188}
189
190int
191mo_system_config(opt **Set, int setlen)
192{
193 char *cfg;
194
195 if (Set == NULL) {
196 if (default_set == NULL) {
197 opt *set = NULL;
198
199 setlen = mo_default_set(&set, 0);
200 } else
201 setlen = default_setlen;
202 Set = &default_set;
203 }
204 cfg = mo_find_option(*Set, setlen, "config");
205 if (!cfg)
206 return setlen;
207 setlen = mo_config_file(Set, setlen, cfg);
208 free(cfg);
209 return setlen;
210}
211
212int
213mo_builtin_settings(opt **Set)
214{
215 int i = 0;
216 opt *set;
217
218 if (Set == NULL)
219 return 0;
220
221#define N_OPTIONS 7 /*MUST MATCH # OPTIONS BELOW */
222 set = malloc(sizeof(opt) * N_OPTIONS);
223 if (set == NULL)
224 return 0;
225
226 set[i].kind = opt_builtin;
227 set[i].name = strdup("gdk_dbpath");
228 set[i].value = strdup(LOCALSTATEDIR DIR_SEP_STR "monetdb5" DIR_SEP_STR
229 "dbfarm" DIR_SEP_STR "demo");
230 i++;
231 set[i].kind = opt_builtin;
232 set[i].name = strdup("mapi_port");
233 set[i].value = strdup("50000");
234 i++;
235 set[i].kind = opt_builtin;
236 set[i].name = strdup("mapi_open");
237 set[i].value = strdup("false");
238 i++;
239 set[i].kind = opt_builtin;
240 set[i].name = strdup("mapi_ipv6");
241 set[i].value = strdup("false");
242 i++;
243 set[i].kind = opt_builtin;
244 set[i].name = strdup("mapi_autosense");
245 set[i].value = strdup("false");
246 i++;
247 set[i].kind = opt_builtin;
248 set[i].name = strdup("sql_optimizer");
249 set[i].value = strdup("default_pipe");
250 i++;
251 set[i].kind = opt_builtin;
252 set[i].name = strdup("sql_debug");
253 set[i].value = strdup("0");
254 i++;
255
256 assert(i == N_OPTIONS);
257 *Set = set;
258 return i;
259}
260
261int
262mo_add_option(opt **Set, int setlen, opt_kind kind, const char *name, const char *value)
263{
264 opt *set;
265
266 if (Set == NULL) {
267 if (default_set == NULL) {
268 set = NULL;
269 setlen = mo_default_set(&set, 0);
270 } else
271 setlen = default_setlen;
272 Set = &default_set;
273 }
274 set = (opt *) realloc(*Set, (setlen + 1) * sizeof(opt));
275 set[setlen].kind = kind;
276 set[setlen].name = strdup(name);
277 set[setlen].value = strdup(value);
278 *Set = set;
279 return setlen + 1;
280}
281
282void
283mo_free_options(opt *set, int setlen)
284{
285 int i;
286
287 if (set == NULL) {
288 set = default_set;
289 setlen = default_setlen;
290 default_set = NULL;
291 default_setlen = 0;
292 }
293 for (i = 0; i < setlen; i++) {
294 if (set[i].name)
295 free(set[i].name);
296 if (set[i].value)
297 free(set[i].value);
298 }
299 free(set);
300}
301