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