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