PEP8: fix E303: too many blank lines (2)
[sfrench/samba-autobuild/.git] / script / generate_param.py
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) 2014 Catalyst.Net Ltd
3 #
4 # Auto generate param_functions.c
5 #
6 #   ** NOTE! The following LGPL license applies to the ldb
7 #   ** library. This does NOT imply that all of Samba is released
8 #   ** under the LGPL
9 #
10 #  This library is free software; you can redistribute it and/or
11 #  modify it under the terms of the GNU Lesser General Public
12 #  License as published by the Free Software Foundation; either
13 #  version 3 of the License, or (at your option) any later version.
14 #
15 #  This library is distributed in the hope that it will be useful,
16 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 #  Lesser General Public License for more details.
19 #
20 #  You should have received a copy of the GNU Lesser General Public
21 #  License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 #
23
24 import errno
25 import os
26 import re
27 import subprocess
28 import xml.etree.ElementTree as ET
29 import sys
30 import optparse
31
32 # parse command line arguments
33 parser = optparse.OptionParser()
34 parser.add_option("-f", "--file", dest="filename",
35                   help="input file", metavar="FILE")
36 parser.add_option("-o", "--output", dest="output",
37                   help='output file', metavar="FILE")
38 parser.add_option("--mode", type="choice", metavar="<FUNCTIONS|S3PROTO|LIBPROTO|PARAMDEFS|PARAMTABLE>",
39                   choices=["FUNCTIONS", "S3PROTO", "LIBPROTO", "PARAMDEFS", "PARAMTABLE"], default="FUNCTIONS")
40 parser.add_option("--scope", metavar="<GLOBAL|LOCAL>",
41                   choices=["GLOBAL", "LOCAL"], default="GLOBAL")
42
43 (options, args) = parser.parse_args()
44
45 if options.filename is None:
46     parser.error("No input file specified")
47 if options.output is None:
48     parser.error("No output file specified")
49
50
51 def iterate_all(path):
52     """Iterate and yield all the parameters. 
53
54     :param path: path to parameters xml file
55     """
56
57     try:
58         p = open(path, 'r')
59     except IOError as e:
60         raise Exception("Error opening parameters file")
61     out = p.read()
62
63     # parse the parameters xml file
64     root = ET.fromstring(out)
65     for parameter in root:
66         name = parameter.attrib.get("name")
67         param_type = parameter.attrib.get("type")
68         context = parameter.attrib.get("context")
69         func = parameter.attrib.get("function")
70         synonym = parameter.attrib.get("synonym")
71         removed = parameter.attrib.get("removed")
72         generated = parameter.attrib.get("generated_function")
73         handler = parameter.attrib.get("handler")
74         enumlist = parameter.attrib.get("enumlist")
75         deprecated = parameter.attrib.get("deprecated")
76         synonyms = parameter.findall('synonym')
77
78         if removed == "1":
79             continue
80
81         constant = parameter.attrib.get("constant")
82         parm = parameter.attrib.get("parm")
83         if name is None or param_type is None or context is None:
84             raise Exception("Error parsing parameter: " + name)
85         if func is None:
86             func = name.replace(" ", "_").lower()
87         if enumlist is None:
88             enumlist = "NULL"
89         if handler is None:
90             handler = "NULL"
91         yield {'name': name,
92                'type': param_type,
93                'context': context,
94                'function': func,
95                'constant': (constant == '1'),
96                'parm': (parm == '1'),
97                'synonym' : synonym,
98                'generated' : generated,
99                'enumlist' : enumlist,
100                'handler' : handler,
101                'deprecated' : deprecated,
102                'synonyms' : synonyms }
103
104 # map doc attributes to a section of the generated function
105 context_dict = {"G": "_GLOBAL", "S": "_LOCAL"}
106 param_type_dict = {
107                     "boolean"      : "_BOOL",
108                     "list"         : "_LIST",
109                     "string"       : "_STRING",
110                     "integer"      : "_INTEGER",
111                     "enum"         : "_INTEGER",
112                     "char"         : "_CHAR",
113                     "boolean-auto" : "_INTEGER",
114                     "cmdlist"      : "_LIST",
115                     "bytes"        : "_INTEGER",
116                     "octal"        : "_INTEGER",
117                     "ustring"      : "_STRING",
118                   }
119
120
121 def generate_functions(path_in, path_out):
122     f = open(path_out, 'w')
123     try:
124         f.write('/* This file was automatically generated by generate_param.py. DO NOT EDIT */\n\n')
125         for parameter in iterate_all(options.filename):
126             # filter out parameteric options
127             if ':' in parameter['name']:
128                 continue
129             if parameter['synonym'] == "1":
130                 continue
131             if parameter['generated'] == "0":
132                 continue
133
134             output_string = "FN"
135             temp = context_dict.get(parameter['context'])
136             if temp is None: 
137                 raise Exception(parameter['name'] + " has an invalid context " + parameter['context'])
138             output_string += temp
139             if parameter['constant']:
140                 output_string += "_CONST"
141             if parameter['parm']: 
142                 output_string += "_PARM"
143             temp = param_type_dict.get(parameter['type'])
144             if temp is None:
145                 raise Exception(parameter['name'] + " has an invalid param type " + parameter['type'])
146             output_string += temp
147             f.write(output_string + "(" + parameter['function'] + ", " + parameter['function'] + ')\n')
148     finally:
149         f.close()
150
151 mapping = {
152             'boolean'      : 'bool ',
153             'string'       : 'char *',
154             'integer'      : 'int ',
155             'char'         : 'char ',
156             'list'         : 'const char **',
157             'enum'         : 'int ',
158             'boolean-auto' : 'int ',
159             'cmdlist'      : 'const char **',
160             'bytes'        : 'int ',
161             'octal'        : 'int ',
162             'ustring'      : 'char *',
163           }
164
165
166 def make_s3_param_proto(path_in, path_out):
167     file_out = open(path_out, 'w')
168     try:
169         file_out.write('/* This file was automatically generated by generate_param.py. DO NOT EDIT */\n\n')
170         header = get_header(path_out)
171         file_out.write("#ifndef %s\n" % header)
172         file_out.write("#define %s\n\n" % header)
173         for parameter in iterate_all(path_in):
174             # filter out parameteric options
175             if ':' in parameter['name']:
176                 continue
177             if parameter['synonym'] == "1":
178                 continue
179             if parameter['generated'] == "0":
180                 continue
181
182             output_string = ""
183             if parameter['constant']:
184                 output_string += 'const '
185             param_type = mapping.get(parameter['type'])
186             if param_type is None:
187                 raise Exception(parameter['name'] + " has an invalid context " + parameter['context'])
188             output_string += param_type
189             output_string += "lp_%s" % parameter['function']
190
191             param = None
192             if parameter['parm']:
193                 param = "const struct share_params *p"
194             else:
195                 param = "int"
196
197             if parameter['type'] == 'string' and not parameter['constant']:
198                 if parameter['context'] == 'G':
199                     output_string += '(TALLOC_CTX *ctx);\n'
200                 elif parameter['context'] == 'S':
201                     output_string += '(TALLOC_CTX *ctx, %s);\n' % param
202                 else:
203                     raise Exception(parameter['name'] + " has an invalid param type " + parameter['type'])
204             else:
205                 if parameter['context'] == 'G':
206                     output_string += '(void);\n'
207                 elif parameter['context'] == 'S':
208                     output_string += '(%s);\n' % param
209                 else:
210                     raise Exception(parameter['name'] + " has an invalid param type " + parameter['type'])
211
212             file_out.write(output_string)
213
214         file_out.write("\n#endif /* %s */\n\n" % header)
215     finally:
216         file_out.close()
217
218
219 def make_lib_proto(path_in, path_out):
220     file_out = open(path_out, 'w')
221     try:
222         file_out.write('/* This file was automatically generated by generate_param.py. DO NOT EDIT */\n\n')
223         for parameter in iterate_all(path_in):
224             # filter out parameteric options
225             if ':' in parameter['name']:
226                 continue
227             if parameter['synonym'] == "1":
228                 continue
229             if parameter['generated'] == "0":
230                 continue
231
232             output_string = ""
233             if parameter['constant']:
234                 output_string += 'const '
235             param_type = mapping.get(parameter['type'])
236             if param_type is None:
237                 raise Exception(parameter['name'] + " has an invalid context " + parameter['context'])
238             output_string += param_type
239
240             output_string += "lpcfg_%s" % parameter['function']
241
242             if parameter['type'] == 'string' and not parameter['constant']:
243                 if parameter['context'] == 'G':
244                     output_string += '(struct loadparm_context *, TALLOC_CTX *ctx);\n'
245                 elif parameter['context'] == 'S':
246                     output_string += '(struct loadparm_service *, struct loadparm_service *, TALLOC_CTX *ctx);\n'
247                 else:
248                     raise Exception(parameter['name'] + " has an invalid param type " + parameter['type'])
249             else:
250                 if parameter['context'] == 'G':
251                     output_string += '(struct loadparm_context *);\n'
252                 elif parameter['context'] == 'S':
253                     output_string += '(struct loadparm_service *, struct loadparm_service *);\n'
254                 else:
255                     raise Exception(parameter['name'] + " has an invalid param type " + parameter['type'])
256
257             file_out.write(output_string)
258     finally:
259         file_out.close()
260
261
262 def get_header(path):
263     header = os.path.basename(path).upper()
264     header = header.replace(".", "_").replace("\\", "_").replace("-", "_")
265     return "__%s__" % header
266
267
268 def make_param_defs(path_in, path_out, scope):
269     file_out = open(path_out, 'w')
270     try:
271         file_out.write('/* This file was automatically generated by generate_param.py. DO NOT EDIT */\n\n')
272         header = get_header(path_out)
273         file_out.write("#ifndef %s\n" % header)
274         file_out.write("#define %s\n\n" % header)
275         if scope == "GLOBAL": 
276             file_out.write("/**\n")
277             file_out.write(" * This structure describes global (ie., server-wide) parameters.\n")
278             file_out.write(" */\n")
279             file_out.write("struct loadparm_global \n")
280             file_out.write("{\n")
281             file_out.write("\tTALLOC_CTX *ctx; /* Context for talloced members */\n")
282         elif scope == "LOCAL":
283             file_out.write("/**\n")
284             file_out.write(" * This structure describes a single service.\n")
285             file_out.write(" */\n")
286             file_out.write("struct loadparm_service \n")
287             file_out.write("{\n")
288             file_out.write("\tbool   autoloaded;\n")
289
290         for parameter in iterate_all(path_in):
291             # filter out parameteric options
292             if ':' in parameter['name']:
293                 continue
294             if parameter['synonym'] == "1":
295                 continue
296
297             if (scope == "GLOBAL" and parameter['context'] != "G" or
298                 scope == "LOCAL" and parameter['context'] != "S"):
299                 continue
300
301             output_string = "\t"
302             param_type = mapping.get(parameter['type'])
303             if param_type is None:
304                 raise Exception(parameter['name'] + " has an invalid context " + parameter['context'])
305             output_string += param_type
306
307             output_string += "  %s;\n" % parameter['function']
308             file_out.write(output_string)
309
310         file_out.write("LOADPARM_EXTRA_%sS\n" % scope)
311         file_out.write("};\n")
312         file_out.write("\n#endif /* %s */\n\n" % header)
313     finally:
314         file_out.close()
315
316 type_dict = {
317               "boolean"      : "P_BOOL",
318               "boolean-rev"  : "P_BOOLREV",
319               "boolean-auto" : "P_ENUM",
320               "list"         : "P_LIST",
321               "string"       : "P_STRING",
322               "integer"      : "P_INTEGER",
323               "enum"         : "P_ENUM",
324               "char"         : "P_CHAR",
325               "cmdlist"      : "P_CMDLIST",
326               "bytes"        : "P_BYTES",
327               "octal"        : "P_OCTAL",
328               "ustring"      : "P_USTRING",
329             }
330
331
332 def make_param_table(path_in, path_out):
333     file_out = open(path_out, 'w')
334     try:
335         file_out.write('/* This file was automatically generated by generate_param.py. DO NOT EDIT */\n\n')
336         header = get_header(path_out)
337         file_out.write("#ifndef %s\n" % header)
338         file_out.write("#define %s\n\n" % header)
339
340         file_out.write("struct parm_struct parm_table[] = {\n")
341
342         for parameter in iterate_all(path_in):
343             # filter out parameteric options
344             if ':' in parameter['name']:
345                 continue
346             if parameter['context'] == 'G':
347                 p_class = "P_GLOBAL"
348             else:
349                 p_class = "P_LOCAL"
350
351             p_type = type_dict.get(parameter['type'])
352
353             if parameter['context'] == 'G':
354                 temp = "GLOBAL"
355             else:
356                 temp = "LOCAL"
357             offset = "%s_VAR(%s)" % (temp, parameter['function'])
358
359             enumlist = parameter['enumlist']
360             handler = parameter['handler']
361             synonym = parameter['synonym']
362             deprecated = parameter['deprecated']
363             flags_list = []
364             if synonym == "1":
365                 flags_list.append("FLAG_SYNONYM")
366             if deprecated == "1":
367                 flags_list.append("FLAG_DEPRECATED")
368             flags = "|".join(flags_list)
369             synonyms = parameter['synonyms']
370
371             file_out.write("\t{\n")
372             file_out.write("\t\t.label\t\t= \"%s\",\n" % parameter['name'])
373             file_out.write("\t\t.type\t\t= %s,\n" % p_type)
374             file_out.write("\t\t.p_class\t= %s,\n" % p_class)
375             file_out.write("\t\t.offset\t\t= %s,\n" % offset)
376             file_out.write("\t\t.special\t= %s,\n" % handler)
377             file_out.write("\t\t.enum_list\t= %s,\n" % enumlist)
378             if flags != "":
379                 file_out.write("\t\t.flags\t\t= %s,\n" % flags)
380             file_out.write("\t},\n")
381
382             if synonyms is not None:
383                 # for synonyms, we only list the synonym flag:
384                 flags = "FLAG_SYNONYM"
385                 for syn in synonyms:
386                     file_out.write("\t{\n")
387                     file_out.write("\t\t.label\t\t= \"%s\",\n" % syn.text)
388                     file_out.write("\t\t.type\t\t= %s,\n" % p_type)
389                     file_out.write("\t\t.p_class\t= %s,\n" % p_class)
390                     file_out.write("\t\t.offset\t\t= %s,\n" % offset)
391                     file_out.write("\t\t.special\t= %s,\n" % handler)
392                     file_out.write("\t\t.enum_list\t= %s,\n" % enumlist)
393                     if flags != "":
394                         file_out.write("\t\t.flags\t\t= %s,\n" % flags)
395                     file_out.write("\t},\n")
396
397         file_out.write("\n\t{NULL,  P_BOOL,  P_NONE,  0,  NULL,  NULL,  0}\n");
398         file_out.write("};\n")
399         file_out.write("\n#endif /* %s */\n\n" % header)
400     finally:
401         file_out.close()
402
403 if options.mode == 'FUNCTIONS':
404     generate_functions(options.filename, options.output)
405 elif options.mode == 'S3PROTO':
406     make_s3_param_proto(options.filename, options.output)
407 elif options.mode == 'LIBPROTO':
408     make_lib_proto(options.filename, options.output)
409 elif options.mode == 'PARAMDEFS':
410     make_param_defs(options.filename, options.output, options.scope)
411 elif options.mode == 'PARAMTABLE':
412     make_param_table(options.filename, options.output)