#! /usr/bin/env awk # # This script recreates C files containing header-specific boilerplate stuff # using the given list of headers (usually obtained from the master structure). # # It can also create a parser table. # # -------------------------------------------------------------------- # # This file is part of the Sofia-SIP package # # Copyright (C) 2005 Nokia Corporation. # # Contact: Pekka Pessi # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 2.1 of # the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA # # -------------------------------------------------------------------- # # Contributor(s): Pekka.Pessi@nokia.com. # # Created: Fri Apr 6 12:59:59 2001 ppessi # BEGIN { "date '+%a %b %e %H:%M:%S %Y'" | getline date; ascii = \ " " \ " !\"#$%&'()*+,-./0123456789:;<=>?" \ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" \ "`abcdefghijklmnopqrstuvwxyz{|}~"; lower_case = "abcdefghijklmnopqrstuvwxyz"; N=0; # Initialize these as arrays split("", symbols); split("", names); split("", comments); split("", hashes); split("", NAMES); split("", Comments); split("", COMMENTS); # indexed by the C name of the header split("", Since); # Non-NUL if extra split("", Extra); # Offset in extra headers template=""; template1=""; template2=""; template3=""; prefix=""; tprefix=""; extra=0; failed=0; success=0; extra_struct = "msg_pub_extra"; ERRNO="error"; } function name_hash (name) { hash = 0; len = split(name, chars, ""); for (i = 1; i <= len; i++) { c = tolower(chars[i]); hash = (38501 * (hash + index(ascii, c))) % 65536; } if (0) { # Test that hash algorithm above agrees with the C version pipe = ("../msg/msg_name_hash " name); pipe | getline; close(pipe); if (hash != $0) { print name ": " hash " != " $0 > "/dev/stderr"; } } return hash ""; } # # Replace magic patterns in template p with header-specific values # function protos (name, comment, hash, since) { NAME=toupper(name); sub(/.*[\/][*][*][<][ ]*/, "", comment); sub(/[ ]*[*][\/].*/, "", comment); sub(/[ ]+/, " ", comment); short = match(comment, /[(][a-z][)]/); if (short) { short = substr(comment, short + 1, 1); sub(/ *[(][a-z][)]/, "", comment); shorts[index(lower_case, short)] = name; } do_hash = hash == 0; if (do_hash) { split(comment, parts, " "); name2 = tolower(parts[1]); gsub(/-/, "_", name2); if (name2 != name && name2 != tprefix "_" name) { print name " mismatch with " comment " (" real ")" > "/dev/stderr"; } hash = name_hash(parts[1]); hashed[name] = hash; if (comment !~ /header/) { comment = comment " header"; } } Comment = comment; if (!do_hash) { comment = tolower(comment); } COMMENT = toupper(comment); # Store the various forms into an array for the footer processing N++; hashes[N] = hash; names[N] = name; NAMES[N] = NAME; comments[N] = comment; Comments[N] = comment; COMMENTS[N] = COMMENT; symbols[name] = comment; if (since) { Since[name] = since; Extra[name] = extra++; } if (PR) { replace(template, hash, name, NAME, comment, Comment, COMMENT, since); replace(template1, hash, name, NAME, comment, Comment, COMMENT, since); replace(template2, hash, name, NAME, comment, Comment, COMMENT, since); replace(template3, hash, name, NAME, comment, Comment, COMMENT, since); } } function replace (p, hash, name, NAME, comment, Comment, COMMENT, since) { # # Replace various forms of header name in template, print it out # if (p) { gsub(/#hash#/, hash, p); gsub(/#xxxxxx#/, name, p); gsub(/#XXXXXX#/, NAME, p); gsub(/#xxxxxxx_xxxxxxx#/, comment, p); gsub(/#Xxxxxxx_Xxxxxxx#/, Comment, p); gsub(/#XXXXXXX_XXXXXXX#/, COMMENT, p); if (since) { gsub(/#version#/, since, p); } else { # Remove line with #version# gsub(/\n[^\n]*#version#[^\n]*\n/, "\n", p); } print p > PR; } } # # Repeat each line in the footer containing the magic replacement # pattern with a instance of all headers # function process_footer (text) { if (!match(tolower(text), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) { n = length(text); while (substr(text, n) == "\n") { n = n - 1; text = substr(text, 1, n); } if (n > 0) print text > PR; return; } n = split(text, lines, RS); for (i = 1; i <= n; i++) { l = lines[i]; if (match(tolower(l), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) { for (j = 1; j <= N; j++) { l = lines[i]; gsub(/#hash#/, hashes[j], l); gsub(/#xxxxxxx_xxxxxxx#/, comments[j], l); gsub(/#Xxxxxxx_Xxxxxxx#/, Comments[j], l); gsub(/#XXXXXXX_XXXXXXX#/, COMMENTS[j], l); gsub(/#xxxxxx#/, names[j], l); gsub(/#XXXXXX#/, NAMES[j], l); print l > PR; } } else { print l > PR; } } } # # Read flags used with headers # function read_header_flags (flagfile, line, tokens, name, value) { while ((getline line < flagfile) > 0) { sub(/^[ \t]+/, "", line); sub(/[ \t]+$/, "", line); if (line ~ /^#/ || line ~ /^$/) continue; split(line, tokens, /[ \t]*=[ \t]*/); name = tolower(tokens[1]); gsub(/-/, "_", name); gsub(/,/, " ", name); # print "FLAG: " name " = " tokens[2] if (header_flags[name]) { print flagfile ": already defined " tokens[1]; } else if (!symbols[name]) { print flagfile ": unknown header \"" tokens[1] "\""; } else { header_flags[name] = tokens[2]; } } close(flagfile); } # # Read in templates # function templates () { if (!auto) { auto = FILENAME; if (!prefix) { prefix = module; } if (!tprefix) { tprefix = prefix; } sub(/.*\//, "", auto); auto = "This file is automatically generated from <" auto "> by msg_parser.awk."; if (PR) { if (TEMPLATE == "") { TEMPLATE = PR ".in"; } RS0=RS; RS="\f\n"; if ((getline theader < TEMPLATE) < 0) { print ( TEMPLATE ": " ERRNO ); failed=1; exit(1); } getline header < TEMPLATE; getline template < TEMPLATE; getline footer < TEMPLATE; if (TEMPLATE1) { if ((getline dummy < TEMPLATE1) < 0) { print(TEMPLATE1 ": " ERRNO); failed=1; exit(1); } getline dummy < TEMPLATE1; getline template1 < TEMPLATE1; getline dummy < TEMPLATE1; } if (TEMPLATE2) { if ((getline dummy < TEMPLATE2) < 0) { print( TEMPLATE2 ": " ERRNO ); failed=1; exit(1); } getline dummy < TEMPLATE2; getline template2 < TEMPLATE2; getline dummy < TEMPLATE2; } if (TEMPLATE3) { if ((getline dummy < TEMPLATE3) < 0) { print( TEMPLATE3 ": " ERRNO ); failed=1; exit(1); } getline dummy < TEMPLATE3; getline template3 < TEMPLATE3; getline dummy < TEMPLATE3; } sub(/.*[\/]/, "", TEMPLATE); gsub(/#AUTO#/, auto, header); gsub(/#DATE#/, "@date Generated: " date, header); if (PACKAGE_NAME) gsub(/#PACKAGE_NAME#/, PACKAGE_NAME, header); if (PACKAGE_VERSION) gsub(/#PACKAGE_VERSION#/, PACKAGE_VERSION, header); print header > PR; RS=RS0; } if (!NO_FIRST) { protos("request", "/**< Request line */", -1); protos("status", "/**< Status line */", -2); } } } /^#### EXTRA HEADER LIST STARTS HERE ####$/ { HLIST=1; templates(); } HLIST && /^[a-z]/ { protos($1, $0, 0, $2); headers[total++] = $1; } /^#### EXTRA HEADER LIST ENDS HERE ####$/ { HLIST=0; } /^ *\/\* === Headers start here \*\// { in_header_list=1; templates(); } /^ *\/\* === Headers end here \*\// { in_header_list=0; } PT && /^ *\/\* === Hash headers end here \*\// { in_header_list=0;} in_header_list && /^ (sip|rtsp|http|msg|mp)_[a-z_0-9]+_t/ { n=$0 sub(/;.*$/, "", n); sub(/^ *(sip|rtsp|http|msg|mp)_[a-z0-9_]*_t[ ]*/, "", n); sub(/^[*](sip|rtsp|http|msg|mp)_/, "", n); if ($0 !~ /[\/][*][*][<]/) { getline; } if ($0 !~ /[\/][*][*][<]/) { printf "msg_protos.awk: header %s is malformed\n", n; failed=1; exit 1; } if (!NO_MIDDLE) protos(n, $0, 0); headers[total++] = n; } END { if (failed) { exit }; if (!NO_LAST) { protos("unknown", "/**< Unknown headers */", -3); protos("error", "/**< Erroneous headers */", -4); protos("separator", "/**< Separator line between headers and payload */", -5); protos("payload", "/**< Message payload */", -6); if (multipart) protos("multipart", "/**< Multipart payload */", -7); } if (PR) { process_footer(footer); } else if (PT) { if (FLAGFILE) read_header_flags(FLAGFILE); if (TEMPLATE == "") { TEMPLATE = PT ".in"; } RS0=RS; RS=" \n"; getline theader < TEMPLATE; getline header < TEMPLATE; getline template < TEMPLATE; getline footer < TEMPLATE; RS=RS0; module_struct = module "_t"; sub(/.*[\/]/, "", TEMPLATE); gsub(/#AUTO#/, auto, header); gsub(/#DATE#/, "@date Generated: " date, header); print header > PT; if (MC_SHORT_SIZE) { printf("static msg_href_t const " \ "%s_short_forms[MC_SHORT_SIZE] = \n{\n", module) > PT; for (i = 1; i <= MC_SHORT_SIZE; i = i + 1) { c = (i == MC_SHORT_SIZE) ? "" : ","; if (i in shorts) { n = shorts[i]; flags = header_flags[n]; if (flags) flags = ",\n " flags; printf(" { /* %s */ %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n", substr(lower_case, i, 1), tprefix, n, module, prefix, n, flags, c) \ > PT; } else { printf(" { NULL }%s\n", c) \ > PT; } } printf("};\n\n") > PT; } # printf("extern msg_hclass_t msg_multipart_class[];\n\n") > PT; if (extra > 0) { printf("struct %s {\n", extra_struct) > PT; printf(" %s base;\n", module_struct) > PT; printf(" msg_header_t *extra[%u];\n", extra) > PT; printf("};\n\n") > PT; module_struct = "struct " extra_struct; } printf("msg_mclass_t const %s_mclass[1] = \n{{\n", module) > PT; printf("# if defined (%s_HCLASS)\n", toupper(module)) > PT; printf(" %s_HCLASS,\n", toupper(module)) > PT; printf("#else\n") > PT; printf(" {{ 0 }},\n") > PT; printf("#endif\n") > PT; printf(" %s_VERSION_CURRENT,\n", toupper(module)) > PT; printf(" %s_PROTOCOL_TAG,\n", toupper(module)) > PT; printf("#if defined (%s_PARSER_FLAGS)\n", toupper(module)) > PT; printf(" %s_PARSER_FLAGS,\n", toupper(module)) > PT; printf("#else\n") > PT; printf(" 0,\n") > PT; printf("#endif\n") > PT; printf(" sizeof (%s),\n", module_struct) > PT; printf(" %s_extract_body,\n", module) > PT; len = split("request status separator payload unknown error", unnamed, " "); for (i = 1; i <= len; i++) { printf(" {{ %s_%s_class, offsetof(%s_t, %s_%s) }},\n", tprefix, unnamed[i], module, prefix, unnamed[i]) > PT; } if (multipart) { printf(" {{ %s_class, offsetof(%s_t, %s_multipart) }},\n", multipart, module, prefix) > PT; } else { printf(" {{ NULL, 0 }},\n") > PT; } if (MC_SHORT_SIZE) { printf(" %s_short_forms, \n", module) > PT; } else { printf(" NULL, \n") > PT; } printf(" %d, %d, \n", MC_HASH_SIZE, total) > PT; printf(" {\n") > PT; for (i = 0; i < total; i++) { n = headers[i]; h = hashed[n]; if (h < 0) continue; j = h % MC_HASH_SIZE; if (j == -0) j = 0; for (; j in header_hash;) { if (++j == MC_HASH_SIZE) { j = 0; } } header_hash[j] = n; } for (i = 0; i < MC_HASH_SIZE; i++) { c = (i + 1 == MC_HASH_SIZE) ? "" : ","; if (i in header_hash) { n = header_hash[i]; flags = header_flags[n]; if (flags) flags = ",\n " flags; if (Since[n]) { printf(" { %s_%s_class, offsetof(struct %s, extra[%u])%s }%s\n", tprefix, n, extra_struct, Extra[n], flags, c) > PT; } else { printf(" { %s_%s_class, offsetof(%s_t, %s_%s)%s }%s\n", tprefix, n, module, prefix, n, flags, c) > PT; } } else { printf(" { NULL, 0 }%s\n", c) > PT; } } printf(" }\n}};\n\n") > PT; } exit success; }