1#! /usr/bin/env awk
2#
3# This script recreates C files containing header-specific boilerplate stuff
4# using the given list of headers (usually obtained from the master structure).
5#
6# It can also create a parser table.
7#
8# --------------------------------------------------------------------
9#
10# This file is part of the Sofia-SIP package
11#
12# Copyright (C) 2005 Nokia Corporation.
13#
14# Contact: Pekka Pessi <pekka.pessi@nokia.com>
15#
16# This library is free software; you can redistribute it and/or
17# modify it under the terms of the GNU Lesser General Public License
18# as published by the Free Software Foundation; either version 2.1 of
19# the License, or (at your option) any later version.
20#
21# This library is distributed in the hope that it will be useful, but
22# WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24# Lesser General Public License for more details.
25#
26# You should have received a copy of the GNU Lesser General Public
27# License along with this library; if not, write to the Free Software
28# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
29# 02110-1301 USA
30#
31# --------------------------------------------------------------------
32#
33# Contributor(s): Pekka.Pessi@nokia.com.
34#
35# Created: Fri Apr  6 12:59:59 2001 ppessi
36#
37
38BEGIN {
39  "date '+%a %b %e %H:%M:%S %Y'" | getline date;
40
41  ascii =			       \
42     "                               " \
43    " !\"#$%&'()*+,-./0123456789:;<=>?" \
44    "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" \
45    "`abcdefghijklmnopqrstuvwxyz{|}~";
46  lower_case = "abcdefghijklmnopqrstuvwxyz";
47
48  N=0;
49  # Initialize these as arrays
50  split("", symbols);
51  split("", names);
52  split("", comments);
53  split("", hashes);
54  split("", NAMES);
55  split("", Comments);
56  split("", COMMENTS);
57
58  # indexed by the C name of the header
59  split("", Since);		# Non-NUL if extra
60  split("", Extra);		# Offset in extra headers
61
62  total = 0;
63  ordinary = 0;
64  basic = 0;
65  extra = 0;
66  without_experimental = 0;
67
68  template="";
69  template1="";
70  template2="";
71  template3="";
72  prefix="";
73  tprefix="";
74  failed=0;
75  success=0;
76
77  ERRNO="error";
78}
79
80function name_hash (name)
81{
82  hash = 0;
83
84  len = length(name);
85
86  for (i = 1; i <= len; i++) {
87    c = tolower(substr(name, i, 1));
88    hash = (38501 * (hash + index(ascii, c))) % 65536;
89  }
90
91  if (hash == 0) {
92    print "*** msg_parser.awk: calculating hash failed\n";
93    exit(5);
94  }
95
96  if (0) {
97    # Test that hash algorithm above agrees with the C version
98    pipe = ("../msg/msg_name_hash " name);
99    pipe | getline;
100    close(pipe);
101    if (hash != $0) {
102      print name ": " hash " != " $0 > "/dev/stderr";
103    }
104  }
105
106  return hash "";
107}
108
109#
110# Replace magic patterns in template p with header-specific values
111#
112function protos (name, comment, hash, since)
113{
114  NAME=toupper(name);
115  sub(/.*[\/][*][*][<][ 	]*/, "", comment);
116  sub(/[ 	]*[*][\/].*/, "", comment);
117  sub(/[ 	]+/, " ", comment);
118
119  short = match(comment, /[(][a-z][)]/);
120  if (short) {
121    short = substr(comment, short + 1, 1);
122    sub(/ *[(][a-z][)]/, "", comment);
123    shorts[index(lower_case, short)] = name;
124  }
125
126  do_hash = hash == 0;
127
128  if (do_hash) {
129    split(comment, parts, " ");
130    name2 = tolower(parts[1]);
131    gsub(/-/, "_", name2);
132    if (name2 != name && name2 != tprefix "_" name) {
133      print name " mismatch with " comment " (" real ")" > "/dev/stderr";
134    }
135
136    hash = name_hash(parts[1]);
137
138    hashed[name] = hash;
139
140    if (comment !~ /header/) {
141      comment = comment " header";
142    }
143  }
144
145  Comment = comment;
146  if (!do_hash) {
147    comment = tolower(comment);
148  }
149  COMMENT = toupper(comment);
150
151  # Store the various forms into an array for the footer processing
152  N++;
153  hashes[N] = hash;
154  names[N] = name;
155  NAMES[N] = NAME;
156  comments[N] = comment;
157  Comments[N] = comment;
158  COMMENTS[N] = COMMENT;
159
160  symbols[name] = comment;
161  if (since) {
162    Since[name] = since;
163  }
164
165  expr = (without_experimental > 0 && do_hash);
166  if (expr) {
167    printf "%s is experimental\n", Comment;
168  }
169
170  experimental[N] = expr;
171
172  if (PR) {
173    if (expr) {
174      print "#if SU_HAVE_EXPERIMENTAL" > PR;
175    }
176    replace(template, hash, name, NAME, comment, Comment, COMMENT, since);
177    replace(template1, hash, name, NAME, comment, Comment, COMMENT, since);
178    replace(template2, hash, name, NAME, comment, Comment, COMMENT, since);
179    replace(template3, hash, name, NAME, comment, Comment, COMMENT, since);
180    if (expr) {
181      print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
182    }
183  }
184}
185
186function replace (p, hash, name, NAME, comment, Comment, COMMENT, since)
187{
188  #
189  # Replace various forms of header name in template, print it out
190  #
191  if (p) {
192    gsub(/#hash#/, hash, p);
193    gsub(/#xxxxxx#/, name, p);
194    gsub(/#XXXXXX#/, NAME, p);
195    gsub(/#xxxxxxx_xxxxxxx#/, comment, p);
196    gsub(/#Xxxxxxx_Xxxxxxx#/, Comment, p);
197    gsub(/#XXXXXXX_XXXXXXX#/, COMMENT, p);
198
199    if (since) {
200      gsub(/#version#/, since, p);
201    }
202    else {
203      # Remove line with #version#
204      gsub(/\n[^#\n]*#version#[^\n]*/, "", p);
205    }
206
207    print p > PR;
208  }
209}
210
211#
212# Repeat each line in the footer containing the magic replacement
213# pattern with an instance of all headers
214#
215function process_footer (text)
216{
217  if (!match(tolower(text), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
218    n = length(text);
219    while (substr(text, n) == "\n") {
220      n = n - 1;
221      text = substr(text, 1, n);
222    }
223    if (n > 0)
224      print text > PR;
225    return;
226  }
227
228  n = split(text, lines, RS);
229
230  for (i = 1; i <= n; i++) {
231    l = lines[i];
232    if (match(tolower(l), /#(xxxxxx(x_xxxxxxx)?|hash)#/)) {
233      expr = 0;
234
235      for (j = 1; j <= N; j++) {
236	l = lines[i];
237	if (expr != experimental[j]) {
238	  expr = experimental[j];
239	  if (expr) {
240	    print "#if SU_HAVE_EXPERIMENTAL" > PR;
241	  }
242	  else {
243	    print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
244	  }
245	}
246	gsub(/#hash#/, hashes[j], l);
247	gsub(/#xxxxxxx_xxxxxxx#/, comments[j], l);
248	gsub(/#Xxxxxxx_Xxxxxxx#/, Comments[j], l);
249	gsub(/#XXXXXXX_XXXXXXX#/, COMMENTS[j], l);
250	gsub(/#xxxxxx#/, names[j], l);
251	gsub(/#XXXXXX#/, NAMES[j], l);
252	print l > PR;
253      }
254
255      if (expr) {
256	print "#endif /* SU_HAVE_EXPERIMENTAL */" > PR;
257      }
258    } else {
259      print l > PR;
260    }
261  }
262}
263
264#
265# Read flags used with headers
266#
267function read_header_flags (flagfile,    line, tokens, name, value)
268{
269  while ((getline line < flagfile) > 0) {
270    sub(/^[ \t]+/, "", line);
271    sub(/[ \t]+$/, "", line);
272    if (line ~ /^#/ || line ~ /^$/)
273      continue;
274
275    split(line, tokens,  /[ \t]*=[ \t]*/);
276    name = tolower(tokens[1]);
277    gsub(/-/, "_", name);
278    gsub(/,/, " ", name);
279    # print "FLAG: " name " = " tokens[2]
280
281    if (header_flags[name]) {
282      print flagfile ": already defined " tokens[1];
283    }
284    else if (!symbols[name]) {
285      print flagfile ": unknown header \"" tokens[1] "\"";
286    }
287    else {
288      header_flags[name] = tokens[2];
289    }
290  }
291  close(flagfile);
292}
293
294#
295# Read in templates
296#
297function templates ()
298{
299  if (!auto) {
300    auto = FILENAME;
301
302    if (!prefix) { prefix = module; }
303    if (!tprefix) { tprefix = prefix; }
304
305    sub(/.*\//, "", auto);
306    auto = "This file is automatically generated from <" auto "> by msg_parser.awk.";
307
308    if (PR) {
309      if (TEMPLATE == "") { TEMPLATE = PR ".in"; }
310      RS0=RS; RS="\f\n";
311      if ((getline theader < TEMPLATE) < 0) {
312	print ( TEMPLATE ": " ERRNO );
313	failed=1;
314        exit(1);
315      }
316      getline header < TEMPLATE;
317      getline template < TEMPLATE;
318      getline footer < TEMPLATE;
319
320      if (TEMPLATE1) {
321	if ((getline dummy < TEMPLATE1) < 0) {
322	  print(TEMPLATE1 ": " ERRNO);
323	  failed=1;
324          exit(1);
325        }
326	getline dummy < TEMPLATE1;
327	getline template1 < TEMPLATE1;
328	getline dummy < TEMPLATE1;
329      }
330
331      if (TEMPLATE2) {
332	if ((getline dummy < TEMPLATE2) < 0) {
333	  print( TEMPLATE2 ": " ERRNO );
334	  failed=1;
335	  exit(1);
336	}
337	getline dummy < TEMPLATE2;
338	getline template2 < TEMPLATE2;
339	getline dummy < TEMPLATE2;
340      }
341
342      if (TEMPLATE3) {
343	if ((getline dummy < TEMPLATE3) < 0) {
344	  print( TEMPLATE3 ": " ERRNO );
345	  failed=1;
346	  exit(1);
347	}
348	getline dummy < TEMPLATE3;
349	getline template3 < TEMPLATE3;
350	getline dummy < TEMPLATE3;
351      }
352
353      sub(/.*[\/]/, "", TEMPLATE);
354      gsub(/#AUTO#/, auto, header);
355      gsub(/#DATE#/, "@date Generated: " date, header);
356      if (PACKAGE_NAME) gsub(/#PACKAGE_NAME#/, PACKAGE_NAME, header);
357      if (PACKAGE_VERSION) gsub(/#PACKAGE_VERSION#/, PACKAGE_VERSION, header);
358      print header > PR;
359
360      RS=RS0;
361    }
362
363    if (!NO_FIRST) {
364      protos("request", "/**< Request line */", -1);
365      protos("status", "/**< Status line */", -2);
366    }
367  }
368}
369
370/^#### EXTRA HEADER LIST STARTS HERE ####$/ { HLIST=1; templates(); }
371HLIST && /^#### DEFAULT HEADER LIST ENDS HERE ####$/ { basic=total; }
372HLIST && /^#### EXPERIMENTAL HEADER LIST STARTS HERE ####$/ {
373  without_experimental = total; }
374
375HLIST && /^[a-z]/ { protos($1, $0, 0, $2);
376  headers[total++] = $1;
377  Extra[$1] = extra++;
378}
379/^#### EXTRA HEADER LIST ENDS HERE ####$/ { HLIST=0;  }
380
381/^ *\/\* === Headers start here \*\// { in_header_list=1;  templates(); }
382/^ *\/\* === Headers end here \*\// { in_header_list=0; }
383
384PT && /^ *\/\* === Hash headers end here \*\// { in_header_list=0;}
385
386in_header_list && /^  (sip|rtsp|http|msg|mp)_[a-z_0-9]+_t/ {
387  n=$0
388  sub(/;.*$/, "", n);
389  sub(/^ *(sip|rtsp|http|msg|mp)_[a-z0-9_]*_t[ 	]*/, "", n);
390  sub(/^[*](sip|rtsp|http|msg|mp)_/, "", n);
391
392  if ($0 !~ /[\/][*][*][<]/) {
393    getline;
394  }
395  if ($0 !~ /[\/][*][*][<]/) {
396    printf "msg_protos.awk: header %s is malformed\n", n;
397    failed=1;
398    exit 1;
399  }
400
401  if (!NO_MIDDLE)
402    protos(n, $0, 0);
403
404  headers[total] = n; total++; ordinary++;
405}
406
407function print_parser_table(struct, scope, name, N, N_EXPERIMENTAL)
408{
409  if (PT) {
410    if (N > ordinary) {
411      printf("/* Ordinary %u, extra %u, experimental %u */\n",
412	     ordinary, N - ordinary, N_EXPERIMENTAL - N) > PT;
413      printf("struct %s {\n", struct) > PT;
414      printf("  %s_t base;\n", module) > PT;
415      printf("  msg_header_t *extra[%u];\n", N - ordinary) > PT;
416      if (N != N_EXPERIMENTAL) {
417	print "#if SU_HAVE_EXPERIMENTAL" > PT;
418	printf("  msg_header_t *extra[%u];\n", N_EXPERIMENTAL - N) > PT;
419	print "#endif" > PT;
420      }
421      printf("};\n\n") > PT;
422    }
423
424    printf("%s\n", scope) > PT;
425    printf("msg_mclass_t const %s[1] = \n{{\n", name) > PT;
426    printf("# if defined (%s_HCLASS)\n", toupper(module)) > PT;
427    printf("  %s_HCLASS,\n", toupper(module)) > PT;
428    printf("#else\n") > PT;
429    printf("  {{ 0 }},\n") > PT;
430    printf("#endif\n") > PT;
431    printf("  %s_VERSION_CURRENT,\n", toupper(module)) > PT;
432    printf("  %s_PROTOCOL_TAG,\n", toupper(module)) > PT;
433    printf("#if defined (%s_PARSER_FLAGS)\n", toupper(module)) > PT;
434    printf("  %s_PARSER_FLAGS,\n", toupper(module)) > PT;
435    printf("#else\n") > PT;
436    printf("  0,\n") > PT;
437    printf("#endif\n") > PT;
438    if (N > ordinary) {
439      printf("  sizeof (struct %s),\n", struct) > PT;
440    }
441    else {
442      printf("  sizeof (%s_t),\n", module) > PT;
443    }
444    printf("  %s_extract_body,\n", module) > PT;
445
446    len = split("request status separator payload unknown error", unnamed, " ");
447
448    for (i = 1; i <= len; i++) {
449      printf("  {{ %s_%s_class, msg_offsetof(%s_t, %s_%s) }},\n",
450	     tprefix, unnamed[i], module, prefix, unnamed[i]) > PT;
451    }
452    if (multipart) {
453      printf("  {{ %s_class, msg_offsetof(%s_t, %s_multipart) }},\n",
454	     multipart, module, prefix) > PT;
455    } else {
456      printf("  {{ NULL, 0 }},\n") > PT;
457    }
458    if (MC_SHORT_SIZE) {
459      printf("  %s_short_forms, \n", module) > PT;
460    }
461    else {
462      printf("  NULL, \n") > PT;
463    }
464    printf("  %d, \n", MC_HASH_SIZE) > PT;
465    if (N != N_EXPERIMENTAL) {
466      print "#if SU_HAVE_EXPERIMENTAL" > PT;
467      printf("  %d,\n", N_EXPERIMENTAL) > PT;
468      print "#else" > PT;
469    }
470    printf("  %d,\n", N) > PT;
471    if (N != N_EXPERIMENTAL) {
472      print "#endif" > PT;
473    }
474
475    printf("  {\n") > PT;
476
477    for (j = 0; j < MC_HASH_SIZE; j++) {
478      c = (j + 1 == MC_HASH_SIZE) ? "" : ",";
479      if (j in header_hash) {
480	n = header_hash[j];
481	i = index_hash[j];
482
483        flags = header_flags[n]; if (flags) flags = ",\n      " flags;
484
485	if (i >= N) {
486	  print "#if SU_HAVE_EXPERIMENTAL" > PT;
487	}
488
489	if (i >= ordinary) {
490	  printf("    { %s_%s_class,\n" \
491		 "      msg_offsetof(struct %s, extra[%u])%s }%s\n",
492		 tprefix, n, struct, Extra[n], flags, c) > PT;
493	}
494	else {
495	  printf("    { %s_%s_class, msg_offsetof(%s_t, %s_%s)%s }%s\n",
496		 tprefix, n, module, prefix, n, flags, c) > PT;
497	}
498
499	if (i >= N) {
500	  printf("#else\n    { NULL, 0 }%s\n#endif\n", c) > PT;
501	}
502      }
503      else {
504	printf("    { NULL, 0 }%s\n", c) > PT;
505      }
506    }
507    printf("  }\n}};\n\n") > PT;
508
509    }
510}
511
512END {
513  if (failed) { exit };
514
515  if (!NO_LAST) {
516    protos("unknown", "/**< Unknown headers */", -3);
517    protos("error", "/**< Erroneous headers */", -4);
518    protos("separator", "/**< Separator line between headers and body */", -5);
519    protos("payload", "/**< Message payload */", -6);
520    if (multipart)
521      protos("multipart", "/**< Multipart payload */", -7);
522  }
523
524  if (PR) {
525    process_footer(footer);
526  }
527  else if (PT) {
528    if (FLAGFILE)
529      read_header_flags(FLAGFILE);
530
531    if (TEMPLATE == "") { TEMPLATE = PT ".in"; }
532    RS0=RS; RS="\n";
533    getline theader < TEMPLATE;
534    getline header < TEMPLATE;
535    getline template < TEMPLATE;
536    getline footer < TEMPLATE;
537    RS=RS0;
538
539    sub(/.*[\/]/, "", TEMPLATE);
540    gsub(/#AUTO#/, auto, header);
541    gsub(/#DATE#/, "@date Generated: " date, header);
542    print header > PT;
543
544    print "" > PT;
545    print "#define msg_offsetof(s, f) ((unsigned short)offsetof(s ,f))" > PT;
546    print "" > PT;
547
548    if (MC_SHORT_SIZE) {
549      printf("static msg_href_t const " \
550	     "%s_short_forms[MC_SHORT_SIZE] = \n{\n",
551	     module) > PT;
552
553      for (i = 1; i <= MC_SHORT_SIZE; i = i + 1) {
554	c = (i == MC_SHORT_SIZE) ? "" : ",";
555	if (i in shorts) {
556	  n = shorts[i];
557        flags = header_flags[n]; if (flags) flags = ",\n      " flags;
558
559	printf("  { /* %s */ %s_%s_class, msg_offsetof(%s_t, %s_%s)%s }%s\n",
560	       substr(lower_case, i, 1),
561	       tprefix, n, module, prefix, n, flags, c)	\
562	    > PT;
563	}
564	else {
565	  printf("  { NULL }%s\n", c) \
566	    > PT;
567	}
568      }
569      printf("};\n\n") > PT;
570    }
571
572    # printf("extern msg_hclass_t msg_multipart_class[];\n\n") > PT;
573
574    if (basic == 0) basic = total;
575    if (without_experimental == 0) without_experimental = total;
576
577    split("", header_hash);
578    split("", index_hash);
579
580    for (i = 0; i < basic; i++) {
581      n = headers[i];
582      h = hashed[n];
583
584      if (h < 0)
585	continue;
586
587      j = h % MC_HASH_SIZE; if (j == -0) j = 0;
588
589      for (; j in header_hash;) {
590	if (++j == MC_HASH_SIZE) {
591	  j = 0;
592	}
593      }
594
595      header_hash[j] = n;
596      index_hash[j] = i;
597    }
598
599    m = module "_mclass";
600    s = "_d_" module "_t";
601
602    # Add basic headers
603    if (ordinary == basic) {
604      print_parser_table(s, "", m, basic, basic);
605    }
606    else if (basic < without_experimental) {
607      print_parser_table(s, "", m, basic, basic);
608    }
609    else {
610      print_parser_table(s, "", m, without_experimental, basic);
611   }
612
613   if (0) {
614
615   # Hash extra headers
616   for (i = basic; i < total; i++) {
617      n = headers[i];
618      h = hashed[n];
619
620      if (h < 0)
621	continue;
622
623      j = h % MC_HASH_SIZE; if (j == -0) j = 0;
624
625      for (; j in header_hash;) {
626	if (++j == MC_HASH_SIZE) {
627	  j = 0;
628	}
629      }
630
631      header_hash[j] = n;
632      index_hash[j] = i;
633    }
634
635    if (basic < total) {
636      m = module "_ext_mclass";
637      s = "_e_" module "_s";
638      print_parser_table(s, "static", m, without_experimental, total);
639    }
640
641    printf("msg_mclass_t const * %s_extended_mclass = %s;\n\n", module, m) > PT;
642
643    }
644
645    if (basic < total) {
646      printf("msg_hclass_t * const %s_extensions[] = {\n", module) > PT;
647      for (i = basic; i < total; i++) {
648	if (i == without_experimental) {
649	  print "#if SU_HAVE_EXPERIMENTAL" > PT;
650        }
651	printf("  %s_%s_class,\n", module, headers[i]) > PT;
652      }
653      if (total != without_experimental)
654	print "#endif" > PT;
655      print "  NULL\n};\n\n" > PT;
656    }
657  }
658
659  exit success;
660}
661