Add FreeBSD's makeobjops.awk.
[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 # $DragonFly: src/sys/tools/makeobjops.awk,v 1.1 2003/11/16 10:30:06 asmodai 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 kobj_method " mname "_method_default = {");
297         printc("\t&" mname "_desc, (kobjop_t) " default);
298         printc("};\n");
299
300         printc("struct kobjop_desc " mname "_desc = {");
301         printc("\t0, &" mname "_method_default");
302         printc("};\n");
303
304         # Print out the method itself
305         if (0) {                # haven't chosen the format yet
306                 printh("static __inline " ret " " umname "(" varname_list ")");
307                 printh("\t" join(";\n\t", arguments, num_arguments) ";");
308         }
309         else {
310                 prototype = "static __inline " ret " " umname "(";
311                 printh(format_line(prototype argument_list ")",
312                     line_width, length(prototype)));
313         }
314         printh("{");
315         printh("\tkobjop_t _m;");
316         if (!static)
317                 firstvar = "((kobj_t)" firstvar ")";
318         printh("\tKOBJOPLOOKUP(" firstvar "->ops," mname ");");
319         retrn =  (ret != "void") ? "return " : "";
320         printh("\t" retrn "((" mname "_t *) _m)(" varname_list ");");
321         printh("}\n");
322 }
323
324 #
325 #   Begin of the main program.
326 #
327
328 BEGIN {
329
330 line_width = 80;
331 gerror = 0;
332
333 #
334 #   Process the command line.
335 #
336
337 num_files = 0;
338
339 for (i = 1; i < ARGC; i++) {
340         if (ARGV[i] ~ /^-/) {
341                 #
342                 #   awk doesn't have getopt(), so we have to do it ourselves.
343                 #   This is a bit clumsy, but it works.
344                 #
345                 for (j = 2; j <= length(ARGV[i]); j++) {
346                         o = substr(ARGV[i], j, 1);
347                         if      (o == "c")      opt_c = 1;
348                         else if (o == "h")      opt_h = 1;
349                         else if (o == "p")      opt_p = 1;
350                         else if (o == "d")      opt_d = 1;
351                         else if (o == "l") {
352                                 if (length(ARGV[i]) > j) {
353                                         opt_l = substr(ARGV[i], j + 1);
354                                         break;
355                                 }
356                                 else {
357                                         if (++i < ARGC)
358                                                 opt_l = ARGV[i];
359                                         else
360                                                 usage();
361                                 }
362                         }
363                         else
364                                 usage();
365                 }
366         }
367         else if (ARGV[i] ~ /\.m$/)
368                 filenames[num_files++] = ARGV[i];
369         else
370                 usage();
371 }
372
373 if (!num_files || !(opt_c || opt_h))
374         usage();
375
376 if (opt_p)
377         debug("Will produce files in original not in current directory");
378
379 if (opt_l) {
380         if (opt_l !~ /^[0-9]+$/ || opt_l < 1)
381                 die("Invalid line width '" opt_l "'");
382         line_width = opt_l;
383         debug("Line width set to " line_width);
384 }
385
386 for (i = 0; i < num_files; i++)
387         debug("Filename: " filenames[i]);
388
389 for (file_i = 0; file_i < num_files; file_i++) {
390         src = filenames[file_i];
391         cfilename = hfilename = src;
392         sub(/\.m$/, ".c", cfilename);
393         sub(/\.m$/, ".h", hfilename);
394         if (!opt_p) {
395                 sub(/^.*\//, "", cfilename);
396                 sub(/^.*\//, "", hfilename);
397         }
398
399         debug("Processing from " src " to " cfilename " / " hfilename);
400
401         ctmpfilename = cfilename ".tmp";
402         htmpfilename = hfilename ".tmp";
403
404         common_head = \
405             "/*\n" \
406             " * This file is produced automatically.\n" \
407             " * Do not modify anything in here by hand.\n" \
408             " *\n" \
409             " * Created from source file\n" \
410             " *   " src "\n" \
411             " * with\n" \
412             " *   makeobjops.awk\n" \
413             " *\n" \
414             " * See the source file for legal information\n" \
415             " */\n";
416
417         printc(common_head "\n" \
418             "#include <sys/param.h>\n" \
419             "#include <sys/queue.h>\n" \
420             "#include <sys/kernel.h>\n" \
421             "#include <sys/kobj.h>");
422
423         printh(common_head);
424
425         delete methods;         # clear list of methods
426         intname = "";
427         lineno = 0;
428         error = 0;              # to signal clean up and gerror setting
429
430         while (!error && (getline < src) > 0) {
431                 lineno++;
432
433                 #
434                 #   Take special notice of include directives.
435                 #
436                 if (/^#[        ]*include[      ]+["<][^">]+[">]/) {
437                         incld = $0;
438                         sub(/^#[        ]*include[      ]+/, "", incld);
439                         debug("Included file: " incld);
440                         printc("#include " incld);
441                 }
442
443                 sub(/#.*/, "");         # remove comments
444                 sub(/^[  ]+/, "");      # remove leading ...
445                 sub(/[  ]+$/, "");      # ... and trailing whitespace
446
447                 if (/^$/) {             # skip empty lines
448                 }
449                 else if (/^INTERFACE[   ]+[^    ;]*[    ]*;?[   ]*$/)
450                         handle_interface();
451                 else if (/^CODE[        ]*{$/)
452                         printc(handle_code());
453                 else if (/^HEADER[       ]*{$/)
454                         printh(handle_code());
455                 else if (/^METHOD/)
456                         handle_method(0);
457                 else if (/^STATICMETHOD/)
458                         handle_method(1);
459                 else {
460                         debug($0);
461                         warnsrc("Invalid line encountered");
462                         error = 1;
463                 }
464         }
465
466         #
467         #   Print the final '#endif' in the header file.
468         #
469         printh("#endif /* _" intname "_if_h_ */");
470
471         close (ctmpfilename);
472         close (htmpfilename);
473
474         if (error) {
475                 warn("Output skipped");
476                 system_check("rm -f " ctmpfilename " " htmpfilename);
477                 gerror = 1;
478         }
479         else {
480                 if (opt_c)
481                         system_check("mv -f " ctmpfilename " " cfilename);
482                 if (opt_h)
483                         system_check("mv -f " htmpfilename " " hfilename);
484         }
485 }
486
487 exit gerror;
488
489 }