sys/types.h ==> sys/param.h
[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 # $DragonFly: src/sys/tools/makeobjops.awk,v 1.4 2004/04/14 18:28:29 joerg Exp $
42
43 #
44 #   Script to produce kobj front-end sugar.
45 #
46
47 function usage ()
48 {
49         print "usage: makeobjops.awk <srcfile.m> [-d] [-p] [-l <nr>] [-c|-h]";
50         print "where -c   produce only .c files";
51         print "      -h   produce only .h files";
52         print "      -p   use the path component in the source file for destination dir";
53         print "      -l   set line width for output files [80]";
54         print "      -d   switch on debugging";
55         exit 1;
56 }
57
58 function warn (msg)
59 {
60         print "makeobjops.awk:", msg > "/dev/stderr";
61 }
62
63 function warnsrc (msg)
64 {
65         warn(src ":" lineno ": " msg);
66 }
67
68 function debug (msg)
69 {
70         if (opt_d)
71                 warn(msg);
72 }
73
74 function die (msg)
75 {
76         warn(msg);
77         exit 1;
78 }
79
80 #   These are just for convenience ...
81 function printc(s) {if (opt_c) print s > ctmpfilename;}
82 function printh(s) {if (opt_h) print s > htmpfilename;}
83
84 #
85 #   If a line exceeds maxlength, split it into multiple
86 #   lines at commas.  Subsequent lines are indented by
87 #   the specified number of spaces.
88 #
89 #   In other words:  Lines are split by replacing ", "
90 #   by ",\n" plus indent spaces.
91 #
92
93 function format_line (line, maxlength, indent)
94 {
95         rline = "";
96
97         while (length(line) > maxlength) {
98                 #
99                 #   Find the rightmost ", " so that the part
100                 #   to the left of it is just within maxlength.
101                 #   If there is none, give up and leave it as-is.
102                 #
103                 if (!match(substr(line, 1, maxlength + 1), /^.*, /))
104                         break;
105                 rline = rline substr(line, 1, RLENGTH - 1) "\n";
106                 line = sprintf("%*s", indent, "") substr(line, RLENGTH + 1);
107         }
108         return rline line;
109 }
110
111 #
112 #   Join an array into a string.
113 #
114
115 function join (separator, array, num)
116 {
117         _result = ""
118         if (num) {
119                 while (num > 1)
120                         _result = separator array[num--] _result;
121                 _result = array[1] _result;
122         }
123         return _result;
124 }
125
126 #
127 #   Execute a system command and report if it failed.
128 #
129
130 function system_check (cmd)
131 {
132         if ((rc = system(cmd)))
133                 warn(cmd " failed (" rc ")");
134 }
135
136 #
137 #   Handle "INTERFACE" line.
138 #
139
140 function handle_interface ()
141 {
142         intname = $2;
143         sub(/;$/, "", intname);
144         if (intname !~ /^[a-z_][a-z0-9_]*$/) {
145                 debug($0);
146                 warnsrc("Invalid interface name '" intname "', use [a-z_][a-z0-9_]*");
147                 error = 1;
148                 return;
149         }
150         if (!/;[        ]*$/)
151                 warnsrc("Semicolon missing at end of line, no problem");
152
153         debug("Interface " intname);
154
155         printh("#ifndef _" intname "_if_h_");
156         printh("#define _" intname "_if_h_\n");
157         printc("#include \"" intname "_if.h\"\n");
158 }
159
160 #
161 #   Handle "CODE" and "HEADER" sections.
162 #   Returns the code as-is.
163 #
164
165 function handle_code ()
166 {
167         code = "\n";
168         getline < src;
169         indent = $0;
170         sub(/[^  ].*$/, "", indent);    # find the indent used
171         while (!/^}/) {
172                 sub("^" indent, "");    # remove the indent
173                 code = code $0 "\n";
174                 getline < src;
175                 lineno++;;
176         }
177         return code;
178 }
179
180 #
181 #   Handle "METHOD" and "STATICMETHOD" sections.
182 #
183
184 function handle_method (static)
185 {
186         #
187         #   Get the return type and function name and delete that from
188         #   the line. What is left is the possibly first function argument
189         #   if it is on the same line.
190         #
191         if (!intname) {
192                 warnsrc("No interface name defined");
193                 error = 1;
194                 return;
195         }
196         sub(/^[^        ]+[     ]+/, "");
197         ret = $0;
198         sub(/[  ]*\{.*$/, "", ret);
199         name = ret;
200         sub(/^.*[       ]/, "", name);  # last element is name of method
201         sub(/[  ]+[^    ]+$/, "", ret); # return type
202         debug("Method: name=" name " return type=" ret);
203
204         sub(/^[^\{]*\{[  ]*/, "");
205
206         if (!name || !ret) {
207                 debug($0);
208                 warnsrc("Invalid method specification");
209                 error = 1;
210                 return;
211         }
212
213         if (name !~ /^[a-z_][a-z_0-9]*$/) {
214                 warnsrc("Invalid method name '" name "', use [a-z_][a-z0-9_]*");
215                 error = 1;
216                 return;
217         }
218
219         if (methods[name]) {
220                 warnsrc("Duplicate method name");
221                 error = 1;
222                 return;
223         }
224         methods[name] = name;
225
226         line = $0;
227         while (line !~ /\}/ && (getline < src) > 0) {
228                 line = line " " $0;
229                 lineno++
230         }
231
232         default = "";
233         if (!match(line, /\};?/)) {
234                 warnsrc("Premature end of file");
235                 error = 1;
236                 return;
237         }
238         extra = substr(line, RSTART + RLENGTH);
239         if (extra ~ /[   ]*DEFAULT[     ]*[a-zA-Z_][a-zA-Z_0-9]*[       ]*;/) {
240                 default = extra;
241                 sub(/.*DEFAULT[  ]*/, "", default);
242                 sub(/[;         ]+.*$/, "", default);
243         }
244         else if (extra && opt_d) {
245                 #   Warn about garbage at end of line.
246                 warnsrc("Ignored '" extra "'");
247         }
248         sub(/\};?.*$/, "", line);
249
250         #
251         #   Create a list of variables without the types prepended.
252         #
253         sub(/^[  ]+/, "", line);        # remove leading ...
254         sub(/[  ]+$/, "", line);        # ... and trailing whitespace
255         gsub(/[  ]+/, " ", line);       # remove double spaces
256
257         num_arguments = split(line, arguments, / *; */) - 1;
258         delete varnames;                # list of varnames
259         num_varnames = 0;
260         for (i = 1; i <= num_arguments; i++) {
261                 if (!arguments[i])
262                         continue;       # skip argument if argument is empty
263                 num_ar = split(arguments[i], ar, /[*    ]+/);
264                 if (num_ar < 2) {       # only 1 word in argument?
265                         warnsrc("no type for '" arguments[i] "'");
266                         error = 1;
267                         return;
268                 }
269                 #   Last element is name of variable.
270                 varnames[++num_varnames] = ar[num_ar];
271         }
272
273         argument_list = join(", ", arguments, num_arguments);
274         varname_list = join(", ", varnames, num_varnames);
275
276         if (opt_d) {
277                 warn("Arguments: " argument_list);
278                 warn("Varnames: " varname_list);
279         }
280
281         mname = intname "_" name;       # method name
282         umname = toupper(mname);        # uppercase method name
283
284         firstvar = varnames[1];
285
286         if (default == "")
287                 default = "kobj_error_method";
288
289         # the method description 
290         printh("extern struct kobjop_desc " mname "_desc;");
291         # the method typedef
292         prototype = "typedef " ret " " mname "_t(";
293         printh(format_line(prototype argument_list ");",
294             line_width, length(prototype)));
295
296         # Print out the method desc
297         printc("struct kobj_method " mname "_method_default = {");
298         printc("\t&" mname "_desc, (kobjop_t) " default);
299         printc("};\n");
300
301         printc("struct kobjop_desc " mname "_desc = {");
302         printc("\t0, &" mname "_method_default");
303         printc("};\n");
304
305         # Print out the method itself
306         if (0) {                # haven't chosen the format yet
307                 printh("static __inline " ret " " umname "(" varname_list ")");
308                 printh("\t" join(";\n\t", arguments, num_arguments) ";");
309         }
310         else {
311                 prototype = "static __inline " ret " " umname "(";
312                 printh(format_line(prototype argument_list ")",
313                     line_width, length(prototype)));
314         }
315         printh("{");
316         printh("\tkobjop_t _m;");
317         if (!static)
318                 firstvar = "((kobj_t)" firstvar ")";
319         printh("\tKOBJOPLOOKUP(" firstvar "->ops," mname ");");
320         retrn =  (ret != "void") ? "return " : "";
321         printh("\t" retrn "((" mname "_t *) _m)(" varname_list ");");
322         printh("}\n");
323 }
324
325 #
326 #   Begin of the main program.
327 #
328
329 BEGIN {
330
331 line_width = 80;
332 gerror = 0;
333
334 #
335 #   Process the command line.
336 #
337
338 num_files = 0;
339
340 for (i = 1; i < ARGC; i++) {
341         if (ARGV[i] ~ /^-/) {
342                 #
343                 #   awk doesn't have getopt(), so we have to do it ourselves.
344                 #   This is a bit clumsy, but it works.
345                 #
346                 for (j = 2; j <= length(ARGV[i]); j++) {
347                         o = substr(ARGV[i], j, 1);
348                         if      (o == "c")      opt_c = 1;
349                         else if (o == "h")      opt_h = 1;
350                         else if (o == "p")      opt_p = 1;
351                         else if (o == "d")      opt_d = 1;
352                         else if (o == "l") {
353                                 if (length(ARGV[i]) > j) {
354                                         opt_l = substr(ARGV[i], j + 1);
355                                         break;
356                                 }
357                                 else {
358                                         if (++i < ARGC)
359                                                 opt_l = ARGV[i];
360                                         else
361                                                 usage();
362                                 }
363                         }
364                         else
365                                 usage();
366                 }
367         }
368         else if (ARGV[i] ~ /\.m$/)
369                 filenames[num_files++] = ARGV[i];
370         else
371                 usage();
372 }
373
374 if (!num_files || !(opt_c || opt_h))
375         usage();
376
377 if (opt_p)
378         debug("Will produce files in original not in current directory");
379
380 if (opt_l) {
381         if (opt_l !~ /^[0-9]+$/ || opt_l < 1)
382                 die("Invalid line width '" opt_l "'");
383         line_width = opt_l;
384         debug("Line width set to " line_width);
385 }
386
387 for (i = 0; i < num_files; i++)
388         debug("Filename: " filenames[i]);
389
390 for (file_i = 0; file_i < num_files; file_i++) {
391         src = filenames[file_i];
392         cfilename = hfilename = src;
393         sub(/\.m$/, ".c", cfilename);
394         sub(/\.m$/, ".h", hfilename);
395         if (!opt_p) {
396                 sub(/^.*\//, "", cfilename);
397                 sub(/^.*\//, "", hfilename);
398         }
399
400         debug("Processing from " src " to " cfilename " / " hfilename);
401
402         ctmpfilename = cfilename ".tmp";
403         htmpfilename = hfilename ".tmp";
404
405         common_head = \
406             "/*\n" \
407             " * This file is produced automatically.\n" \
408             " * Do not modify anything in here by hand.\n" \
409             " *\n" \
410             " * Created from source file\n" \
411             " *   " src "\n" \
412             " * with\n" \
413             " *   makeobjops.awk\n" \
414             " *\n" \
415             " * See the source file for legal information\n" \
416             " */\n";
417
418         printc(common_head "\n" \
419             "#include <sys/param.h>\n" \
420             "#include <sys/queue.h>\n" \
421             "#include <sys/kernel.h>\n" \
422             "#include <sys/kobj.h>");
423
424         printh(common_head);
425
426         delete methods;         # clear list of methods
427         intname = "";
428         lineno = 0;
429         error = 0;              # to signal clean up and gerror setting
430
431         while (!error && (getline < src) > 0) {
432                 lineno++;
433
434                 #
435                 #   Take special notice of include directives.
436                 #
437                 if (/^#[        ]*include[      ]+["<][^">]+[">]/) {
438                         incld = $0;
439                         sub(/^#[        ]*include[      ]+/, "", incld);
440                         debug("Included file: " incld);
441                         printc("#include " incld);
442                 }
443
444                 sub(/#.*/, "");         # remove comments
445                 sub(/^[  ]+/, "");      # remove leading ...
446                 sub(/[  ]+$/, "");      # ... and trailing whitespace
447
448                 if (/^$/) {             # skip empty lines
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 }