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