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