Merge branch 'vendor/LIBARCHIVE'
[dragonfly.git] / sys / tools / makeobjops.awk
1 #!/usr/bin/awk -f
2 #
3 # Copyright (c) 1992, 1993
4 #        The Regents of the University of California.  All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 # 3. All advertising materials mentioning features or use of this software
15 #    must display the following acknowledgement:
16 #        This product includes software developed by the University of
17 #        California, Berkeley and its contributors.
18 # 4. Neither the name of the University nor the names of its contributors
19 #    may be used to endorse or promote products derived from this software
20 #    without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 # SUCH DAMAGE.
33 #
34 # From @(#)vnode_if.sh        8.1 (Berkeley) 6/10/93
35 # From @(#)makedevops.sh 1.1 1998/06/14 13:53:12 dfr Exp $
36 # From @(#)makedevops.sh ?.? 1998/10/05
37 # From src/sys/kern/makedevops.pl,v 1.12 1999/11/22 14:40:04 n_hibma Exp
38 # From src/sys/kern/makeobjops.pl,v 1.8 2001/11/16 02:02:42 joe Exp
39 #
40 # $FreeBSD: src/sys/tools/makeobjops.awk,v 1.3 2003/10/16 13:29:26 dfr Exp $
41
42 #
43 #   Script to produce kobj front-end sugar.
44 #
45
46 function usage ()
47 {
48         print "usage: makeobjops.awk <srcfile.m> [-d] [-p] [-l <nr>] [-c|-h]";
49         print "where -c   produce only .c files";
50         print "      -h   produce only .h files";
51         print "      -p   use the path component in the source file for destination dir";
52         print "      -l   set line width for output files [80]";
53         print "      -d   switch on debugging";
54         exit 1;
55 }
56
57 function warn (msg)
58 {
59         print "makeobjops.awk:", msg > "/dev/stderr";
60 }
61
62 function warnsrc (msg)
63 {
64         warn(src ":" lineno ": " msg);
65 }
66
67 function debug (msg)
68 {
69         if (opt_d)
70                 warn(msg);
71 }
72
73 function die (msg)
74 {
75         warn(msg);
76         exit 1;
77 }
78
79 #   These are just for convenience ...
80 function printc(s) {if (opt_c) print s > ctmpfilename;}
81 function printh(s) {if (opt_h) print s > htmpfilename;}
82
83 #
84 #   If a line exceeds maxlength, split it into multiple
85 #   lines at commas.  Subsequent lines are indented by
86 #   the specified number of spaces.
87 #
88 #   In other words:  Lines are split by replacing ", "
89 #   by ",\n" plus indent spaces.
90 #
91
92 function format_line (line, maxlength, indent)
93 {
94         rline = "";
95
96         while (length(line) > maxlength) {
97                 #
98                 #   Find the rightmost ", " so that the part
99                 #   to the left of it is just within maxlength.
100                 #   If there is none, give up and leave it as-is.
101                 #
102                 if (!match(substr(line, 1, maxlength + 1), /^.*, /))
103                         break;
104                 rline = rline substr(line, 1, RLENGTH - 1) "\n";
105                 line = sprintf("%*s", indent, "") substr(line, RLENGTH + 1);
106         }
107         return rline line;
108 }
109
110 #
111 #   Join an array into a string.
112 #
113
114 function join (separator, array, num)
115 {
116         _result = ""
117         if (num) {
118                 while (num > 1)
119                         _result = separator array[num--] _result;
120                 _result = array[1] _result;
121         }
122         return _result;
123 }
124
125 #
126 #   Execute a system command and report if it failed.
127 #
128
129 function system_check (cmd)
130 {
131         if ((rc = system(cmd)))
132                 warn(cmd " failed (" rc ")");
133 }
134
135 #
136 #   Handle "INTERFACE" line.
137 #
138
139 function handle_interface ()
140 {
141         intname = $2;
142         sub(/;$/, "", intname);
143         if (intname !~ /^[a-z_][a-z0-9_]*$/) {
144                 debug($0);
145                 warnsrc("Invalid interface name '" intname "', use [a-z_][a-z0-9_]*");
146                 error = 1;
147                 return;
148         }
149         if (!/;[        ]*$/)
150                 warnsrc("Semicolon missing at end of line, no problem");
151
152         debug("Interface " intname);
153
154         printh("#ifndef _" intname "_if_h_");
155         printh("#define _" intname "_if_h_\n");
156         printc("#include \"" intname "_if.h\"\n");
157 }
158
159 #
160 #   Handle "CODE" and "HEADER" sections.
161 #   Returns the code as-is.
162 #
163
164 function handle_code ()
165 {
166         code = "\n";
167         getline < src;
168         indent = $0;
169         sub(/[^  ].*$/, "", indent);    # find the indent used
170         while (!/^}/) {
171                 sub("^" indent, "");    # remove the indent
172                 code = code $0 "\n";
173                 getline < src;
174                 lineno++;;
175         }
176         return code;
177 }
178
179 #
180 #   Handle "METHOD" and "STATICMETHOD" sections.
181 #
182
183 function handle_method (static)
184 {
185         #
186         #   Get the return type and function name and delete that from
187         #   the line. What is left is the possibly first function argument
188         #   if it is on the same line.
189         #
190         if (!intname) {
191                 warnsrc("No interface name defined");
192                 error = 1;
193                 return;
194         }
195         sub(/^[^        ]+[     ]+/, "");
196         ret = $0;
197         sub(/[  ]*\{.*$/, "", ret);
198         name = ret;
199         sub(/^.*[       ]/, "", name);  # last element is name of method
200         sub(/[  ]+[^    ]+$/, "", ret); # return type
201         debug("Method: name=" name " return type=" ret);
202
203         sub(/^[^\{]*\{[  ]*/, "");
204
205         if (!name || !ret) {
206                 debug($0);
207                 warnsrc("Invalid method specification");
208                 error = 1;
209                 return;
210         }
211
212         if (name !~ /^[a-z_][a-z_0-9]*$/) {
213                 warnsrc("Invalid method name '" name "', use [a-z_][a-z0-9_]*");
214                 error = 1;
215                 return;
216         }
217
218         if (methods[name]) {
219                 warnsrc("Duplicate method name");
220                 error = 1;
221                 return;
222         }
223         methods[name] = name;
224
225         line = $0;
226         while (line !~ /\}/ && (getline < src) > 0) {
227                 line = line " " $0;
228                 lineno++
229         }
230
231         default = "";
232         if (!match(line, /\};?/)) {
233                 warnsrc("Premature end of file");
234                 error = 1;
235                 return;
236         }
237         extra = substr(line, RSTART + RLENGTH);
238         if (extra ~ /[   ]*DEFAULT[     ]*[a-zA-Z_][a-zA-Z_0-9]*[       ]*;/) {
239                 default = extra;
240                 sub(/.*DEFAULT[  ]*/, "", default);
241                 sub(/[;         ]+.*$/, "", default);
242         }
243         else if (extra && opt_d) {
244                 #   Warn about garbage at end of line.
245                 warnsrc("Ignored '" extra "'");
246         }
247         sub(/\};?.*$/, "", line);
248
249         #
250         #   Create a list of variables without the types prepended.
251         #
252         sub(/^[  ]+/, "", line);        # remove leading ...
253         sub(/[  ]+$/, "", line);        # ... and trailing whitespace
254         gsub(/[  ]+/, " ", line);       # remove double spaces
255
256         num_arguments = split(line, arguments, / *; */) - 1;
257         delete varnames;                # list of varnames
258         num_varnames = 0;
259         for (i = 1; i <= num_arguments; i++) {
260                 if (!arguments[i])
261                         continue;       # skip argument if argument is empty
262                 num_ar = split(arguments[i], ar, /[*    ]+/);
263                 if (num_ar < 2) {       # only 1 word in argument?
264                         warnsrc("no type for '" arguments[i] "'");
265                         error = 1;
266                         return;
267                 }
268                 #   Last element is name of variable.
269                 varnames[++num_varnames] = ar[num_ar];
270         }
271
272         argument_list = join(", ", arguments, num_arguments);
273         varname_list = join(", ", varnames, num_varnames);
274
275         if (opt_d) {
276                 warn("Arguments: " argument_list);
277                 warn("Varnames: " varname_list);
278         }
279
280         mname = intname "_" name;       # method name
281         umname = toupper(mname);        # uppercase method name
282
283         firstvar = varnames[1];
284
285         if (default == "")
286                 default = "kobj_error_method";
287
288         # the method description 
289         printh("extern struct kobjop_desc " mname "_desc;");
290         # the method typedef
291         prototype = "typedef " ret " " mname "_t(";
292         printh(format_line(prototype argument_list ");",
293             line_width, length(prototype)));
294
295         # Print out the method desc
296         printc("struct kobjop_desc " mname "_desc = {");
297         printc("\t0, { &" mname "_desc, (kobjop_t)" default " }");
298         printc("};\n");
299
300         # Print out the method itself
301         if (0) {                # haven't chosen the format yet
302                 printh("static __inline " ret " " umname "(" varname_list ")");
303                 printh("\t" join(";\n\t", arguments, num_arguments) ";");
304         }
305         else {
306                 prototype = "static __inline " ret " " umname "(";
307                 printh(format_line(prototype argument_list ")",
308                     line_width, length(prototype)));
309         }
310         printh("{");
311         printh("\tkobjop_t _m;");
312         if (!static)
313                 firstvar = "((kobj_t)" firstvar ")";
314         printh("\tKOBJOPLOOKUP(" firstvar "->ops, " mname ");");
315         retrn =  (ret != "void") ? "return " : "";
316         printh("\t" retrn "((" mname "_t *) _m)(" varname_list ");");
317         printh("}\n");
318 }
319
320 #
321 #   Begin of the main program.
322 #
323
324 BEGIN {
325
326 line_width = 80;
327 gerror = 0;
328
329 #
330 #   Process the command line.
331 #
332
333 num_files = 0;
334
335 for (i = 1; i < ARGC; i++) {
336         if (ARGV[i] ~ /^-/) {
337                 #
338                 #   awk doesn't have getopt(), so we have to do it ourselves.
339                 #   This is a bit clumsy, but it works.
340                 #
341                 for (j = 2; j <= length(ARGV[i]); j++) {
342                         o = substr(ARGV[i], j, 1);
343                         if      (o == "c")      opt_c = 1;
344                         else if (o == "h")      opt_h = 1;
345                         else if (o == "p")      opt_p = 1;
346                         else if (o == "d")      opt_d = 1;
347                         else if (o == "l") {
348                                 if (length(ARGV[i]) > j) {
349                                         opt_l = substr(ARGV[i], j + 1);
350                                         break;
351                                 }
352                                 else {
353                                         if (++i < ARGC)
354                                                 opt_l = ARGV[i];
355                                         else
356                                                 usage();
357                                 }
358                         }
359                         else
360                                 usage();
361                 }
362         }
363         else if (ARGV[i] ~ /\.m$/)
364                 filenames[num_files++] = ARGV[i];
365         else
366                 usage();
367 }
368
369 if (!num_files || !(opt_c || opt_h))
370         usage();
371
372 if (opt_p)
373         debug("Will produce files in original not in current directory");
374
375 if (opt_l) {
376         if (opt_l !~ /^[0-9]+$/ || opt_l < 1)
377                 die("Invalid line width '" opt_l "'");
378         line_width = opt_l;
379         debug("Line width set to " line_width);
380 }
381
382 for (i = 0; i < num_files; i++)
383         debug("Filename: " filenames[i]);
384
385 for (file_i = 0; file_i < num_files; file_i++) {
386         src = filenames[file_i];
387         cfilename = hfilename = src;
388         sub(/\.m$/, ".c", cfilename);
389         sub(/\.m$/, ".h", hfilename);
390         if (!opt_p) {
391                 sub(/^.*\//, "", cfilename);
392                 sub(/^.*\//, "", hfilename);
393         }
394
395         debug("Processing from " src " to " cfilename " / " hfilename);
396
397         ctmpfilename = cfilename ".tmp";
398         htmpfilename = hfilename ".tmp";
399
400         common_head = \
401             "/*\n" \
402             " * This file is produced automatically.\n" \
403             " * Do not modify anything in here by hand.\n" \
404             " *\n" \
405             " * Created from source file\n" \
406             " *   " src "\n" \
407             " * with\n" \
408             " *   makeobjops.awk\n" \
409             " *\n" \
410             " * See the source file for legal information\n" \
411             " */\n";
412
413         printc(common_head "\n" \
414             "#include <sys/param.h>\n" \
415             "#include <sys/queue.h>\n" \
416             "#include <sys/kernel.h>\n" \
417             "#include <sys/kobj.h>");
418
419         printh(common_head);
420
421         delete methods;         # clear list of methods
422         intname = "";
423         lineno = 0;
424         error = 0;              # to signal clean up and gerror setting
425
426         while (!error && (getline < src) > 0) {
427                 lineno++;
428
429                 #
430                 #   Take special notice of include directives.
431                 #
432                 if (/^#[        ]*include[      ]+["<][^">]+[">]/) {
433                         incld = $0;
434                         sub(/^#[        ]*include[      ]+/, "", incld);
435                         debug("Included file: " incld);
436                         printc("#include " incld);
437                 }
438
439                 sub(/#.*/, "");         # remove comments
440                 sub(/^[  ]+/, "");      # remove leading ...
441                 sub(/[  ]+$/, "");      # ... and trailing whitespace
442
443                 if (/^$/) {             # skip empty lines
444                 }
445                 else if (/^\/\*\*/)     # ... and doxygen comments
446                         while (!/\*\//) {
447                                 getline < src;
448                                 lineno++;
449                         }
450                 else if (/^INTERFACE[   ]+[^    ;]*[    ]*;?[   ]*$/)
451                         handle_interface();
452                 else if (/^CODE[        ]*{$/)
453                         printc(handle_code());
454                 else if (/^HEADER[       ]*{$/)
455                         printh(handle_code());
456                 else if (/^METHOD/)
457                         handle_method(0);
458                 else if (/^STATICMETHOD/)
459                         handle_method(1);
460                 else {
461                         debug($0);
462                         warnsrc("Invalid line encountered");
463                         error = 1;
464                 }
465         }
466
467         #
468         #   Print the final '#endif' in the header file.
469         #
470         printh("#endif /* _" intname "_if_h_ */");
471
472         close (ctmpfilename);
473         close (htmpfilename);
474
475         if (error) {
476                 warn("Output skipped");
477                 system_check("rm -f " ctmpfilename " " htmpfilename);
478                 gerror = 1;
479         }
480         else {
481                 if (opt_c)
482                         system_check("mv -f " ctmpfilename " " cfilename);
483                 if (opt_h)
484                         system_check("mv -f " htmpfilename " " hfilename);
485         }
486 }
487
488 exit gerror;
489
490 }