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