1#!/bin/sh
2# Minimal Object-Oriented style PreProcessor.
3
4# Copyright (C) 2006-2008, 2016, 2018-2019 Free Software Foundation, Inc.
5# Written by Bruno Haible <bruno@clisp.org>, 2006.
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
20# As a special exception to the GNU General Public License, if you
21# distribute this file as part of a program that contains a
22# configuration script generated by Autoconf, you may include it under
23# the same distribution terms that you use for the rest of that program.
24
25# Usage: moopp source.oo.c source.oo.h superclass.oo.h ...
26# Arguments:
27#   - the source file of the class,
28#   - the header file declaring the class,
29#   - the header file declaring its superclass,
30#   - etc. up to the root class.
31# Creates four files in the current directory:
32#   - source.c, the preprocessing result of source.oo.c,
33#   - source.h, the preprocessing result of source.oo.h,
34#   - class.priv.h, a file declaring the private fields of the class,
35#   - class.vt.h, a file declaring the virtual function table of the class.
36
37# This implementation of the preprocessor is a quick hack. It makes assumptions
38# about the source code:
39#   - GNU indentation style,
40#   - the struct declaration must be in a single line,
41#   - no comments on the struct declaration line,
42#   - no #ifs in relevant position,
43#   - ...
44# Someday this should be rewritten to use a proper tokenizer and parser.
45
46# func_usage
47# outputs to stdout the --help usage message.
48func_usage ()
49{
50  echo "\
51Usage: moopp [OPTION]... SOURCE.oo.c SOURCE.oo.h SUPERCLASS.oo.h ...
52
53Preprocesses SOURCE.oo.c into CLASS.c and SOURCE.oo.h into CLASS.h,
54where CLASS is the name of the class defined in these files.
55
56See moo.h for the object-oriented features and the syntax of the input files.
57
58Options:
59      --help           print this help and exit
60      --version        print version information and exit
61      --dllexport=NAME Arrange so that the specified class name can be accessed
62                       from outside the shared library it is compiled into.
63                       This option can be repeated.
64
65Report bugs to <bruno@clisp.org>."
66}
67
68# func_version
69# outputs to stdout the --version message.
70func_version ()
71{
72  echo "$progname (GNU $package) $version"
73  echo "Copyright (C) 2006-2019 Free Software Foundation, Inc.
74License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
75This is free software: you are free to change and redistribute it.
76There is NO WARRANTY, to the extent permitted by law."
77  echo "Written by" "Bruno Haible"
78}
79
80# func_fatal_error message
81# outputs to stderr a fatal error message, and terminates the program.
82func_fatal_error ()
83{
84  echo "moopp: *** $1" 1>&2
85  echo "moopp: *** Stop." 1>&2
86  exit 1
87}
88
89# Command-line option processing.
90# Removes the OPTIONS from the arguments. Sets the variables:
91# - dllexports      list of class names to export from Woe32 DLLs
92dllexports=
93while test $# -gt 0; do
94  case "$1" in
95    --dllexport | --dllexpor | --dllexpo | --dllexp | --dllex | --dlle )
96      shift
97      if test $# = 0; then
98        func_fatal_error "missing argument for --dllexport"
99      fi
100      case "$1" in
101        -*) func_fatal_error "missing argument for --dllexport" ;;
102      esac
103      dllexports="$dllexports $1"
104      shift ;;
105    --dllexport=* )
106      arg=`echo "X$1" | sed -e 's/^X--dllexport=//'`
107      dllexports="$dllexports $arg"
108      shift ;;
109    --help | --hel | --he | --h )
110      func_usage
111      exit 0 ;;
112   --version | --versio | --versi | --vers | --ver | --ve | --v )
113      func_version
114      exit 0 ;;
115    -- )      # Stop option prcessing
116      shift; break ;;
117    -* )
118      func_fatal_error "unrecognized option: $option"
119      ;;
120    * )
121      break ;;
122  esac
123done
124
125if test $# -lt 2; then
126  func_fatal_error "Usage: $0 [OPTION]... source.oo.c source.oo.h superclass.oo.h ..."
127fi
128
129# Check that all files exist.
130for file
131do
132  test -r "$file" || {
133    func_fatal_error "file $file does not exist"
134  }
135done
136
137source_impl_file="$1"
138source_header_file="$2"
139shift
140shift
141
142case "$source_impl_file" in
143  *.oo.c) ;;
144  *) func_fatal_error "invalid class source file name: $source_impl_file" ;;
145esac
146case "$source_header_file" in
147  *.oo.h) ;;
148  *) func_fatal_error "invalid class header file name: $source_header_file" ;;
149esac
150
151# A sed expression that removes empty lines.
152sed_remove_empty_lines='/^$/d'
153
154# A sed expression that removes ANSI C and ISO C99 comments.
155sed_remove_comments="
156/[/][/*]/{
157  ta
158  :a
159  s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)//.*,\\1,
160  te
161  s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*]\\([^*]\\|[*][^/*]\\)*[*][*]*/,\\1 ,
162  ta
163  /^\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*[/][*]/{
164    s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*].*,\\1 ,
165    tu
166    :u
167    n
168    s,^\\([^*]\\|[*][^/*]\\)*[*][*]*/,,
169    tv
170    s,^.*\$,,
171    bu
172    :v
173  }
174  :e
175}"
176# The same thing as an extended regular expression, for use with
177# sed --regexp-extended.
178sed_remove_comments_ERE="
179/[/][/*]/{
180  ta
181  :a
182  s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)//.*,\\1,
183  te
184  s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*]([^*]|[*][^/*])*[*][*]*/,\\1 ,
185  ta
186  /^([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*[/][*]/{
187    s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*].*,\\1 ,
188    tu
189    :u
190    n
191    s,^([^*]|[*][^/*])*[*][*]*/,,
192    tv
193    s,^.*\$,,
194    bu
195    :v
196  }
197  :e
198}"
199
200# Check that 'sed' supports the kind of regular expressions used in
201# sed_remove_comments. The use of \| meaning alternation of basic regular
202# expressions is a GNU extension.
203sed_test='s,^\(\(a\|X\)*\)//.*,\1,'
204sed_result=`echo 'aaa//bcd' | sed -e "$sed_test"`
205test "$sed_result" = 'aaa' \
206  || func_fatal_error "The 'sed' program is not GNU sed. Try installing GNU sed."
207
208# func_check_impl_syntax file
209# Check the syntax of the source implementation file.
210# Output:
211#   - classname         name of the class being defined (without 'struct')
212#   - superclassname    name of the superclass, or empty for a root class
213#   - impl_decl_lineno  line number of the class name declaration ('struct')
214#   - impl_beg_lineno   line number of the start of the class declaration ('{')
215#   - impl_end_lineno   line number of the end of the class declaration ('}')
216#   - fields            field declarations, including preprocessor directives
217func_check_impl_syntax ()
218{
219  file="$1"
220  sed -e "$sed_remove_comments" < "$file" | grep '^fields:' > /dev/null || {
221    func_fatal_error "$file does not contain 'fields:'"
222  }
223  test `sed -e "$sed_remove_comments" < "$file" | grep -c '^fields:'` = 1 || {
224    func_fatal_error "$file contains more than one 'fields:'"
225  }
226  fields_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^fields:' | sed -e 's,:.*,,'`
227  sed_before_fields="$fields_lineno"',$d'
228  impl_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^struct[ 	]' | tail -n 1 | sed -e 's,:.*,,'`
229  test -n "$impl_decl_lineno" || {
230    func_fatal_error "$file: class declaration not found"
231  }
232  class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$impl_decl_lineno"'p'`
233  sed_extract_classname='s,^struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
234  classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
235  test -n "$classname" || {
236    func_fatal_error "$0: $file: class name not recognized at line $impl_decl_lineno"
237  }
238  superclassname=
239  if echo "$class_line" | grep ':' > /dev/null; then
240    sed_extract_superclassname='s,^.*:[ 	]*struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
241    superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
242    test -n "$superclassname" || {
243      func_fatal_error "$file: superclass name not recognized at line $impl_decl_lineno"
244    }
245  fi
246  impl_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
247  { test -n "$impl_beg_lineno" && test "$impl_decl_lineno" -lt "$impl_beg_lineno"; } || {
248    func_fatal_error "$file: opening brace of class declaration not found after line $impl_decl_lineno"
249  }
250  sed_after_fields='1,'"$fields_lineno"'d'
251  impl_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_fields" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
252  test -n "$impl_end_lineno" || {
253    func_fatal_error "$file: closing brace of class declaration not found after line $fields_lineno"
254  }
255  impl_end_lineno=`expr $fields_lineno + $impl_end_lineno`
256  sed_extract_fields="$impl_end_lineno"',$d;1,'"$fields_lineno"'d'
257  fields=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_fields"`
258}
259
260# func_check_header_syntax file
261# Check the syntax of a header file.
262# Output:
263#   - classname         name of the class being defined (without 'struct')
264#   - superclassname    name of the superclass, or empty for a root class
265#   - class_decl_lineno line number of the class name declaration ('struct')
266#   - class_beg_lineno  line number of the start of the class declaration ('{')
267#   - class_end_lineno  line number of the end of the class declaration ('}')
268#   - methods           newline-separated list of method declarations
269func_check_header_syntax ()
270{
271  file="$1"
272  sed -e "$sed_remove_comments" < "$file" | grep '^methods:' > /dev/null || {
273    func_fatal_error "$file does not contain 'methods:'"
274  }
275  test `sed -e "$sed_remove_comments" < "$file" | grep -c '^methods:'` = 1 || {
276    func_fatal_error "$file contains more than one 'methods:'"
277  }
278  methods_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^methods:' | sed -e 's,:.*,,'`
279  sed_before_methods="$methods_lineno"',$d'
280  class_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^struct[ 	]' | tail -n 1 | sed -e 's,:.*,,'`
281  test -n "$class_decl_lineno" || {
282    func_fatal_error "$file: class declaration not found"
283  }
284  class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$class_decl_lineno"'p'`
285  sed_extract_classname='s,^struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
286  classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
287  test -n "$classname" || {
288    func_fatal_error "$0: $file: class name not recognized at line $class_decl_lineno"
289  }
290  superclassname=
291  if echo "$class_line" | grep ':' > /dev/null; then
292    sed_extract_superclassname='s,^.*:[ 	]*struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
293    superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
294    test -n "$superclassname" || {
295      func_fatal_error "$file: superclass name not recognized at line $class_decl_lineno"
296    }
297  fi
298  class_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
299  { test -n "$class_beg_lineno" && test "$class_decl_lineno" -lt "$class_beg_lineno"; } || {
300    func_fatal_error "$file: opening brace of class declaration not found after line $class_decl_lineno"
301  }
302  sed_after_methods='1,'"$methods_lineno"'d'
303  class_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_methods" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
304  test -n "$class_end_lineno" || {
305    func_fatal_error "$file: closing brace of class declaration not found after line $methods_lineno"
306  }
307  class_end_lineno=`expr $methods_lineno + $class_end_lineno`
308  sed_extract_methods="$class_end_lineno"',$d;1,'"$methods_lineno"'d'
309  methods=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_methods" | tr '\015\n' '  ' | tr ';' '\n' | sed -e 's,[ 	]*$,,'`
310  sed_remove_valid_arg1_lines='/([ 	]*'"$classname"'_t[ 	]*[A-Za-z_0-9]*[ 	]*[,)]/d'
311  sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*(.*$,\1,'
312  methods_with_bad_arg1=`echo "$methods" | sed -e "$sed_remove_empty_lines" -e "$sed_remove_valid_arg1_lines" -e "$sed_extract_method_name"`
313  if test -n "$methods_with_bad_arg1"; then
314    methods_with_bad_arg1=`{ echo "$methods_with_bad_arg1" | sed -e 's/$/, /' | tr -d '\015\n'; echo; } | sed -e 's/\(, \)*$//'`
315    func_fatal_error "$file: some methods don't have a first argument of type ${classname}_t: $methods_with_bad_arg1"
316  fi
317}
318
319func_check_impl_syntax "$source_impl_file"
320impl_classname="$classname"
321impl_superclassname="$superclassname"
322
323func_check_header_syntax "$source_header_file"
324main_classname="$classname"
325main_superclassname="$superclassname"
326main_class_decl_lineno="$class_decl_lineno"
327main_class_beg_lineno="$class_beg_lineno"
328main_class_end_lineno="$class_end_lineno"
329main_methods="$methods"
330all_superclasses=
331all_methods="$methods"
332inherited_methods=
333last_header_file="$source_header_file"
334expected_superclassname="$superclassname"
335
336for file
337do
338  if test -z "$expected_superclassname"; then
339    func_fatal_error "file $last_header_file does not specify a superclass; superfluous command line argument $file"
340  fi
341  func_check_header_syntax "$file"
342  all_superclasses="$classname $all_superclasses"
343  all_methods="$methods
344$all_methods"
345  inherited_methods="$methods
346$inherited_methods"
347  if test "$classname" != "$expected_superclassname"; then
348    func_fatal_error "file $last_header_file specifies superclass '$expected_superclassname', but file $file defines class '$classname'"
349  fi
350  last_header_file="$file"
351  expected_superclassname="$superclassname"
352done
353
354if test -n "$expected_superclassname"; then
355  func_fatal_error "$0: file $last_header_file specifies superclass '$expected_superclassname', please specify the header file that defines it as additional command line argument"
356fi
357
358if test "$impl_classname" != "$main_classname"; then
359  func_fatal_error "file $source_header_file specifies class '$main_classname', but file $source_impl_file specifies class '$impl_classname'"
360fi
361if test "$impl_superclassname" != "$main_superclassname"; then
362  if test -z "$main_superclassname"; then
363    func_fatal_error "file $source_header_file specifies no superclass, but file $source_impl_file specifies a superclass '$impl_superclassname'"
364  fi
365  if test -z "$impl_superclassname"; then
366    func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies no superclass"
367  fi
368  func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies a superclass '$impl_superclassname'"
369fi
370
371# func_start_creation file
372# starts the creation of the named file.
373func_start_creation ()
374{
375  file="$1"
376  if test -f "$file"; then
377    echo "Updating $file (backup in ${file}~)"
378    mv -f "$file" "${file}~" || func_fatal_error "failed"
379  else
380    echo "Creating $file"
381  fi
382}
383
384# func_emit_priv_h newfile
385# outputs to $newfile the contents of class.priv.h.
386func_emit_priv_h ()
387{
388  newfile="$1"
389  {
390    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
391    echo
392    if test -n "${main_superclassname}"; then
393      echo "/* Field layout of superclass.  */"
394      echo "#include \"${main_superclassname}.priv.h\""
395      echo
396    fi
397    echo "/* Field layout of ${main_classname} class.  */"
398    echo "struct ${main_classname}_representation"
399    echo "{"
400    if test -n "${main_superclassname}"; then
401      echo "  struct ${main_superclassname}_representation base;"
402    else
403      echo "  const void *vtable;"
404    fi
405    echo "$fields" | sed -e "$sed_remove_empty_lines"
406    echo "};"
407  } > "$newfile"
408}
409
410# func_emit_vt_h newfile
411# outputs to $newfile the contents of class.vt.h.
412func_emit_vt_h ()
413{
414  newfile="$1"
415  {
416    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
417    echo
418    if test -n "${main_superclassname}"; then
419      echo "/* Virtual function table layout of superclass.  */"
420      echo "#include \"${main_superclassname}.vt.h\""
421      echo
422    fi
423    echo "/* Virtual function table layout of ${main_classname} class.  */"
424    echo "$main_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*([^,)]*/\1(*\2) (THIS_ARG/' -e 's,$,;,'
425  } > "$newfile"
426}
427
428# In C++ mode, we have a precise type checking. But in C mode, we have only
429# loose type checking: So that rootclass_t and subclass_t are assignment
430# compatible, we have to define subclass_t as identical to rootclass_t.
431# Therefore we need an alias name for the representation of any type in the
432# hierarchy.
433if test -z "$main_superclassname"; then
434  main_repclassalias="any_${main_classname}_representation"
435else
436  main_repclassalias="${main_classname}_representation"
437fi
438
439sed_extract_method_rettype='s,^\(.*[^A-Za-z_0-9]\)[A-Za-z_0-9][A-Za-z_0-9]*[ 	]*(.*$,\1,
440s,^[ 	]*,,
441s,[ 	]*$,,'
442sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*(.*$,\1,'
443sed_extract_method_arglist='s,^.*[^A-Za-z_0-9][A-Za-z_0-9][A-Za-z_0-9]*[ 	]*([^,)]*\(.*\)).*$,'"${main_classname}_t"' first_arg\1,'
444sed_extract_method_args='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)$,\1,'
445
446# func_emit_source_h newfile newfile_base
447# outputs to $newfile the contents of source.h.
448source_header_file_base=`echo "$source_header_file" | sed -e 's,^.*/,,'`
449func_emit_source_h ()
450{
451  newfile="$1"
452  newfile_base="$2"
453  # Use DLL_VARIABLE if and only if the main classname is among the names
454  # specified with --dllexport options.
455  dllexport_for_variables=
456  for name in $dllexports; do
457    if test "${main_classname}" = "${name}"; then
458      dllexport_for_variables=" DLL_VARIABLE"
459      break
460    fi
461  done
462  {
463    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
464    echo
465    echo "#line 1 \"${source_header_file_base}\""
466    cat "$source_header_file" | sed -e "${main_class_decl_lineno}"',$d'
467    echo "#line "`expr 3 + ${main_class_decl_lineno} + 1`" \"$newfile_base\""
468    echo "struct ${main_repclassalias};"
469    echo "/* ${main_classname}_t is defined as a pointer to struct ${main_repclassalias}."
470    echo "   In C++ mode, we use a smart pointer class."
471    echo "   In C mode, we have no other choice than a typedef to the root class type.  */"
472    echo "#if IS_CPLUSPLUS"
473    echo "struct ${main_classname}_t"
474    echo "{"
475    echo "private:"
476    echo "  struct ${main_repclassalias} *_pointer;"
477    echo "public:"
478    echo "  ${main_classname}_t () : _pointer (NULL) {}"
479    echo "  ${main_classname}_t (struct ${main_repclassalias} *pointer) : _pointer (pointer) {}"
480    echo "  struct ${main_repclassalias} * operator -> () { return _pointer; }"
481    echo "  operator struct ${main_repclassalias} * () { return _pointer; }"
482    atroot=yes
483    for classname in $all_superclasses; do
484      if test -n "$atroot"; then
485        repclassalias="any_${classname}_representation"
486      else
487        repclassalias="${classname}_representation"
488      fi
489      echo "  operator struct ${repclassalias} * () { return (struct ${repclassalias} *) _pointer; }"
490      atroot=
491    done
492    # The 'operator void *' is needed to avoid ambiguous conversion chains.
493    echo "  operator void * () { return _pointer; }"
494    # The 'operator ==' and 'operator !=' are needed to avoid ambiguous comparisons with NULL.
495    echo "  bool operator == (const void *p) { return _pointer == p; }"
496    echo "  bool operator != (const void *p) { return _pointer != p; }"
497    atroot=yes
498    for classname in $all_superclasses; do
499      if test -n "$atroot"; then
500        repclassalias="any_${classname}_representation"
501      else
502        repclassalias="${classname}_representation"
503      fi
504      echo "  operator ${classname}_t () { return (${classname}_t) (struct ${repclassalias} *) _pointer; }"
505      # The 'explicit' constructors allow to downcast.
506      echo "  explicit ${main_classname}_t (${classname}_t x) : _pointer ((struct ${main_repclassalias} *) (void *) x) {}"
507      atroot=
508    done
509    echo "};"
510    echo "#else"
511    if test -n "${main_superclassname}"; then
512      echo "typedef ${main_superclassname}_t ${main_classname}_t;"
513    else
514      echo "typedef struct ${main_repclassalias} * ${main_classname}_t;"
515    fi
516    echo "#endif"
517    echo
518    echo "/* Functions that invoke the methods.  */"
519    echo "#ifdef __cplusplus"
520    echo "extern \"C\" {"
521    echo "#endif"
522    echo "$all_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*([^,)]*/\1'"${main_classname}_"'\2 ('"${main_classname}_t first_arg"'/' -e 's,^,extern ,' -e 's,$,;,'
523    echo "#ifdef __cplusplus"
524    echo "}"
525    echo "#endif"
526    echo
527    # Now come the implementation details.
528    echo "/* Type representing an implementation of ${main_classname}_t.  */"
529    echo "struct ${main_classname}_implementation"
530    echo "{"
531    echo "  const typeinfo_t * const *superclasses;"
532    echo "  size_t superclasses_length;"
533    echo "  size_t instance_size;"
534    echo "#define THIS_ARG ${main_classname}_t first_arg"
535    echo "#include \"${main_classname}.vt.h\""
536    echo "#undef THIS_ARG"
537    echo "};"
538    echo
539    echo "/* Public portion of the object pointed to by a ${main_classname}_t.  */"
540    echo "struct ${main_classname}_representation_header"
541    echo "{"
542    echo "  const struct ${main_classname}_implementation *vtable;"
543    echo "};"
544    echo
545    echo "#if HAVE_INLINE"
546    echo
547    echo "/* Define the functions that invoke the methods as inline accesses to"
548    echo "   the ${main_classname}_implementation."
549    echo "   Use #define to avoid a warning because of extern vs. static.  */"
550    echo
551    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
552      while read method; do
553        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
554        name=`echo "$method" | sed -e "$sed_extract_method_name"`
555        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
556        if test "$arglist" = "void"; then
557          args=
558        else
559          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
560        fi
561        if test "$rettype" = "void"; then
562          return=
563        else
564          return="return "
565        fi
566        echo "# define ${main_classname}_${name} ${main_classname}_${name}_inline"
567        echo "static inline $rettype"
568        echo "${main_classname}_${name} ($arglist)"
569        echo "{"
570        echo "  const struct ${main_classname}_implementation *vtable ="
571        echo "    ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
572        echo "  ${return}vtable->${name} ($args);"
573        echo "}"
574        echo
575      done
576    echo "#endif"
577    echo
578    echo "extern${dllexport_for_variables} const typeinfo_t ${main_classname}_typeinfo;"
579    if test -n "${main_superclassname}"; then
580      superclasses_array_initializer="${main_superclassname}_SUPERCLASSES"
581    else
582      superclasses_array_initializer="NULL"
583    fi
584    echo "#define ${main_classname}_SUPERCLASSES &${main_classname}_typeinfo, ${superclasses_array_initializer}"
585    if test -n "${main_superclassname}"; then
586      echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + ${main_superclassname}_SUPERCLASSES_LENGTH)"
587    else
588      echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + 1)"
589    fi
590    echo
591    echo "extern${dllexport_for_variables} const struct ${main_classname}_implementation ${main_classname}_vtable;"
592    echo
593    echo "#line "`expr $main_class_end_lineno + 1`" \"${source_header_file_base}\""
594    cat "$source_header_file" | sed -e "1,${main_class_end_lineno}d"
595  } > "$newfile"
596}
597
598# func_emit_source_c newfile newfile_base
599# outputs to $newfile the contents of source.c.
600source_impl_file_base=`echo "$source_impl_file" | sed -e 's,^.*/,,'`
601func_emit_source_c ()
602{
603  newfile="$1"
604  newfile_base="$2"
605  {
606    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
607    echo
608    # In C mode, where subclass_t is identical to rootclass_t, we define the
609    # any_rootclass_representation type to the right one for subclass.
610    if test -n "$all_superclasses"; then
611      for classname in $all_superclasses; do
612        rootclassname="$classname"
613        break
614      done
615    else
616      rootclassname="$main_classname"
617    fi
618    echo "#if !IS_CPLUSPLUS"
619    echo "#define ${main_classname}_representation any_${rootclassname}_representation"
620    echo "#endif"
621    echo "#line 1 \"${source_impl_file_base}\""
622    cat "$source_impl_file" | sed -e "${impl_decl_lineno}"',$d'
623    echo "#line "`expr 6 + ${impl_decl_lineno} + 1`" \"$newfile_base\""
624    echo "#include \"${main_classname}.priv.h\""
625    echo
626    echo "const typeinfo_t ${main_classname}_typeinfo = { \"${main_classname}\" };"
627    echo
628    echo "static const typeinfo_t * const ${main_classname}_superclasses[] ="
629    echo "  { ${main_classname}_SUPERCLASSES };"
630    echo
631    if test -n "${main_superclassname}"; then
632      echo "#define super ${main_superclassname}_vtable"
633      echo
634    fi
635    echo "#line "`expr $impl_end_lineno + 1`" \"${source_impl_file_base}\""
636    cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "s,${main_classname}::,${main_classname}__,g"
637    echo
638    lineno=`wc -l < "$newfile"`
639    echo "#line "`expr $lineno + 2`" \"$newfile_base\""
640    # Define trivial stubs for methods that are not defined or overridden.
641    inherited_method_names=`echo "$inherited_methods" | sed -e "$sed_remove_empty_lines" | sed -e "$sed_extract_method_name"`
642    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
643      while read method; do
644        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
645        name=`echo "$method" | sed -e "$sed_extract_method_name"`
646        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
647        if test "$arglist" = "void"; then
648          args=
649        else
650          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
651        fi
652        if test "$rettype" = "void"; then
653          return=
654        else
655          return="return "
656        fi
657        if cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "$sed_remove_comments" | grep "${main_classname}::${name} *(" > /dev/null; then
658          # The class defines or overrides the method.
659          :
660        else
661          # Add a stub for the method.
662          inherited=
663          for i in $inherited_method_names; do
664            if test "$i" = "$name"; then
665              inherited=yes
666            fi
667          done
668          # First a prototype, to avoid gcc -Wmissing-prototypes warnings.
669          echo "$rettype ${main_classname}__${name} ($arglist);"
670          echo "$rettype"
671          echo "${main_classname}__${name} ($arglist)"
672          echo "{"
673          if test -n "$inherited"; then
674            echo "  ${return}super.${name} ($args);"
675          else
676            echo "  /* Abstract (unimplemented) method called.  */"
677            echo "  abort ();"
678            # Avoid C++ compiler warning about missing return value.
679            echo "  #ifndef __GNUC__"
680            echo "  ${return}${main_classname}__${name} ($args);"
681            echo "  #endif"
682          fi
683          echo "}"
684          echo
685        fi
686      done
687    echo
688    echo "const struct ${main_classname}_implementation ${main_classname}_vtable ="
689    echo "{"
690    echo "  ${main_classname}_superclasses,"
691    echo "  sizeof (${main_classname}_superclasses) / sizeof (${main_classname}_superclasses[0]),"
692    echo "  sizeof (struct ${main_classname}_representation),"
693    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
694      while read method; do
695        name=`echo "$method" | sed -e "$sed_extract_method_name"`
696        echo "  ${main_classname}__${name},"
697      done
698    echo "};"
699    echo
700    echo "#if !HAVE_INLINE"
701    echo
702    echo "/* Define the functions that invoke the methods.  */"
703    echo
704    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
705      while read method; do
706        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
707        name=`echo "$method" | sed -e "$sed_extract_method_name"`
708        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
709        if test "$arglist" = "void"; then
710          args=
711        else
712          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
713        fi
714        if test "$rettype" = "void"; then
715          return=
716        else
717          return="return "
718        fi
719        echo "$rettype"
720        echo "${main_classname}_${name} ($arglist)"
721        echo "{"
722        echo "  const struct ${main_classname}_implementation *vtable ="
723        echo "    ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
724        echo "  ${return}vtable->${name} ($args);"
725        echo "}"
726        echo
727      done
728    echo "#endif"
729  } > "$newfile"
730}
731
732# Generate the files in the source directory, not in the current directory.
733# This is needed because they need to be distributed, since not all platforms
734# have GNU 'sed' preinstalled.
735
736sed_butbase='s,[^/]*$,,'
737destdir=`echo "$source_impl_file" | sed -e "$sed_butbase"`
738
739# Generate the source.h file first. The Makefile.am snippets rely on the
740# fact that the other generated files have the same or a newer timestamp.
741#
742# Also, generate the source.c file last. The Makefile.am snippets don't know
743# about the other generated files; they assume that when the source.c file
744# is finished, this command is complete.
745
746new_source_header_file_base=`echo "$source_header_file_base" | sed -e 's,\.oo\.h$,.h,'`
747new_source_header_file="${destdir}$new_source_header_file_base"
748func_start_creation "$new_source_header_file"
749func_emit_source_h "$new_source_header_file" "$new_source_header_file_base" \
750  || func_fatal_error "failed"
751
752new_priv_header_file="${destdir}${main_classname}.priv.h"
753func_start_creation "$new_priv_header_file"
754func_emit_priv_h "$new_priv_header_file" \
755  || func_fatal_error "failed"
756
757new_vt_header_file="${destdir}${main_classname}.vt.h"
758func_start_creation "$new_vt_header_file"
759func_emit_vt_h "$new_vt_header_file" \
760  || func_fatal_error "failed"
761
762new_source_impl_file_base=`echo "$source_impl_file_base" | sed -e 's,\.oo\.c$,.c,'`
763new_source_impl_file="${destdir}$new_source_impl_file_base"
764func_start_creation "$new_source_impl_file"
765func_emit_source_c "$new_source_impl_file" "$new_source_impl_file_base" \
766  || func_fatal_error "failed"
767