1#!/usr/bin/perl -w
2# Copyright (C) 2019-2020 Free Software Foundation, Inc., GPL3
3#
4# Generate src/dynapi.c and test/unit-testing/dynapi_test.c
5# C structs/arrays for all dwg objects and its fields for a dynamic API.
6#   -> name, type, size, offset, dxfgroup, memory-type
7# Within each object linear search is good enough.
8# This is needed for in_dxf, dwgfilter,
9# a maintainable and shorter dwg_api and shorter language bindings.
10# Written by: Reini Urban
11
12#dwg.h:
13# typedef struct _dwg_header_variables
14# typedef struct _dwg_entity_(.*)
15# typedef struct _dwg_object_(.*)
16#subclasses, typically like
17# typedef struct _dwg_TYPE_subclass
18# typedef union _dwg_MLEADER_Content
19
20use strict;
21use warnings;
22use vars qw(@entity_names @object_names @subclasses
23            $max_entity_names $max_object_names $max_subclasses);
24use Convert::Binary::C;
25#use Data::Dumper; # if DEBUG
26#BEGIN { chdir 'src' if $0 =~ /src/; }
27
28# add gcc/clang -print-search-dirs paths
29my (@ccincdir, $srcdir, $topdir);
30if ($0 =~ m{(^\.\./.*src)/gen}) {
31  $srcdir = $1;
32  $topdir = "$srcdir/..";
33  $topdir =~ s{^\.\./src/}{};
34} elsif ($0 =~ m{^src/gen}) {
35  $srcdir = "src";
36  $topdir = ".";
37} else {
38  $srcdir = ".";
39  $topdir = "..";
40}
41my $CC = `grep '^CC =' Makefile`;
42if ($CC) {
43  $CC =~ s/^CC =//;
44  chomp $CC;
45  $CC =~ s/^\s+//;
46  if ($CC =~ /^afl-(gcc|clang)/) {
47    $ENV{AFL_QUIET} = 1;
48  }
49  my $out = `$CC -print-search-dirs`;
50  if ($out) {
51    if ($out =~ /^install: (.+?)$/m) {
52      my $d = $1;
53      $d =~ s/\/$//;
54      @ccincdir = ("$d/include") if -d "$d/include";
55    } elsif ($out =~ /^libraries: =(.+?)$/m) {
56      @ccincdir = grep { -d "$_/include" ? "$_/include" : undef }
57                       split ':', $1;
58    }
59  }
60}
61#__WORDSIZE=64
62# glibc quirks to include stdint.h and stddef.h directly with this old pre-C99
63# Convert::Binary::C preprocessor
64my @defines = ('__GNUC__=4', '__x86_64__', '__inline=inline',
65               '__THROW=', '__attribute__(x)=');
66if ($^O =~ /darwin|bsd/) {
67  push @defines, ('__signed=signed', '__builtin_va_list=void*', '__extension__=');
68}
69if (@ccincdir and join(" ",@ccincdir) =~ /clang/) {
70  push @defines, ('__has_feature(x)=0', '__has_include_next(x)=0',
71                  '__INTPTR_TYPE__=long int', '__UINTPTR_TYPE__=unsigned long int',
72                  '__INTMAX_TYPE__=long int', '__UINTMAX_TYPE__=unsigned long int',
73                  '__gwchar_t=int', '____gwchar_t_defined',
74    );
75}
76
77warn "using $CC with @ccincdir\n" if @ccincdir;
78my $c = Convert::Binary::C->new
79  ->Include('.', @ccincdir, '/usr/include')
80  ->Define(@defines);
81my $hdr = "$topdir/include/dwg.h";
82$c->parse_file($hdr);
83
84#print Data::Dumper->Dump([$c->struct('_dwg_MLINESTYLE_line')], ['_dwg_MLINESTYLE_line']);
85#print Data::Dumper->Dump([$c->struct('_dwg_entity_TEXT')], ['_dwg_entity_TEXT']);
86#print Data::Dumper->Dump([$c->struct('struct _dwg_header_variables')], ['Dwg_Header_Variables']);
87
88my (%h, $n, %structs, %unions, %ENT, %DXF, %SIZE, %SUBCLASS, %SUBCLASSES, %DWG_TYPE,
89    @unhandled_names);
90local (@entity_names, @object_names, @subclasses, $max_entity_names, $max_object_names,
91       $max_subclasses);
92# todo: harmonize more subclasses, detect abstract classes with concrete typedefs
93for (sort $c->struct_names) {
94  if (/^_dwg_entity_([A-Z0-9_]+)$/) {
95    my $n = $1;
96    $structs{$n}++;
97    push @entity_names, $n;
98  } elsif (/^_dwg_object_([A-Z0-9_]+)$/) {
99    my $n = $1;
100    $structs{$n}++;
101    push @object_names, $n;
102  } elsif (/^_dwg_header_variables/) {
103    ;
104  } elsif (/^Dwg_([A-Z0-9]+)/) { # AuxHeader, Header, R2004_Header, SummaryInfo
105    my $n = $1;
106    next if length $n <= 1;
107    $structs{$n}++;
108  } elsif (/_dwg_([A-Z0-9_]+)/ && !/_dwg_ODA/) {
109    $structs{$_}++;
110    push @subclasses, $_;
111  } else {
112    warn "skip struct $_\n";
113  }
114}
115for (sort $c->union_names) {
116  if (/_dwg_([A-Z0-9_]+)/) {
117    $structs{$_}++;
118    $unions{$_}++;
119    push @subclasses, $_;
120  } else {
121    warn "skip union $_\n";
122  }
123}
124# typedefs as aliases:
125push @entity_names, qw(XLINE);                    # RAY
126push @entity_names, qw(VERTEX_MESH VERTEX_PFACE); # VERTEX_3D
127push @entity_names, qw(REGION BODY);              # 3DSOLID
128$structs{abstractobject_UNDERLAYDEFINITION}++;
129$structs{abstractentity_UNDERLAY}++;
130push @entity_names, qw(PDFUNDERLAY DGNUNDERLAY DWFUNDERLAY);
131push @object_names, qw(PDFDEFINITION DGNDEFINITION DWFDEFINITION);
132push @object_names, qw(ASSOCARRAYMODIFYPARAMETERS
133                       ASSOCARRAYPATHPARAMETERS
134                       ASSOCARRAYPOLARPARAMETERS
135                       ASSOCARRAYRECTANGULARPARAMETERS);
136$structs{abstractobject_ASSOCARRAYPARAMETERS}++;
137
138@entity_names = sort @entity_names;
139# get BITCODE_ macro types for each struct field
140open my $in, "<", $hdr or die "hdr: $!";
141my $f;
142while (<$in>) {
143  if (!$n) {
144    if (/^typedef struct (_dwg_.+) \{/) {
145      $n = $1;
146    } elsif (/^typedef struct (_dwg_\S+)$/) {
147      $n = $1;
148    } elsif (/^typedef union (_dwg_\S+)$/) {
149      $n = $1;
150    } elsif (/^#define (COMMON_\w+)\((\w+)\)/) { # COMMON_TABLE_CONTROL_FIELDS
151      # BUG: Convert::Binary::C cannot separate H* from H, both are just Dwg_Object_Ref
152      # So we need to parse the defines
153      $n = $1;
154      $f = $2;
155      $h{$n}{reactors} = 'H*'; # has no ;
156    } elsif (/^#define (COMMON_\w+)/) { # COMMON_ENTITY_POLYLINE
157      $n = $1;
158      $h{$n}{seqend}   = 'H' if $n eq 'COMMON_ENTITY_POLYLINE'; # has no ;
159    }
160  } elsif (/^\}/) { # close the struct
161    $n = '';
162  } elsif ($n and $_ =~ /^ +BITCODE_([\w\*]+)\s+(\w.*);/) {
163    my $type = $1;
164    my $v = $2;
165    $v =~ s/^_3/3/;
166    $type =~ s/\s+$//;
167    if ($n eq 'COMMON_TABLE_CONTROL_FIELDS' && $v eq 'entries') {
168      $h{$n}{$_} = 'H*'
169        for qw(block_headers layers styles linetypes views ucs vports apps
170               dimstyles vport_entity_headers);
171    }
172    $h{$n}{$v} = $type;
173  }
174}
175#$h{Dwg_Bitcode_3BD} = '3BD';
176#$h{Dwg_Bitcode_2BD} = '2BD';
177#$h{Dwg_Bitcode_3RD} = '3RD';
178#$h{Dwg_Bitcode_2RD} = '2RD';
179$ENT{LAYER}->{flag} = 'BS';
180$ENT{LAYER}->{name} = 'T';
181$ENT{DIMSTYLE}->{name} = 'T';
182$SUBCLASSES{DIMENSION_common} = [ qw(AcDbDimension) ];
183$SUBCLASSES{ACTION_3DSOLID} = [ qw(AcDbModelerGeometry AcDb3dSolid) ];
184$SUBCLASSES{TABLECONTENTs} = [ qw( AcDbLinkedTableData AcDbFormattedTableData AcDbTableContent) ];
185#$ENT{LTYPE}->{strings_area} = 'TF';
186close $in;
187my @old;
188
189sub expand_define {
190  my ($def, $n, $defined, $param) = @_;
191  if (exists $defined->{$def}) {
192    warn "expand defined $def fields";
193    if (!$param) { # replace the macro arg with $param? no. for now ignore
194      for (keys %{$DXF{$def}}) {
195        $DXF{$n}->{$_} = $DXF{$def}->{$_};
196      }
197      for (keys %{$ENT{$def}}) {
198        $ENT{$n}->{$_} = $ENT{$def}->{$_};
199      }
200    }
201    my $aref = $SUBCLASSES{$n};
202    for my $k (@{$SUBCLASSES{$def}}) {
203      # if defined subclass is not in the main subclass yet
204      if (!grep $_ eq $k, @$aref) {
205        if (@$aref) {
206          push @$aref, $k;
207        } else {
208          $aref = [ $k ];
209        }
210        $SUBCLASSES{$n} = $aref;
211      }
212    }
213  } else {
214    unless (/(DXF|DECODE|ENCODE|JSON|FREE)_3DSOLID/) {
215      warn "TODO $def fields not defined";
216    } else {
217      $n = 'ACTION_3DSOLID';
218      $defined->{$n}++;
219      @old = (); # TODO pop 3DSOLID_material
220    }
221  }
222}
223
224sub embedded_struct {
225  my ($pre, $subclass) = @_;
226  if ($f =~ /^$pre\.(\w+)$/) {
227    $f = $1;
228    if ($n ne $subclass) {
229      push @old, $n;
230      warn "$n pushed";
231      $n = $subclass;
232    }
233  } elsif ($n eq $subclass && $f !~ /\./) {
234    $n = pop @old;
235    warn "$n popped";
236  }
237}
238
239# parse a spec for its objects, subclasses and dxf values
240sub dxf_in {
241  $in = shift;
242  my $v = qr /[\w\.\[\]]+/;
243  my $vx = qr /[\w\.\[\]>-]+/;
244  my $outdef;
245  my %defined; #define subclass_fields
246  while (<$in>) {
247    $f = '';
248    s/DXF \{ //;
249    if (!$n) {
250      if (/^DWG_(ENTITY|OBJECT)\s?\((\w+)\)/) {
251        $n = $2;
252        $n =~ s/^_3/3/;
253        warn $n;
254      } elsif (/^\s*SECTION\s?\(HEADER\)/) {
255        $n = 'header_variables';
256        warn $n;
257      } elsif (/^int DWG_FUNC_N\s?\(ACTION,_HATCH(\w+)\)/) {
258        $n = 'HATCH';
259        warn $n;
260      } elsif (/^\#define (COMMON_ENTITY_POLYLINE)/) {
261        $n = $1;
262        warn "define $n";
263      } elsif (/^\#define (COMMON_3DSOLID|ACTION_3DSOLID|COMMON_ENTITY_DIMENSION)/) {
264        $n = $1;
265        $defined{$n}++;
266        warn "define $n";
267      } elsif (/^\#define (\w+)_fields/) {
268        $n = $1;
269        $defined{$n}++;
270        warn "define $n fields";
271      }
272    } elsif (/^\#define (\w+)_fields/) {
273      $n = $1;
274      $defined{$n}++;
275      warn "define $n fields";
276    # i.e. after #define
277    } elsif (/^DWG_(ENTITY|OBJECT)\s?\((\w+)\)/) {
278      $n = $2;
279      $n =~ s/^_3/3/;
280      warn $n;
281    } elsif (/^DWG_(ENTITY|OBJECT)_END/) { # close
282      $n = '';
283      @old = ();
284      $outdef = 0;
285    } elsif (!$n) {
286      ;
287    #} elsif ($outdef) {
288    #  ;
289    #} elsif (/^\s*\#ifdef IS_JSON/) {
290    #  $outdef++;
291    #} elsif ($outdef && /^\s*\#endif/) {
292    #  $outdef = 0;
293    # single-line REPEAT
294    } elsif (/^\s+REPEAT.*\($v,\s*$v,\s*(\w+)\)/) {
295      my $tmp = $1; # subclass?
296      if ($tmp =~ /^Dwg_(.*)/) {
297        push @old, $n;
298        $n = $1; # not _dwg_$1
299        warn "$n pushed";
300      } else {
301        push @old, '';
302      }
303    } elsif (/^\s+_REPEAT_C?N\s*\($vx,\s*$v,\s*(\w+),/) {
304      my $tmp = $1; # subclass?
305      if ($tmp =~ /^Dwg_(.*)/) {
306        push @old, $n;
307        $n = $1;
308        warn "$n pushed";
309      } else {
310        push @old, '';
311      }
312    # multiline REPEAT
313    } elsif (/^\s+REPEAT.*\($v,\s*$v,$/) {
314      $_ = <$in>;
315      if (/^\s+(\w+)\)/) {
316        my $tmp = $1; # subclass?
317        if ($tmp =~ /^Dwg_(.*)/) {
318          push @old, $n;
319          $n = $1;
320          warn "$n pushed";
321        } else {
322          push @old, '';
323        }
324      }
325    # skip END_REPEAT(paths); return DWG_ERR_VALUEOUTOFBOUNDS;
326    } elsif (/^\s+END_REPEAT\s*\(($vx)\);?$/) { # close
327      my $tmp = pop @old;
328      if ($tmp) {
329        $n = $tmp;
330        warn "$n popped";
331      } elsif (!defined $tmp) {
332        # mostly due to type being a primitive, not a subclass (like T)
333        warn "missing REPEAT block in $n: $1";
334      }
335    } elsif (/^\s+FIELD_HANDLE\s*\((\w+),\s*\d+,\s*(\d+)\)/) {
336      $f = $1;
337      if ($n eq 'MLEADER_AnnotContext' && $f eq 'mleaderstyle') {
338        $n = 'MULTILEADER'; # pop back
339      }
340      $DXF{$n}->{$f} = $2 if $2;
341    } elsif (/^\s+VALUE_HANDLE\s*\(.+,\s*(\w+),\s*\d,\s*(\d+)\)/) {
342      $f = $1;
343      $DXF{$n}->{$f} = $2 if $2;
344    } elsif (/^\s+FIELD_TF\s*\((\w+),\s*(\d*),\s*(\d+)\)/) {
345      my $type = 'TF';
346      $f = $1;
347      $DXF{$n}->{$f} = $3 if $3;
348      $SIZE{$n}->{$f} = $2;
349      $ENT{$n}->{$f} = 'TF';
350    } elsif (/^\s+FIELD_(.+?)\s*\(([\w\.]+),\s*(\d+)\)/) {
351      my $type = $1;
352      $f = $2;
353      my $dxf = $3;
354      $type =~ s/0$//; # strip ending 0, optional dxf (default 0 not printed)
355      $type =~ s/^BD1$/BD/;
356      # inlined unions
357      $f =~ s/^(?:fmt|sty|name|value)\.//;
358      # inlined struct: ctx.
359      if ($f =~ /^ctx\.(\w+)$/) {
360        $f = $1;
361        $n = 'MLEADER_AnnotContext';
362      }
363      embedded_struct ('plotsettings', 'PLOTSETTINGS');
364      embedded_struct ('cellstyle', 'TABLESTYLE_Cell');
365      embedded_struct ('body', 'ACTIONBODY');
366      embedded_struct ('ldata', 'LinkedData');
367      embedded_struct ('tdata', 'LinkedTableData');
368      embedded_struct ('fdata', 'FormattedTableData');
369      embedded_struct ('dimension', 'OCD_Dimension');
370      if ($n eq 'VISUALSTYLE') {
371        $DXF{$n}->{$f."_int"} = 176;
372        next if $DXF{$n}->{$f};
373      }
374      # (scale.x, 41) as is
375      $DXF{$n}->{$f} = $dxf if $dxf;
376      $ENT{$n}->{$f} = 'TF' if $type eq 'BINARY';
377      $ENT{$n}->{$f} = $type if $type =~ /^T/;
378      $ENT{$n}->{$f} = $type if $type =~ /^[23][RB]D_1/;
379    } elsif (@old && /^\s+SUB_FIELD_HANDLE\s*\($v,\s*(\w+),\s*\d+,\s*(\d+)\)/) {
380      my $type = $1;
381      $f = $1;
382      $DXF{$n}->{$f} = $2 if $2;
383    } elsif (@old && /^\s+SUB_FIELD_(.+?)\s*\($v,\s*(\w+),\s*(\d+)\)/) {
384      my $type = $1;
385      $f = $2;
386      $DXF{$n}->{$f} = $3 if $3;
387    } elsif (/^\s+FIELD_(?:CMC|ENC)\s*\((\w+),\s*(\d+)\)/) {
388      $f = $1;
389      if ($2) {
390        $DXF{$n}->{$f} = $2;
391        if ($2 < 90) {
392          $DXF{$n}->{"$f.index"} = $2;
393          $DXF{$n}->{"$f.rbg"} = $2 + 420 - 62;
394          $DXF{$n}->{"$f.name"} = $2 + 430 - 62;
395          $DXF{$n}->{"$f.book_name"} = $2 + 430 - 62;
396          $DXF{$n}->{"$f.alpha"} = $2 + 440 - 62;
397        } else {
398          $DXF{$n}->{"$f.rbg"} = $2;
399        }
400      }
401    } elsif (/^\s+FIELD_(.+?)\s*\((\w+),.*,\s*(\d+)\)/) {
402      my $type = $1;
403      $f = $2;
404      $DXF{$n}->{$f} = $3 if $3;
405      $ENT{$n}->{$f} = 'TF' if $type eq 'BINARY';
406      $ENT{$n}->{$f} = $type if $type =~ /^T/;
407      $ENT{$n}->{$f} = $type if $type =~ /^[23][RB]D_1/;
408    } elsif (@old && /^\s+SUB_FIELD_(.+?)\s*\(\w+,\s*(\w+),.*,\s*(\d+)\)/) {
409      my $type = $1;
410      $f = $2;
411      $DXF{$n}->{$f} = $3 if $3;
412      $ENT{$n}->{$f} = 'TF' if $type eq 'BINARY';
413      $ENT{$n}->{$f} = $type if $type =~ /^T/;
414      $ENT{$n}->{$f} = $type if $type =~ /^[23][RB]D_1/;
415    } elsif (/^\s+HANDLE_VECTOR(?:_N)?\s*\((\w+),.*?,\s*(\d+)\)/) {
416      $f = $1;
417      $DXF{$n}->{$f} = $2 if $2;
418    } elsif (/^\s+SUB_HANDLE_VECTOR\s*\([^,]+?, (\w+), .+, (\d+)\)/) {
419      $f = $1;
420      $DXF{$n}->{$f} = $2 if $2;
421    } elsif (/^\s+HEADER_.+\s*\((\w+),\s*(\d+)\)/) { # HEADER_RC
422      $f = $1;
423      $DXF{$n}->{$f} = $2 if $2;
424    } elsif (/^\s+HEADER_.+\s*\((\w+),\s*(\d+),\s*\w+\)/) {
425      $f = $1;
426      $DXF{$n}->{$f} = $2 if $2;
427    } elsif (/^\s+HEADER_.+\s*\((\w+),\s*\w+,\s*(\d+),\s*\D.+\)/) {
428      $f = $1;
429      #$f =~ s/^_3/3/;
430      $DXF{$n}->{$f} = $2 if $2;
431    } elsif (/^\s+HEADER_([23])D\s*\((\w+)\)/) {
432      $f = $2;
433      $DXF{$n}->{$f} = $1 eq '2' ? 20 : 30;
434    } elsif (/^\s+VALUE_(.+)\s*\((\w.+),.*,\s*(\d+)\)/) {
435      my $type = $1;
436      $f = $2;
437      $DXF{$n}->{$f} = $3 if $3;
438      $ENT{$n}->{$f} = $type if $type =~ /^T/;
439    } elsif (/^\s+SUBCLASS\s*\((\w.+)\)/) {
440      my $sc = $1;
441      if ($sc eq 'AcDbLayout') { # FIXME: pop PLOTSETTINGS earlier in LAYOUT
442        if (@old && $old[0] eq 'LAYOUT') {
443          $n = pop @old;
444          warn "$n popped";
445        }
446      }
447      if (exists $SUBCLASSES{$n}) {
448        my $aref = $SUBCLASSES{$n};
449        push @$aref, $sc;
450        $SUBCLASSES{$n} = $aref;
451      } else {
452        $SUBCLASSES{$n} = [ $sc ];
453      }
454    } elsif (/^\s+(\w+)_fields;/) {
455      expand_define ($1, $n, \%defined);
456    } elsif (/^\s+(\w+_3DSOLID)/) { # special defines
457      expand_define ($1, $n, \%defined);
458    } elsif (/^\s+(\w+)_fields \((\w+)\)/) { # parametric macro
459      expand_define ($1, $n, \%defined, $2);
460    } elsif (/^\s+(COMMON_ENTITY_DIMENSION)/) {
461      expand_define ($1, $n, \%defined);
462    } elsif (/^$/ && $defined{$n}) {
463      warn "undef $n\n";
464      @old = ();
465      undef $n;
466    }
467    if ($f and $n and exists $DXF{$n}->{$f}) {
468      warn "  $f $DXF{$n}->{$f}\n";
469    }
470  }
471}
472
473# get dxf group for each struct field
474sub dxfin_spec {
475  my $fn = shift;
476  open my $in, "<", $fn  or die "$fn: $!";
477  dxf_in ($in);
478  close $in;
479}
480dxfin_spec "$srcdir/dwg.spec";
481$DXF{'BLOCK'}->{'name'} = 2; # and 3
482$DXF{'BLOCK'}->{'filename'} = 4;
483$DXF{'INSERT'}->{'block_header'} = 2;
484$DXF{'MINSERT'}->{'block_header'} = 2;
485$DXF{'POLYLINE_3D'}->{'flag'} = 70;
486$DXF{'POLYLINE_MESH'}->{'flag'} = 70;
487$DXF{'VISUALSTYLE'}->{'edge_hide_precision_flag'} = 290;
488$DXF{'VISUALSTYLE'}->{'is_internal_use_only'} = 291;
489# $DXF{'VISUALSTYLE'}->{'face_lighting_model'} = 71;
490$DXF{'DIMSTYLE_CONTROL'}->{'morehandles'} = 340;
491# $DXF{'DIMSTYLE'}->{'DIMFIT'} = 287;   # <= r14 only
492$DXF{'PROXY_ENTITY'}->{'version'} = 95; # or 91 <= r14
493$DXF{'DIMASSOC'}->{'intsect_gsmarker'} = 92;
494$DXF{'DIMASSOC_Ref'}->{'xrefpaths'} = 301;
495$DXF{'DIMSTYLE'}->{'flag'} = 70;
496$DXF{'PLOTSETTINGS'}->{'plotview'} = 6;
497$DXF{'SORTENTSTABLE'}->{'ents'} = 331;
498$DXF{'SORTENTSTABLE'}->{'sort_ents'} = 5;
499$DXF{'PLOTSETTINGS'}->{'shadeplot'} = 333;
500$DXF{'OCD_Dimension'}->{'block'} = 2;
501$DXF{'TABLECONTENT'}->{'tablestyle'} = 340;
502$DXF{'TABLESTYLE'}->{'name'} = 3; # not 300
503$DXF{'TABLE_Cell'}->{'cell_flag_override'} = 177;
504$DXF{'ACSH_HistoryNode'}->{'trans'} = 40; # but inc by 1 for 16
505# $DXF{'DIMENSION_ORDINATE'}->{'def_pt'} = 10;
506# $DXF{'DIMENSION_ORDINATE'}->{'feature_location_pt'} = 13;
507# $DXF{'DIMENSION_ORDINATE'}->{'leader_endpt'} = 14;
508# $DXF{'DIMENSION_ORDINATE'}->{'flag2'} = 70;
509# $DXF{'DIMENSION_ORDINATE'}->{'dimstyle'} = 3;
510# $DXF{'DIMENSION_ORDINATE'}->{'block'} = 2;
511$DXF{$_}->{'class_version'} = 280 for qw(ATTRIB ATTDEF); #r2010 only
512$DXF{$_}->{'elevation'} = 30 for qw(TEXT ATTRIB ATTDEF); # not 31
513$DXF{$_}->{'has_attribs'} = 66 for qw(INSERT MINSERT);
514#$DXF{$_}->{'has_vertex'} = 66 for qw(POLYLINE_2D POLYLINE_3D POLYLINE_PFACE);
515$DXF{UNDERLAYDEFINITION}->{'filename'} = 1;
516$DXF{UNDERLAYDEFINITION}->{'name'} = 2;
517$DXF{$_}->{'flag'} = 70 for qw(VERTEX_3D VERTEX_MESH VERTEX_PFACE_FACE POLYLINE_PFACE);
518my @solids = qw(3DSOLID REGION BODY
519                EXTRUDEDSURFACE LOFTEDSURFACE NURBSURFACE PLANESURFACE REVOLVEDSURFACE SWEPTSURFACE
520                ACSH_BREP_CLASS);
521$DXF{$_}->{'version'} = 70 for @solids;
522$DXF{$_}->{'encr_sat_data'} = 1 for @solids;
523$DXF{$_}->{'history_id'} = 350 for @solids;
524$DXF{$_}->{'acis_empty'} = 290 for @solids;
525$DXF{$_}->{'revision_guid[39]'} = 2 for @solids;
526my @annotscale = qw (TEXTOBJECTCONTEXTDATA MTEXTOBJECTCONTEXTDATA ALDIMOBJECTCONTEXTDATA
527                     MTEXTATTRIBUTEOBJECTCONTEXTDATA MLEADEROBJECTCONTEXTDATA LEADEROBJECTCONTEXTDATA
528                     BLKREFOBJECTCONTEXTDATA);
529$DXF{$_}->{'class_version'} = 70 for @annotscale;
530$DXF{$_}->{'is_default'} = 290 for @annotscale;
531$DXF{$_}->{'scale'} = 340 for @annotscale;
532
533dxfin_spec "$srcdir/header_variables_dxf.spec";
534$DXF{header_variables}->{'_3DDWFPREC'} = 40;
535
536$n = 'object_entity';
537dxfin_spec "$srcdir/common_entity_data.spec";
538dxfin_spec "$srcdir/common_entity_handle_data.spec";
539$DXF{$n}->{'color'} = $DXF{$n}->{'color_r11'} = 62;
540$DXF{$n}->{'color.rgb'} = 420; # handle 420?
541$DXF{$n}->{'color.book'} = 430;
542$DXF{$n}->{'color.alpha'} = 440;
543$DXF{$n}->{'paper_r11'} = 67;
544$DXF{$n}->{'plotstyle'} = 390;
545$DXF{$n}->{'ownerhandle'} = 330;
546$DXF{$n}->{'xdicobjhandle'} = 360;
547$DXF{$n}->{'reactors'} = 330;
548$DXF{$n}->{'preview_size'} = 160; # or 92
549
550$n = 'object_object';
551#dxfin_spec "$srcdir/common_object_handle_data.spec";
552$DXF{$n}->{'ownerhandle'} = 330;
553$DXF{$n}->{'xdicobjhandle'} = 360;
554$DXF{$n}->{'reactors'} = 330;
555
556$n = 'summaryinfo';
557dxfin_spec "$srcdir/summaryinfo.spec";
558
559# dxfclassname for each of our classes and subclasses (not complete)
560# Our NAME => 100
561%SUBCLASS = (
562  '3DSOLID' => "AcDbModelerGeometry",
563  DIMENSION_ALIGNED => "AcDbAlignedDimension",
564  DIMENSION_ORDINATE => "AcDbOrdinateDimension",
565  DIMENSION_ORDINATE => "AcDb2LineAngularDimension",
566  ARC_DIMENSION => "AcDbArcDimension",
567  LWPOLYLINE => "AcDbPolyline",
568  POLYLINE_3D => "AcDb3dPolyline",
569  #VERTEX => "AcDbVertex",
570  VERTEX_3D => "AcDb3dPolylineVertex",
571  HATCH => "AcDbHatch",
572  '3DFACE' => "AcDbFace",
573  DICTIONARYVAR => "DictionaryVariables",
574
575  "3DSOLID_silhouette" => "",
576  "3DSOLID_wire" => "",
577  "ACTIONBODY" => "AcDbAssocActionBody",
578  # AcDbAssocParamBasedActionBody?
579  # ASSOCDEPENDENCY AcDbAssocDependency
580  # ASSOCGEOMDEPENDENCY AcDbAssocDependency
581  # ASSOCGEOMDEPENDENCY AcDbAssocGeomDependency
582  # ASSOCOSNAPPOINTREFACTIONPARAM => AcDbAssocActionParam,
583  # ASSOCOSNAPPOINTREFACTIONPARAM => AcDbAssocCompoundActionParam,
584  # ASSOCVERTEXACTIONPARAM => AcDbAssocActionParam,
585  # ASSOCVERTEXACTIONPARAM => AcDbAssocSingleDependencyActionParam,
586  # ASSOCVERTEXACTIONPARAM => AcDbAssocVertexActionParam,
587  "CELLSTYLEMAP_Cell" => "",
588  "DIMASSOC_Ref" => "",
589  "DIMENSION_common" => "AcDbDimension",
590  "EVAL_Node" => "",
591  "FIELD_ChildValue" => "",
592  "GEODATA_meshface" => "",
593  "GEODATA_meshpt" => "",
594  "HATCH_DefLine" => "",
595  "HATCH_color" => "",
596  "HATCH_control_point" => "",
597  "HATCH_path" => "",
598  "HATCH_pathseg" => "",
599  "HATCH_polylinepath" => "",
600  "ACSH_HistoryNode," => 'AcDbShHistoryNode',
601  "LEADER_ArrowHead" => "",
602  "LEADER_BlockLabel" => "",
603  "LEADER_Break" => "",
604  "LEADER_Line" => "",
605  "LEADER_Node" => "",
606  "LTYPE_dash" => "",
607  "LWPOLYLINE_width" => "",
608  "MLEADER_Content" => "",
609  "MLEADER_AnnotContext" => "AcDbMLeaderAnnotContext",
610  "MLINESTYLE_line" => "",
611  "MLINE_line" => "",
612  "MLINE_vertex" => "",
613  # "OBJECTCONTEXTDATA" => "AcDbObjectContextData", # inlined, no subclass
614  "OCD_Dimension" => "AcDbDimensionObjectContextData",
615  "SPLINE_control_point" => "",
616  "SPLINE_point" => "",
617  "SUNSTUDY_Dates" => "",
618  "TABLEGEOMETRY_Cell" => "",
619  "TABLESTYLE_Cell" => "",
620  "TABLE_BreakHeight" => "",
621  "TABLE_BreakRow" => "",
622  "TABLE_CustomDataItem" => "",
623  "TABLE_cell" => "",
624  "TABLE_value" => "",
625
626  # TABLECONTENT => AcDbLinkedData
627  # TABLECONTENT => AcDbLinkedTableData
628  # TABLECONTENT => AcDbFormattedTableData
629  "TABLECONTENT" => "AcDbTableContent",
630  "TABLEGEOMETRY" => "AcDbTableGeometry",
631  "DATATABLE" => "ACDBDATATABLE",
632  # VIEWSTYLE_ModelDoc => "AcDbModelDocViewStyle",
633  DETAILVIEWSTYLE => "AcDbDetailViewStyle",
634  SECTIONVIEWSTYLE => "AcDbSectionViewStyle",
635  ASSOCGEOMDEPENDENCY => "AcDbAssocDependency",
636  ASSOCOSNAPPOINTREFACTIONPARAM => "ACDBASSOCOSNAPPOINTREFACTIONPARAM",
637  ASSOCALIGNEDDIMACTIONBODY => "ACDBASSOCALIGNEDDIMACTIONBODY",
638  ASSOCOSNAPPOINTREFACTIONPARAM => "ACDBASSOCOSNAPPOINTREFACTIONPARAM",
639  # ACSH_HISTORY_CLASS => AcDbShHistory
640  # ACSH_HISTORY_CLASS_Node => AcDbShHistoryNode
641  );
642
643# 300 CONTEXT_DATA{
644# unused
645my %SUBGROUP = (
646  MLEADER_AnnotContext => "CONTEXT_DATA{",
647  LEADER  => "LEADER{",
648  LEADER_Line => "LEADER_LINE{",
649  );
650
651# DXFNAME 0 => our name
652my %DXFALIAS = # see also CLASS.dxfname
653  (
654  ACDBDETAILVIEWSTYLE => "DETAILVIEWSTYLE",
655  ACDBSECTIONVIEWSTYLE => "SECTIONVIEWSTYLE",
656  ACDBPLACEHOLDER => "PLACEHOLDER",
657  ACDBASSOCACTION => "ASSOCACTION",
658  ACDBASSOCALIGNEDDIMACTIONBODY => "ASSOCALIGNEDDIMACTIONBODY",
659  ACDBASSOCGEOMDEPENDENCY => "ASSOCGEOMDEPENDENCY",
660  ACDBASSOCOSNAPPOINTREFACTIONPARAM => "ASSOCOSNAPPOINTREFACTIONPARAM",
661  ACDBASSOCALIGNEDDIMACTIONBODY => "ASSOCALIGNEDDIMACTIONBODY",
662  ACDBDATATABLE => "DATATABLE",
663  );
664my %DXFNAME =
665  (
666    POLYLINE_2D    => "POLYLINE",
667    POLYLINE_3D    => "POLYLINE",
668    POLYLINE_MESH  => "POLYLINE",
669    POLYLINE_PFACE => "POLYLINE",
670    VERTEX_2D         => "VERTEX",
671    VERTEX_3D         => "VERTEX",
672    VERTEX_MESH       => "VERTEX",
673    VERTEX_PFACE      => "VERTEX",
674    VERTEX_PFACE_FACE => "VERTEX",
675    DIMENSION_ALIGNED  => "DIMENSION",
676    DIMENSION_ANG2LN   => "DIMENSION",
677    DIMENSION_ANG3PT   => "DIMENSION",
678    DIMENSION_DIAMETER => "DIMENSION",
679    DIMENSION_LINEAR   => "DIMENSION",
680    DIMENSION_ORDINATE => "DIMENSION",
681    DIMENSION_RADIUS   => "DIMENSION",
682    TABLE => "ACAD_TABLE",
683    TABLECONTENT => "TABLE",
684    DETAILVIEWSTYLE => "ACDBDETAILVIEWSTYLE",
685    SECTIONVIEWSTYLE => "ACDBSECTIONVIEWSTYLE",
686    EVALUATION_GRAPH => "ACAD_EVALUATION_GRAPH",
687    DICTIONARYWDFLT => "ACDBDICTIONARYWDFLT",
688    PLACEHOLDER => "ACDBPLACEHOLDER",
689    CURVEPATH => "ACDBCURVEPATH",
690    MOTIONPATH => "ACDBMOTIONPATH",
691    POINTPATH => "ACDBPOINTPATH",
692    IBL_BACKGROUND => "RAPIDRTRENDERENVIRONMENT",
693    NAVISWORKSMODEL => "COORDINATION_MODEL", #?
694    PERSUBENTMGR => "ACDBPERSSUBENTMANAGER",
695    DYNAMICBLOCKPURGEPREVENTER => "ACDB_DYNAMICBLOCKPURGEPREVENTER_VERSION",
696    BLOCKREPRESENTATION => "ACDB_BLOCKREPRESENTATION_DATA",
697    DYNAMICBLOCKPROXYNODE => "ACAD_DYNAMICBLOCKPROXYNODE",
698    XREFPANELOBJECT => "EXACXREFPANELOBJECT",
699    GEOPOSITIONMARKER => "POSITIONMARKER",
700    PROXY_ENTITY => "ACAD_ENTITY_OBJECT",
701    PROXY_OBJECT => "ACAD_PROXY_OBJECT",
702    #PROXY_ENTITY => "ACAD_PROXY_ENTITY_WRAPPER",
703    #PROXY_OBJECT => "ACAD_PROXY_OBJECT_WRAPPER",
704   );
705
706sub dxfname {
707  $_ = shift;
708  return $DXFNAME{$_} if exists $DXFNAME{$_};
709  my $dxfname = $_;
710  if (/^(ASSOC|NAVISWORKS|POINTCLOUD)/) {
711    return "ACDB$dxfname";
712  }
713  if (/OBJECTCONTEXTDATA$/) {
714    return "ACDB_${_}_CLASS";
715  }
716  return $dxfname;
717}
718
719my $cfile = "$srcdir/dynapi.c";
720chmod 0644, $cfile if -e $cfile;
721open my $fh, ">", $cfile or die "$cfile: $!";
722
723# generate docs (GH #127)
724my $docfile = "$topdir/doc/dynapi.texi";
725chmod 0644, $docfile if -e $docfile;
726open my $doc, ">", $docfile or die "$docfile: $!";
727print $doc <<'EOF';
728@c This file is automatically generated by src/gen-dynapi.pl. Do not modify here.
729@c It is intended to be included within the main document.
730
731EOF
732
733sub is_table {
734  return shift =~ /^(?:BLOCK_HEADER|LAYER|STYLE|LTYPE|VIEW|UCS|
735                     VPORT|APPID|DIMSTYLE|VX_TABLE_RECORD)$/x;
736}
737
738sub is_table_control {
739  return shift =~ /^(?:BLOCK|LAYER|STYLE|LTYPE|VIEW|UCS|
740                     VPORT|APPID|DIMSTYLE|VX)_CONTROL$/x;
741}
742
743sub out_declarator {
744  my ($d,$tmpl,$key,$prefix) = @_;
745  my $n = "_dwg_$key" unless $key =~ /^_dwg_/;
746  my $ns = $tmpl;
747  $ns =~ s/^struct //;
748  my $type = $d->{type};
749  my $decl = $d->{declarators}->[0];
750  my $name = $decl->{declarator};
751  while ($name =~ /^\*/) {
752    $name =~ s/^\*//;
753    $type .= '*';
754  }
755  if ($prefix) { # optional, for unions only
756    $name = $prefix . "." . $name;
757  }
758  # unexpand BITCODE_ macros: e.g. unsigned int -> BITCODE_BL
759  my $bc = exists $h{$ns} ? $h{$ns}{$name} : undef;
760  if (!$bc && $ns =~ /_CONTROL$/) {
761    $bc = $h{COMMON_TABLE_CONTROL_FIELDS}{$name};
762  } elsif (!$bc && $ns =~ /_entity_POLYLINE_/) {
763    $bc = $h{COMMON_ENTITY_POLYLINE}{$name};
764  }
765  $type = $bc if $bc;
766  if ($name eq 'encr_sat_data') {
767    $type = 'char **'; $bc = '';
768  }
769  $type =~ s/\s+$//;
770  my $size = $bc ? "sizeof (BITCODE_$type)" : "sizeof ($type)";
771  $type =~ s/BITCODE_//;
772  # TODO: DIMENSION_COMMON, _3DSOLID_FIELDS macros
773  if ($type eq 'unsigned char') {
774    $type = 'RC';
775  } elsif ($type eq 'unsigned char*') {
776    $type = 'RC*';
777  } elsif ($type eq 'double') {
778    $type = 'BD';
779  } elsif ($type eq 'double*') {
780    $type = 'BD*';
781  } elsif ($type =~ /^Dwg_Bitcode_(\w+)/) {
782    $type = $1;
783  } elsif ($type eq 'char*') {
784    $type = 'TV';
785  } elsif ($type eq 'unsigned short int') {
786    $type = 'BS';
787  } elsif ($type eq 'uint16_t') {
788    $type = 'BS';
789  } elsif ($type eq 'unsigned int') {
790    $type = 'BL';
791  } elsif ($type eq 'unsigned int*') {
792    $type = 'BL*';
793  } elsif ($type eq 'uint32_t') {
794    $type = 'BL';
795  } elsif ($type eq 'uint32_t*') {
796    $type = 'BL*';
797  } elsif ($type eq 'Dwg_Object_Ref*') {
798    $type = 'H';
799  } elsif ($type eq 'Dwg_Object_Ref**') {
800    $type = 'H*';
801  } elsif ($type =~ /\b(unsigned|char|int|long|double)\b/) {
802    warn "unexpanded $type $n.$name\n";
803  } elsif ($type =~ /^struct/) {
804    if ($type =~ /\*$/) {
805      $size = "sizeof (void *)";
806    } else {
807      # e.g. MLEADER_Content.txt.
808      warn "inline struct $key.$name\n";
809      for (@{$c->struct($d->{type})->{declarations}}) {
810        out_declarator ($_, $tmpl, $key, $name);
811      }
812      #next;
813    }
814  } elsif ($type =~ /^union/) {
815    warn "inline union $key.$name\n";
816    for (@{$c->union($d->{type})->{declarations}}) {
817      out_declarator ($_, $tmpl, $key, $name);
818    }
819    #next;
820  } elsif ($type =~ /^HASH\(/) { # inlined struct or union
821    if ($type->{type} eq 'union' && $n !~ /^_dwg_object_/) {
822      # take all declarators and add the "$name." prefix
823      warn "note: union field $n.$name\n";
824      for (@{$type->{declarations}}) {
825        out_declarator ($_, $tmpl, $key, $name);
826      }
827      next;
828    } else {
829      warn "ignore inlined field $n.$name\n";
830      next;
831    }
832  }
833  if ($ENT{$key}->{$name}) {
834    $type = $ENT{$key}->{$name};
835  } else {
836    $ENT{$key}->{$name} = $type;
837  }
838  my $is_malloc = ($type =~ /\*$/ or $type =~ /^(T$|T[UVF]|D2T)/) ? 1 : 0;
839  my $is_indirect = ($is_malloc or $type =~ /^(struct|[23T]|H$)/) ? 1 : 0;
840  my $is_string = ($is_malloc and $type =~ /^(T[UV]?|D2T)$/) ? 1 : 0; # not TF or TFF
841  my $sname = $name;
842  if ($name =~ /\[(\d+)\]$/) {
843    $is_malloc = 0;
844    $size = "$1 * $size";
845    $sname =~ s/\[(\d+)\]$//;
846    $name = $sname if $sname eq 'conn_pts';
847  }
848  if ($type =~ /^TF/ && exists $SIZE{$key}->{$name}) {
849    $size = $SIZE{$key}->{$name};
850  }
851  my $dxf = $DXF{$key}->{$name};
852  if (!$dxf && $key =~ /DIMENSION/) {
853    $dxf = $DXF{COMMON_ENTITY_DIMENSION}->{$name};
854  }
855  if (!$dxf && $key =~ /ASSOC/) {
856    $dxf = $DXF{ASSOCACTION}->{$name};
857  }
858  $dxf = 0 unless $dxf;
859  warn "no dxf for $key: $name 0\n" unless $dxf or
860    ($name eq 'parent') or
861    ($key eq 'header_variables' and $name eq lc($name));
862
863  printf $fh "  { \"%s\",\t\"%s\", %s,  OFF (%s, %s),\n    %d,%d,%d, %d },\n",
864    $name, $type, $size, $tmpl, $sname, $is_indirect, $is_malloc, $is_string, $dxf;
865
866  print $doc "\@item $name\n$type", $dxf ? ",\tDXF $dxf" : "", "\n";
867}
868
869# until the type is a struct or union
870sub expand_typedef {
871  my $s = shift;
872  if ($s =~ /^(struct|union)/) {
873    return $s;
874  }
875  my $typedef = $c->typedef($s);
876  return undef unless $typedef;
877  my $type;
878  $s = $typedef->{type};
879  while ($s and $s !~ /^(struct|union)/) {
880    $s = expand_typedef ($s);
881  }
882  return $s;
883}
884
885my %out_struct;
886sub out_struct {
887  my ($tmpl, $n) = @_;
888  #print $fh " /* ", Data::Dumper->Dump([$s], [$n]), "*/\n";
889  my $key = $n;
890  my $sortedby = 'offset';
891  my $s = $c->struct($tmpl);
892  unless ($s) {
893    $s = $c->union($tmpl);
894  }
895  unless ($s) { # resolve abstract typedef to struct|union
896    $s = expand_typedef ($tmpl);
897    $s = $c->struct($s) if $s;
898  }
899  unless ($s->{declarations}) {
900    delete $ENT{$n};   # don't show up in dynapi_test.c
901    return;
902  }
903  if (exists $out_struct{$tmpl}) {
904    warn "skip duplicate $tmpl\n";
905    my $see = $out_struct{$tmpl};
906    print $doc "\@indentedblock\n";
907    print $doc "\@xref{$see}\n";
908    print $doc "\@end indentedblock\n\n";
909    return;
910  }
911  $out_struct{$tmpl} = $key;
912  $n = "_dwg_$n" unless $n =~ /^_dwg_/;
913  my @declarations = @{$s->{declarations}};
914  if ($n =~ /^_dwg_(header_variables|object_object|object_entity)$/) {
915    @declarations = sort {
916      my $aname = $a->{declarators}->[0]->{declarator};
917      my $bname = $b->{declarators}->[0]->{declarator};
918      $aname =~ s/^\*//g;
919      $bname =~ s/^\*//g;
920      return $aname cmp $bname
921    } @declarations;
922    $sortedby = 'name';
923  }
924  if ($tmpl =~ /_dwg_object_/) {
925    if (is_table($key)) {
926      print $doc "\@cindex table, $key\n\n";
927      print $doc "$key is a table object.\n\n";
928    }
929    elsif (is_table_control($key)) {
930      print $doc "\@cindex table_control, $key\n\n";
931      print $doc "$key is a table_control object.\n\n";
932    }
933  }
934  print $doc "\@indentedblock\n";
935  print $doc "\@vtable \@code\n\n";
936  print $fh "/* from typedef $tmpl: (sorted by $sortedby) */\n",
937    "static const Dwg_DYNAPI_field $n","_fields[] = {\n";
938  for my $d (@declarations) {
939    out_declarator($d, $tmpl, $key);
940  }
941  print $fh "  {NULL,\tNULL,\t0,\t0,\t0,0,0, 0},\n";
942  print $fh "};\n";
943  print $doc "\n\@end vtable\n";
944  print $doc "\@end indentedblock\n\n";
945}
946
947sub maxlen {
948  my $maxlen = 0;
949  for (@_) {
950    $maxlen = length($_) if $maxlen < length($_);
951  }
952  $maxlen
953}
954$max_entity_names = 1+maxlen(@entity_names);
955$max_object_names = 1+maxlen(@object_names);
956my %entity_names = map {$_ => 1} @entity_names;
957my %object_names = map {$_ => 1} @object_names;
958$max_subclasses = 0;
959for (keys %SUBCLASSES) {
960  if (!exists $entity_names{$_} and !exists $object_names{$_}) {
961    # FIXME: find parent class and add array to it.
962    # delete $SUBCLASSES{$_};
963  }
964}
965for (sort @entity_names) {
966# FIXME: fixup duplicates in TEXT
967  if ($_ eq 'TEXT') {
968    $SUBCLASSES{$_} = [ 'AcDbText' ];
969  } elsif (/^...UNDERLAY$/) {
970    $SUBCLASSES{$_} = [ 'AcDbUnderlayReference' ];
971  }
972  my $aref = $SUBCLASSES{$_};
973  unshift @$aref, 'AcDbEntity';
974  $SUBCLASSES{$_} = $aref; # if it didnt exist
975  my $len = @$aref;
976  $max_subclasses = $len if $len > $max_subclasses;
977}
978# /^(BLOCK_HEADER|LAYER|STYLE|LTYPE|VIEW|UCS|VPORT|APPID|DIMSTYLE|VX_TABLE_RECORD)$/
979my %table_dxfname = ( # See COMMON_TABLE_FLAGS
980  BLOCK_HEADER => 'Block',
981  LAYER => 'Layer',
982  STYLE => 'TextStyle',
983  LTYPE => 'Linetype',
984  VIEW => 'View',
985  UCS => 'UCS',
986  VPORT => 'Viewport',
987  APPID => 'RegApp',
988  DIMSTYLE => 'DimStyle',
989  VX_TABLE_RECORD => 'VX',
990);
991for (sort @object_names) {
992  my $aref = $SUBCLASSES{$_};
993  if (is_table ($_)) {
994    unshift @$aref, ('AcDbSymbolTableRecord', 'AcDb' . $table_dxfname{$_} . 'TableRecord');
995  } elsif (is_table_control ($_)) {
996    unshift @$aref, 'AcDbSymbolTable';
997  } else {
998    unshift @$aref, 'AcDbObject';
999  }
1000  $SUBCLASSES{$_} = $aref; # if it didnt exist
1001  my $len = @$aref;
1002  $max_subclasses = $len if $len > $max_subclasses;
1003}
1004
1005# ---------------------------------------------------------------
1006for (<DATA>) {
1007  # expand enum or struct
1008  if (/^(.*)\@\@(\w+ \w+)\@\@(.*)/) {
1009    my ($pre, $post) = ($1, $3);
1010    my $tmpl = $2;
1011    print $fh $pre;
1012    if ($tmpl =~ /^enum (\w+)/) {
1013      my $s = $c->enum($tmpl);
1014      #print $fh "\n/* ";
1015      #print $fh Data::Dumper->Dump([$s], [$1]);
1016      #print $fh "\n*/";
1017      my $i = 0;
1018      my @keys = map { s/^DWG_TYPE__3D/DWG_TYPE_3D/; $_ } keys %{$s->{enumerators}};
1019      for (sort @keys) {
1020        my ($k,$v) = ($_, $s->{enumerators}->{$_});
1021        if ($tmpl eq 'enum DWG_OBJECT_TYPE') {
1022          $k =~ s/^DWG_TYPE_//;
1023          # Let the type rather be symbolic: DWG_TYPE_SOLID, not int
1024          my $vs = $_;
1025          if (!$v && $k =~ /^3D/) {
1026            $v = $s->{enumerators}->{'DWG_TYPE__'.$k};
1027            $vs = 'DWG_TYPE__'.$k;
1028          }
1029          my $size = "0";
1030          if ($c->struct("_dwg_entity_$k")) {
1031            $size = "sizeof (struct _dwg_entity_$k)";
1032          } elsif ($c->struct("_dwg_object_$k")) {
1033            $size = "sizeof (struct _dwg_object_$k)";
1034          #} elsif ($c->typedef("Dwg_Object_$k")) {
1035          #  my $type = expand_typedef ("Dwg_Object_$k");
1036          #  if ($type) {
1037          #    $size = "sizeof (Dwg_Object_$k)";
1038          #    $k = $type;
1039          #    $k =~ s/struct //;
1040          #    $k =~ s/_dwg_(?:abstract)?object_//;
1041          #    $k =~ s/ //g;
1042          #  }
1043          #} elsif ($c->typedef("Dwg_Entity_$k")) {
1044          #  my $type = expand_typedef ("Dwg_Entity_$k");
1045          #  if ($type) {
1046          #    $size = "sizeof (Dwg_Entity_$k)";
1047          #    $k = $type;
1048          #    $k =~ s/struct //;
1049          #    $k =~ s/_dwg_(?:abstract)?entity_//;
1050          #    $k =~ s/ //g;
1051          #  }
1052          }
1053          # see if the fields do exist:
1054          my $fields = exists $structs{$k} ? "_dwg_".$k."_fields" : "NULL";
1055          if ($k =~ /^(BODY|REGION)$/) {
1056            $fields = "_dwg_3DSOLID_fields";
1057            $size = "sizeof (struct _dwg_entity_3DSOLID)";
1058          } elsif ($k eq 'XLINE') {
1059            $fields = "_dwg_RAY_fields";
1060            $size = "sizeof (struct _dwg_entity_RAY)";
1061          } elsif ($k =~ /^(PDF|DWF|DGN)DEFINITION$/) {
1062            $fields = "_dwg_UNDERLAYDEFINITION_fields";
1063            $size = "sizeof (Dwg_Object_$k)";
1064          } elsif ($k =~ /^(PDF|DWF|DGN)UNDERLAY$/) {
1065            $fields = "_dwg_UNDERLAY_fields";
1066            $size = "sizeof (Dwg_Entity_$k)";
1067          } elsif ($k =~ /^ASSOCARRAY(?:MODIFY|PATH|POLAR|RECTANGULAR)PARAMETERS$/) {
1068            $fields = "_dwg_ASSOCARRAYPARAMETERS_fields";
1069            $size = "sizeof (Dwg_Object_ASSOCARRAYPARAMETERS)";
1070          } elsif ($k =~ /^VERTEX_(MESH|PFACE)$/) {
1071            $fields = "_dwg_VERTEX_3D_fields";
1072            $size = "sizeof (struct _dwg_entity_VERTEX_3D)";
1073          } elsif ($k =~ /^PROXY_LWPOLYLINE$/) { # TODO subent, not entity
1074            $size = "sizeof (struct _dwg_entity_PROXY_LWPOLYLINE)";
1075          }
1076          $DWG_TYPE{$k} = $vs;
1077          printf $fh "  { \"%s\", %s /*(%d)*/, %s, %s },\t/* %d */\n",
1078            $k, $vs, $v, $fields, $size, $i++;
1079        } else {
1080          printf $fh "  { \"%s\", %d },\t/* %d */\n",
1081            $k, $v, $i++;
1082        }
1083      }
1084    } elsif ($tmpl =~ /^list (\w+)/) {
1085      no strict 'refs';
1086      my $n = $1;
1087      my $i = 0;
1088      my $maxlen = 0;
1089      for (@{$n}) {
1090        $maxlen = length($_) if $maxlen < length($_);
1091      }
1092      if ($n eq 'subclasses') {
1093        for (sort @{$n}) {
1094          if (/^_dwg_(.*)/) {
1095            my $n = $1;
1096            # find class type for this subclass. DWG_TYPE_parent if unique, or -1
1097            my $type = 0;
1098            for my $o (keys %DWG_TYPE) {
1099              my $match = qr '^' . $o . '_\w+';
1100              if ($n =~ $match) {
1101                if ($type) {
1102                  $type = '-1'; # multiple
1103                } else {
1104                  $type = '(int)'.$DWG_TYPE{$o};
1105                }
1106              }
1107            }
1108            my $size = "sizeof (Dwg_$n)";
1109            if ($c->union("$_")) {
1110              $size = "sizeof (Dwg_$n)";
1111            }
1112            my $subclass = $SUBCLASS{$n};
1113            if ($subclass) {
1114              $subclass = '"' . $subclass . '"';
1115            } else {
1116              $subclass = "NULL";
1117            }
1118            printf $fh "  { \"%s\", %s, %s, %s, %s },\t/* %d */\n",
1119              $n, $type, $subclass, $_ . "_fields", $size, $i++;
1120          }
1121        }
1122      } elsif ($n eq 'name_subclasses') {
1123        #print Dumper \%SUBCLASSES;
1124        for (sort keys %SUBCLASSES) {
1125          my $cl = $_;
1126          if (exists $entity_names{$cl} or exists $object_names{$cl}) {
1127            my $a = $SUBCLASSES{$cl};
1128            # $cl =~ s/^3D/_3D/;
1129            printf $fh "  { \"%s\", {", $cl;
1130            for (0 .. $max_subclasses-1) {
1131              if ($a->[$_]) {
1132                printf $fh "\"%s\"", $a->[$_];
1133              } else {
1134                printf $fh "NULL";
1135              }
1136              if ($_ < $max_subclasses - 1) {
1137                printf $fh ", ";
1138              }
1139            }
1140            printf $fh "} },\n";
1141          }
1142        }
1143      } else {
1144        for (@{$n}) {
1145          my $len = length($_);
1146          printf $fh "  \"%s\" \"%s\",\t/* %d */\n", $_, "\\0" x ($maxlen-$len), $i++;
1147        }
1148      }
1149    } elsif ($tmpl =~ /^scalar (\w+)/) {
1150      no strict 'refs';
1151      my $n = $1;
1152      printf $fh ${$n};
1153    } elsif ($tmpl =~ /^for dwg_entity_ENTITY/) {
1154      print $doc "\n\@node ENTITIES\n\@section ENTITIES\n\@cindex ENTITIES\n\n";
1155      print $doc "All graphical objects with its fields. \@xref{Common Entity fields}\n\n";
1156      for (@entity_names) {
1157        print $doc "\@strong{$_} \@anchor{$_}\n\@cindex entity, $_\n",
1158          "\@vindex $_\n" unless $_ eq 'DIMENSION_';
1159        print $doc "\@anchor{UNDERLAY}\n\@vindex UNDERLAY\n" if /^DGNUNDERLAY/;
1160        print $doc "\n" unless $_ eq 'DIMENSION_';
1161        my $typedef = $c->typedef("Dwg_Entity_$_");
1162        # multiple type aliases, only emit one _field[]
1163        if ($typedef and $typedef->{type} ne "struct _dwg_entity_$_") {
1164          my $type = expand_typedef ($typedef->{type});
1165          if ($type) {
1166            my $n = $type;
1167            $n =~ s/struct //;
1168            $n =~ s/_dwg_(?:abstract)?entity_//;
1169            $n =~ s/ //g;
1170            out_struct($type, $n);
1171          } else {
1172            out_struct("struct _dwg_entity_$_", $_);
1173          }
1174        } else {
1175          out_struct("struct _dwg_entity_$_", $_);
1176        }
1177      }
1178    } elsif ($tmpl =~ /^for dwg_object_OBJECT/) {
1179      print $doc "\n\@node OBJECTS\n\@section OBJECTS\n\@cindex OBJECTS\n\n";
1180      print $doc "All non-graphical objects with its fields. \@xref{Common Object fields}\n\n";
1181      for (@object_names) {
1182        print $doc "\@strong{$_} \@anchor{$_}\n\@cindex object, $_\n\@vindex $_\n";
1183        print $doc "\@anchor{UNDERLAYDEFINITION}\n\@vindex UNDERLAYDEFINITION\n" if /^PDFDEFINITION/;
1184        print $doc "\@anchor{ASSOCARRAYPARAMETERS}\n\@vindex ASSOCARRAYPARAMETERS\n" if /^ASSOCARRAYMODIFYPARAMETERS/;
1185        print $doc "\n";
1186        my $typedef = $c->typedef("Dwg_Object_$_");
1187        if ($typedef and $typedef->{type} ne "struct _dwg_object_$_") {
1188          my $type = expand_typedef ($typedef->{type}); # unify to one struct
1189          if ($type) {
1190            my $n = $type;
1191            $n =~ s/struct //;
1192            $n =~ s/_dwg_(?:abstract)?object_//;
1193            $n =~ s/ //g;
1194            out_struct($type, $n);
1195          } else {
1196            out_struct("struct _dwg_object_$_", $_);
1197          }
1198        } else {
1199          out_struct("struct _dwg_object_$_", $_);
1200        }
1201      }
1202    } elsif ($tmpl =~ /^for dwg_subclasses/) {
1203      for (@subclasses) {
1204        my ($name) = $_ =~ /^_dwg_(.*)/;
1205        print $doc "\@strong{Dwg_$name} \@anchor{Dwg_$name}\n\@vindex Dwg_$name\n\n";
1206        if ($unions{$_}) {
1207          out_struct("union $_", $name);
1208        } else {
1209          out_struct("struct $_", $name);
1210        }
1211      }
1212    } elsif ($tmpl =~ /^struct _dwg_(\w+)/) {
1213      if ($1 eq 'header_variables') {
1214        print $doc "\n\@node HEADER\n\@section HEADER\n\@cindex HEADER\n\n";
1215        print $doc "All header variables.\n\n";
1216      } elsif ($1 eq 'object_object') {
1217        print $doc "\@strong{Common Object fields} \@anchor{Common Object fields}\n";
1218        print $doc "\@cindex Common Object fields\n\n";
1219      } elsif ($1 eq 'object_entity') {
1220        print $doc "\@strong{Common Entity fields} \@anchor{Common Entity fields}\n";
1221        print $doc "\@cindex Common Entity fields\n\n";
1222      } elsif ($1 eq 'summaryinfo') {
1223        print $doc "\@strong{SummaryInfo fields} \@anchor{SummaryInfo fields}\n";
1224        print $doc "\@cindex SummaryInfo fields\n\n";
1225        print $doc "\@pxref{SummaryInfo}\n\n";
1226      } else {
1227        print $doc "\@strong{$1}\n";
1228        print $doc "\@vindex $1\n\n";
1229      }
1230      out_struct($tmpl, $1);
1231    } elsif ($tmpl =~ /^struct Dwg_(\w+)/) {
1232      print $doc "\@strong{$1}\n";
1233      print $doc "\@vindex $1\n\n";
1234      out_struct($tmpl, $1);
1235    }
1236    print $fh $post,"\n";
1237  } else {
1238    print $fh $_;
1239  }
1240}
1241chmod 0444, $fh;
1242close $fh;
1243chmod 0444, $doc;
1244close $doc;
1245
1246# TODO: use dwg.h formats
1247my %FMT = (
1248    'double' => '%g',
1249    'unsigned char' => '%c',
1250    'unsigned int' => '%u',
1251    'unsigned long' => '%lu',
1252    'unsigned short int' => '%hu',
1253    'short' => '%hd',
1254    'long' => '%l',
1255    'char**' => '%p',
1256    'TV' => '%s',
1257    'T'  => '%s',
1258    'D2T' => '%s',
1259    'TU' => '%ls',
1260    'TFF' => '%s',
1261    'BD' => '%g',
1262    'BL' => '%u',
1263    'BS' => '%hu',
1264    'RD' => '%g',
1265    'RL' => '%u',
1266    'RS' => '%hu',
1267    'RC' => '%u',
1268    'RC*' => '%s',
1269    );
1270
1271# ---------------------------------------------------------------
1272# The simple list of macro defs (linear search)
1273my $objfile = "$srcdir/objects.inc";
1274chmod 0644, $objfile if -e $objfile;
1275open my $inc, ">", "$objfile.tmp" or die "$objfile: $!";
1276print $inc <<"EOF";
1277/* ex: set ro ft=c: -*- mode: c; buffer-read-only: t -*- */
1278/*****************************************************************************/
1279/*  LibreDWG - free implementation of the DWG file format                    */
1280/*                                                                           */
1281/*  Copyright (C) 2019-2020 Free Software Foundation, Inc.                   */
1282/*                                                                           */
1283/*  This library is free software, licensed under the terms of the GNU       */
1284/*  General Public License as published by the Free Software Foundation,     */
1285/*  either version 3 of the License, or (at your option) any later version.  */
1286/*  You should have received a copy of the GNU General Public License        */
1287/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
1288/*****************************************************************************/
1289
1290/*
1291 * objects.inc: define all object and entities
1292 * written by Reini Urban
1293 * generated by src/gen-dynapi.pl from include/dwg.h, do not modify.
1294 */
1295
1296EOF
1297
1298for my $name (@entity_names) {
1299  my $xname = $name =~ /^3/ ? "_$name" : $name; # 3DFACE, 3DSOLID
1300  next if $name eq 'DIMENSION_';
1301  next if $name eq 'PROXY_LWPOLYLINE';
1302  print $inc "DWG_ENTITY ($xname)\n";
1303}
1304print $inc "\n";
1305for my $name (@object_names) {
1306  print $inc "DWG_OBJECT ($name)\n";
1307}
1308close $inc;
1309mv_if_not_same ("$objfile.tmp", $objfile);
1310chmod 0444, $objfile;
1311
1312# ---------------------------------------------------------------
1313my $infile = "$topdir/test/unit-testing/dynapi_test.c.in";
1314open $in, $infile or die "$infile: $!";
1315$cfile  = "$topdir/test/unit-testing/dynapi_test.c";
1316chmod 0644, $cfile if -e $cfile;
1317open $fh, ">", $cfile or die "$cfile: $!";
1318print $fh "/* ex: set ro ft=c: -*- mode: c; buffer-read-only: t -*- */\n";
1319
1320for (<$in>) {
1321  print $fh $_;
1322  if (m{/\* \@\@for test_HEADER\@@ \*/}) {
1323    my $s = $c->struct('_dwg_header_variables');
1324    for my $d (@{$s->{declarations}}) {
1325      my $type = $d->{type};
1326      my $decl = $d->{declarators}->[0];
1327      my $name = $decl->{declarator};
1328      while ($name =~ /^\*/) {
1329        $name =~ s/^\*//;
1330        $type .= '*';
1331      }
1332      $type =~ s/ $//g;
1333      my $xname = $name =~ /^3/ ? "_$name" : $name;
1334      my $lname = lc $xname;
1335      my $var = $lname;
1336      my $sname = $name;
1337      if (exists $ENT{header_variables}->{$name}) {
1338        $type = $ENT{header_variables}->{$name};
1339      }
1340      $type =~ s/D_1$/D/;
1341      my $fmt = exists $FMT{$type} ? $FMT{$type} : undef;
1342      if (!$fmt) {
1343        if ($type =~ /[ \*]/ or $type eq 'H') {
1344          $fmt = '%p';
1345        } else {
1346          $fmt = "\" FORMAT_$type \"";
1347        }
1348      }
1349      my $is_ptr = ($type =~ /^(struct|Dwg_)/ or
1350                    $type =~ /^[23HT]/ or
1351                    $type =~ /\*$/ or
1352                    $var  =~ /\[\d+\]$/ or
1353                    $type =~ /^(BE|CMC)$/)
1354        ? 1 : 0;
1355      if ($var  =~ /\[\d+\]$/) {
1356        $lname =~ s/\[\d+\]$//g;
1357        $sname =~ s/\[\d+\]$//g;
1358      }
1359      my $stype = $type;
1360      $type = 'BITCODE_'.$type unless ($type =~ /^(struct|Dwg_)/ or $type =~ /^[a-z]/);
1361      if (!$is_ptr) {
1362        print $fh <<"EOF";
1363  {
1364    $type $var;
1365    if (dwg_dynapi_header_value (dwg, "$name", &$var, NULL)
1366        && $var == dwg->header_vars.$name)
1367      pass ();
1368    else
1369      fail ("HEADER.$name [$stype] $fmt != $fmt", dwg->header_vars.$sname, $var);
1370EOF
1371        if ($type =~ /(int|long|short|char ||double|_B\b|_B[BSLD]\b|_R[CSLD])/) {
1372          print $fh "    $var++;\n";
1373        }
1374        print $fh <<"EOF";
1375    if (dwg_dynapi_header_set_value (dwg, "$name", &$var, 0)
1376        && $var == dwg->header_vars.$name)
1377      pass ();
1378    else
1379      fail ("HEADER.$name [$stype] set+1 $fmt != $fmt",
1380            dwg->header_vars.$sname, $var);
1381EOF
1382        if ($type =~ /(int|long|short|char ||double|_B\b|_B[BSLD]\b|_R[CSLD])/) {
1383          print $fh "    $var--;\n";
1384          print $fh "    dwg_dynapi_header_set_value (dwg, \"$name\", &$var, 0);\n";
1385        }
1386        print $fh "\n  }\n";
1387      } else {
1388        print $fh <<"EOF";
1389  {
1390    $type $var;
1391    if (dwg_dynapi_header_value (dwg, "$name", &$lname, NULL)
1392EOF
1393        if ($type !~ /\*\*/) {
1394          print $fh <<"EOF";
1395        && !memcmp (&$lname, &dwg->header_vars.$sname, sizeof (dwg->header_vars.$sname))
1396EOF
1397        }
1398        print $fh <<"EOF";
1399       )
1400      pass ();
1401    else
1402      fail ("HEADER.$name [$stype]");
1403  }
1404EOF
1405      }
1406    }
1407  }
1408  if (m{/\* \@\@for if_test_OBJECT\@\@ \*/}) { # The impl, inside test_object
1409    for my $name (@entity_names, @object_names) {
1410      my $xname = $name =~ /^3/ ? "_$name" : $name; # 3DFACE, 3DSOLID
1411      #next if $name eq 'DIMENSION_';
1412      next if $name =~ /^(PROXY_LWPOLYLINE|UNKNOWN_)/;
1413      print $fh "  else" if $name ne '3DFACE'; # the first
1414      print $fh <<"EOF";
1415  if (obj->fixedtype == DWG_TYPE_$xname)
1416    error += test_$xname(obj);
1417EOF
1418    }
1419  }
1420  # The first, as decl
1421  if (m{/\* \@\@for test_OBJECT\@\@ \*/}) {
1422    for my $name (@entity_names, @object_names) {
1423      #next if $name eq 'DIMENSION_';
1424      #TABLE is stored as fixedtype UNKNOWN_ENT, so the dynapi test would fail
1425      next if $name =~ /^(PROXY_LWPOLYLINE|UNKNOWN_)/;
1426      my $is_ent = grep { $name eq $_ } @entity_names;
1427      my ($Entity, $lentity) = $is_ent ? ('Entity', 'entity') : ('Object', 'object');
1428      my $xname = $name =~ /^3/ ? "_$name" : $name;
1429      my $lname = lc $xname;
1430      my $struct = "Dwg_$Entity" . "_$xname";
1431      print $fh <<"EOF";
1432static int test_$xname (const Dwg_Object *obj)
1433{
1434  int error = 0;
1435  const Dwg_Object_$Entity *restrict obj_obj = obj->tio.$lentity;
1436  $struct *restrict $lname = obj->tio.$lentity->tio.$xname;
1437  failed = 0;
1438  if (!obj_obj || !$lname)
1439    {
1440      fail ("NULL $xname");
1441      return 1;
1442    }
1443EOF
1444
1445  for my $var (sort keys %{$ENT{$name}}) {
1446    my $type = $ENT{$name}->{$var};
1447    # if 0 ignored in .spec
1448    # next if $type eq 'T' and $name eq 'LIGHT' and $var eq 'web_file';
1449    # next if $type eq 'TF' and $name eq 'SUN' and $var eq 'bytes';
1450    my $fmt = exists $FMT{$type} ? $FMT{$type} : undef;
1451    if (!$fmt) {
1452      if ($type =~ /[ \*]/ or $type eq 'H') {
1453        $fmt = '%p';
1454      } else {
1455        $fmt = "\" FORMAT_$type \"";
1456      }
1457    }
1458    my $key = $var;
1459    my $svar = $var;
1460    my $skey = $var;
1461    my $is_ptr = ($type =~ /^(struct|Dwg_)/ or
1462                  $type =~ /^[TH23]/ or
1463                  $type =~ /\*$/ or
1464                  $var =~ /\[\d+\]$/ or
1465                  $type =~ /^(BE|CMC)$/)
1466      ? 1 : 0;
1467    if ($var  =~ /\./) { # embedded structs, like ovr.name. some have fields, some not
1468      next if $var =~ /^ovr\./;
1469      $svar =~ s/\./_/g;
1470      $var = $svar;
1471    }
1472    if ($var =~ /\[\d+\]$/) {
1473      $svar =~ s/\[\d+\]$//g;
1474      $skey =~ s/\[\d+\]$//g;
1475      $var = $svar if $var =~ /^conn_pts\[\d\]$/;
1476    }
1477    next if $key eq 'evalexpr.value.text1'; # already handled by evalexpr memcmp
1478    my $stype = $type;
1479    $type =~ s/D_1$/D/;
1480    $type = 'BITCODE_'.$type unless ($type =~ /^(struct|Dwg_)/ or $type =~ /^[a-z]/);
1481    if (!$is_ptr) {
1482      # TODO DEBUGGING [BR]D can be nan
1483      print $fh <<"EOF";
1484  {
1485    $type $var;
1486    if (dwg_dynapi_entity_value ($lname, "$name", "$key", &$svar, NULL)
1487        && $var == $lname->$key)
1488      pass ();
1489    else
1490      fail ("$name.$key [$stype] $fmt != $fmt", $lname->$key, $svar);
1491EOF
1492      if ($type =~ /(int|long|short|char|double|_B\b|_B[BSLD]\b|_R[CSLD])/) {
1493        print $fh "    $svar++;\n";
1494      }
1495      print $fh <<"EOF";
1496    if (dwg_dynapi_entity_set_value ($lname, "$name", "$key", &$svar, 0)
1497        && $var == $lname->$key)
1498      pass ();
1499    else
1500      fail ("$name.$key [$stype] set+1 $fmt != $fmt", $lname->$key, $svar);
1501EOF
1502      if ($type =~ /(int|long|short|char ||double|_B\b|_B[BSLD]\b|_R[CSLD])/) {
1503        print $fh "    $lname->$key--;";
1504      }
1505      print $fh "\n  }\n";
1506    } elsif ($type =~ /\*$/ and $type !~ /(RC\*|struct _dwg_object_)/
1507             # no countfield
1508             and $var !~ /^(ref|block_size|extra_acis_data|objid_object_handles)$/
1509             # VECTOR_N
1510             and $var !~ /(_transform|_transmatrix1?|shhn_pts)$/) {
1511      my %countfield = (
1512        attribs => 'num_owned',
1513        attribs => 'num_owned', # XXX TABLE
1514        vertex => 'num_owned',
1515        itemhandles => 'numitems',
1516        entities => 'num_owned',
1517        #inserts => 'num_inserts',
1518        #groups => 'num_groups',
1519        #field_handles => 'num_fields',
1520        sort_ents => 'num_ents',
1521        attr_def_id => 'num_attr_defs',
1522        layer_entries => 'num_entries',
1523        readdeps  => 'num_deps',
1524        writedeps => 'num_deps',
1525        dashes_r11 => 'num_dashes',
1526        texts => 'numitems',
1527        encr_sat_data => 'num_blocks',
1528        );
1529      my $countfield = exists $countfield{$var} ? $countfield{$var} : "num_$var";
1530      $countfield = 'num_dashes' if $name eq 'LTYPE' and $var eq 'styles';
1531      my $count = 1;
1532      if ($var eq 'encr_sat_data') {
1533        print $fh <<"EOF";
1534  {
1535    $type $var;
1536    if (dwg_dynapi_entity_value ($lname, "$name", "$key", &$svar, NULL)
1537        && !memcmp (&$svar, &$lname->$skey, sizeof ($lname->$skey)))
1538      pass ();
1539    else
1540      fail ("$name.$key [$stype]");
1541  }
1542EOF
1543      }
1544      elsif ($var eq 'reactors' and $type eq 'BITCODE_H*') {
1545        print $fh <<"EOF";
1546  {
1547    $type $var;
1548    BITCODE_BL count = obj_obj->num_reactors;
1549    if (dwg_dynapi_entity_value ($lname, "$name", "$key", &$svar, NULL)
1550        && $svar == $lname->$key)
1551      pass ();
1552    else
1553      fail ("$name.$var [$stype] * %u $countfield", count);
1554  }
1555EOF
1556      } else {
1557        print $fh <<"EOF";
1558  {
1559    $type $var;
1560    BITCODE_BL count = 0;
1561    if (dwg_dynapi_entity_value ($lname, "$name", "$countfield", &count, NULL)
1562        && dwg_dynapi_entity_value ($lname, "$name", "$var", &$svar, NULL)
1563EOF
1564        if ($type eq 'BITCODE_BD') {
1565          print $fh "        && (isnan ($svar) || $svar == $lname->$svar)";
1566        }
1567        elsif ($type !~ /\*\*/) {
1568          print $fh "        && $svar == $lname->$svar";
1569        }
1570        print $fh ")\n";
1571        print $fh <<"EOF";
1572      pass ();
1573    else
1574      fail ("$name.$var [$stype] * %u $countfield", count);
1575  }
1576EOF
1577      }
1578    } else { # is_ptr
1579      my $is_str;
1580      my $vardecl = $var;
1581      my $size = "sizeof ($type)";
1582      if (0 and $stype =~ /^TF/) {
1583        my $_size = $SIZE{$name}->{$var};
1584        if ($_size && $_size =~ /^\d+$/) {
1585          $type = 'char';
1586          $size = $_size;
1587          $vardecl .= "[$size]";
1588          if ($var eq 'strings_area') {
1589            $vardecl .= ";\n    const Dwg_Data* dwg = obj->parent;";
1590            $vardecl .= "\n    const int size = dwg->header.version >= 2004 ? 512 : 256";
1591            $size = 'size';
1592          }
1593        }
1594      }
1595      print $fh <<"EOF";
1596  {
1597    $type $vardecl;
1598    if (dwg_dynapi_entity_value ($lname, "$name", "$key", &$svar, NULL)
1599EOF
1600        if ($stype =~ /^(TV|T|TU|RC\*|unsigned char\*|char\*)$/) {
1601          $is_str = 1;
1602          print $fh "        && $svar\n";
1603          print $fh "           ? strEQ ((char *)$svar, (char *)$lname->$key)\n";
1604          print $fh "           : !$lname->$key)\n";
1605        } elsif (0 and $stype =~ /^TF/ and $size !~ /^sizeof/) {
1606          print $fh "        && !memcmp ($svar, $lname->$skey, $size))\n";
1607        } elsif ($type !~ /\*\*/) {
1608          print $fh "        && !memcmp (&$svar, &$lname->$skey, $size))\n";
1609        } else {
1610          print $fh ")\n";
1611        }
1612        if ($is_str) {
1613          print $fh <<"EOF";
1614      pass ();
1615    else
1616      fail ("$name.$key [$stype] '$fmt' <> '$fmt'", $svar, $lname->$skey);
1617  }
1618EOF
1619        } else {
1620          print $fh <<"EOF";
1621        pass ();
1622    else
1623        fail ("$name.$key [$stype]");
1624  }
1625EOF
1626        }
1627      }
1628    }
1629    print $fh <<"EOF";
1630  if (failed && (is_class_unstable ("$name") || is_class_debugging ("$name")))
1631    {
1632      ok ("%s failed %d tests (TODO unstable)", "$name", failed);
1633      failed = 0;
1634    }
1635  return failed;
1636}
1637EOF
1638    }
1639  }
1640
1641  if (m{/\* \@\@for if_test_OBJECT\@\@ \*/}) {
1642    for my $name (@entity_names, @object_names) {
1643      my $xname = $name =~ /^3/ ? "_$name" : $name; # 3DFACE, 3DSOLID
1644      next if $name eq 'DIMENSION_';
1645      next if $name =~ /^(PROXY_LWPOLYLINE|UNKNOWN_)/;
1646      print $fh "  else" if $name ne '3DFACE'; # the first
1647      print $fh <<"EOF";
1648  if (obj->fixedtype == DWG_TYPE_$xname)
1649    error += test_$xname (obj);
1650EOF
1651    }
1652  }
1653
1654  if (m{/\* \@\@for test_SIZES\@\@ \*/}) {
1655    for my $name (@entity_names) {
1656      my $xname = $name =~ /^3/ ? "_$name" : $name; # 3DFACE, 3DSOLID
1657      print $fh <<"EOF";
1658  size1 = sizeof (Dwg_Entity_$xname);
1659  size2 = dwg_dynapi_fields_size (\"$name\");
1660  if (size1 != size2)
1661    {
1662      fprintf (stderr, "sizeof(Dwg_Entity_$xname): %d != "
1663               "dwg_dynapi_fields_size (\\\"$name\\\"): %d\\n", size1, size2);
1664      error++;
1665    }
1666EOF
1667      if ($name eq 'PROXY_LWPOLYLINE') {
1668        print $fh <<"EOF";
1669  if (size1 != size2) // TODO
1670    error--;
1671EOF
1672      }
1673    }
1674    for my $name (@object_names) {
1675      my $xname = $name;
1676      print $fh <<"EOF";
1677  size1 = sizeof (Dwg_Object_$xname);
1678  size2 = dwg_dynapi_fields_size (\"$name\");
1679  if (size1 != size2)
1680    {
1681      fprintf (stderr, "sizeof(Dwg_Object_$xname): %d != "
1682               "dwg_dynapi_fields_size (\\\"$name\\\"): %d\\n", size1, size2);
1683      error++;
1684    }
1685EOF
1686    }
1687    for my $name (@subclasses) {
1688      my $xname = $name;
1689      my $struct = "struct $name";
1690      if ($unions{$name}) {
1691        $struct = "union $name";
1692      }
1693      $xname =~ s/^_dwg_//;
1694      print $fh <<"EOF";
1695  size1 = sizeof ($struct);
1696  size2 = dwg_dynapi_fields_size (\"$xname\");
1697  if (size1 != size2)
1698    {
1699      fprintf (stderr, "sizeof($struct): %d != "
1700               "dwg_dynapi_fields_size (\\\"$xname\\\"): %d\\n", size1, size2);
1701      error++;
1702    }
1703EOF
1704    }
1705  }
1706}
1707close $in;
1708chmod 0444, $fh;
1709close $fh;
1710
1711sub mv_if_not_same {
1712  my ($tmp, $orig) = @_;
1713  if (`cmp "$tmp" "$orig"`) {
1714    system("mv", "-f", $orig, "$orig.bak");
1715    system("mv", "-f", $tmp, $orig);
1716    warn "new $orig\n";
1717  } else {
1718    unlink $tmp;
1719    warn "keep $orig\n";
1720  }
1721}
1722
1723# find DEBUGGING classes
1724my $classes_inc = "$srcdir/classes.inc";
1725my (%STABLE, %UNSTABLE, %DEBUGGING, %UNHANDLED, %FIXED, %STABLEVAR);
1726open $in, "<", $classes_inc or die "$classes_inc: $!";
1727while (<$in>) {
1728  if (/^\s*STABLE_CLASS(?:_DXF|_CPP|)\s*\(ACTION,\s+(.+?)[,\)]/) {
1729      $STABLE{$1}++;
1730  }
1731  elsif (/^\s*UNSTABLE_CLASS(?:_DXF|_CPP|)\s*\(ACTION,\s+(.+?)[,\)]/) {
1732      $UNSTABLE{$1}++;
1733  }
1734  elsif (/^\s*DEBUGGING_CLASS(?:_DXF|_CPP|)\s*\(ACTION,\s+(.+?)[,\)]/) {
1735      $DEBUGGING{$1}++;
1736  }
1737  elsif (/^\s*UNHANDLED_CLASS(?:_DXF|_CPP|)\s+\(ACTION,\s+(\S+?)[,\)]/) {
1738      $UNHANDLED{$1}++;
1739  }
1740}
1741delete $UNHANDLED{PROXY_LWPOLYLINE};
1742close $in;
1743for (sort keys %UNHANDLED) {
1744  push @unhandled_names, $_ if !exists $entity_names{$_} and !exists $object_names{$_};
1745}
1746# many stable/fixed names are not in classes.inc
1747for (@entity_names) {
1748  if (!$STABLE{$_} && !$UNSTABLE{$_} && !$DEBUGGING{$_} && !$UNHANDLED{$_}) {
1749    $FIXED{$_}++;
1750    $STABLE{$_}++;
1751  }
1752}
1753for (@object_names) {
1754  if (!$STABLE{$_} && !$UNSTABLE{$_} && !$DEBUGGING{$_} && !$UNHANDLED{$_}) {
1755    $FIXED{$_}++;
1756    $STABLE{$_}++;
1757  }
1758}
1759$STABLE{_3DSOLID}++;
1760$STABLE{_3DFACE}++;
1761$FIXED{_3DSOLID}++;
1762$FIXED{'_3DFACE'}++;
1763for (keys %STABLE) {
1764  $STABLEVAR{$_}++ unless $FIXED{$_};
1765}
1766
1767sub stability {
1768  my $n = shift;
1769  return 'STABLE' if $STABLE{$n};
1770  return 'STABLE' if $FIXED{$n};
1771  return 'UNSTABLE' if $UNSTABLE{$n};
1772  return 'DEBUGGING' if $DEBUGGING{$n};
1773  return 'UNHANDLED' if $UNHANDLED{$n};
1774  die "no stability class for $n";
1775}
1776
1777# ---------------------------------------------------------------
1778# The gperf hash
1779my $ofile = "$srcdir/objects.in";
1780chmod 0644, $ofile if -e $ofile;
1781open $inc, ">", "$ofile.tmp" or die "$ofile: $!";
1782print $inc <<'EOF';
1783%{ // -*- mode: c -*-
1784/*****************************************************************************/
1785/*  LibreDWG - free implementation of the DWG file format                    */
1786/*                                                                           */
1787/*  Copyright (C) 2020 Free Software Foundation, Inc.                        */
1788/*                                                                           */
1789/*  This library is free software, licensed under the terms of the GNU       */
1790/*  General Public License as published by the Free Software Foundation,     */
1791/*  either version 3 of the License, or (at your option) any later version.  */
1792/*  You should have received a copy of the GNU General Public License        */
1793/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
1794/*****************************************************************************/
1795
1796/*
1797 * objects.c: define all our entity and object names as hashmap,
1798 *            generated via gperf from object.in,
1799 *            which is generated by gen-dynapi.pl
1800 *
1801 * written Reini Urban
1802 */
1803
1804#include <string.h>
1805#include "config.h"
1806#include "dwg.h"
1807#include "common.h"
1808#include "classes.h"
1809
1810// v3.1 changed len type from unsigned int to size_t (gperf d519d1a821511eaa22eae6d9019a548aea21e6)
1811#ifdef GPERF_VERSION
1812#  if GPERF_VERSION < 301
1813#    define SIZE_TYPE unsigned int
1814#  else
1815#    define SIZE_TYPE size_t
1816#  endif
1817#else
1818#  define SIZE_TYPE size_t
1819#endif
1820static const struct _dwg_dxfname * in_word_set (register const char *str, register SIZE_TYPE len);
1821
1822#define STABLE (unsigned)DWG_CLASS_STABLE
1823#define UNSTABLE (unsigned)DWG_CLASS_UNSTABLE
1824#define DEBUGGING (unsigned)DWG_CLASS_DEBUGGING
1825#define UNHANDLED (unsigned)DWG_CLASS_UNHANDLED
1826
1827%}
1828%7bit
1829%language=ANSI-C
1830%struct-type
1831%readonly-tables
1832%pic
1833
1834struct _dwg_dxfname {int name; const char *const dxfname; const Dwg_Object_Type type; const unsigned isent:1; const unsigned stability:4; };
1835
1836%%
1837# Entities
1838EOF
1839$n = 28;
1840for my $name (@entity_names) {
1841  my $xname = $name =~ /^3/ ? "_$name" : $name; # 3DFACE, 3DSOLID
1842  #next if $name eq 'DIMENSION_';
1843  #next if $name eq 'PROXY_LWPOLYLINE';
1844  my $dxfname = dxfname($name);
1845  printf $inc "%-${n}s %-${n}s  DWG_TYPE_%s,\t1,\t%s\n", "\"$name\",", "\"$dxfname\",",
1846    $xname, stability($xname);
1847}
1848print $inc "# Objects\n";
1849$n = 35;
1850for my $name (sort @object_names) {
1851  my $dxfname = dxfname($name);
1852  printf $inc "%-${n}s %-${n}s  DWG_TYPE_%s,\t0,\t%s\n", "\"$name\",", "\"$dxfname\",",
1853    $name, stability($name);
1854}
1855print $inc <<'EOF';
1856%%
1857
1858/* Find if an object name (our internal name, not anything used elsewhere)
1859   is defined, and return our fixed type, the public dxfname and if it's an entity. */
1860EXPORT int dwg_object_name (const char *const restrict name,
1861                            const char **restrict dxfname,
1862                            Dwg_Object_Type *restrict typep, int *restrict is_entp,
1863                            Dwg_Class_Stability *restrict stabilityp)
1864{
1865  const struct _dwg_dxfname* result;
1866  const size_t len = strlen (name);
1867  // only allow UPPERCASE 7-bit names
1868  if (strspn (name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_23") != len)
1869    return 0;
1870  result = in_word_set (name, len);
1871  if (result)
1872    {
1873      if (dxfname)
1874        *dxfname = result->dxfname;
1875      if (typep)
1876        *typep   = result->type;
1877      if (is_entp)
1878        *is_entp = result->isent;
1879      if (stabilityp)
1880        *stabilityp = result->stability;
1881      return 1;
1882    }
1883  return 0;
1884}
1885
1886/*
1887 * Local variables:
1888 *   c-file-style: "gnu"
1889 * End:
1890 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
1891 */
1892EOF
1893close $inc;
1894mv_if_not_same ("$ofile.tmp", $ofile);
1895chmod 0444, $ofile;
1896
1897sub out_classes {
1898  my ($fh, $names, $STABILITY, $tmpl) = @_;
1899  my $lname;
1900  for my $name (@$names) {
1901      if ($STABILITY->{$name}) {
1902        my $s = $tmpl;
1903        if ($name =~ /^3/) {
1904          $name =~ s/^3/_3/;
1905        }
1906        $s =~ s/\$name/$name/g;
1907        if ($s =~ /\$lname/) {
1908          $lname = lc $name;
1909          # skip typedefs of
1910          if ($lname =~ /^(xline|vertex_mesh|vertex_pface|region|body)$/) {
1911            next;
1912          }
1913          $lname =~ s/dimension_/dim_/;
1914          $lname =~ s/lwpolyline/lwpline/;
1915          $lname =~ s/multileader/mleader/;
1916          $lname =~ s/vertex_pface_face/vert_pface_face/;
1917          $s =~ s/\$lname/$lname/;
1918        }
1919        print $fh $s;
1920      }
1921    }
1922}
1923
1924# generate API's lists per stabilty
1925my $tmpl;
1926my $api_c = "$srcdir/dwg_api.c";
1927open $in, "<", $api_c or die "$api_c: $!";
1928open my $out, ">", "$api_c.tmp" or die "$api_c.tmp: $!";
1929my $gen = 0;
1930while (<$in>) {
1931  if (m/^\/\* Start auto-generated/) {
1932    print $out $_;
1933
1934    $tmpl = "dwg_get_OBJECT (ent_\$lname, \$name)\n";
1935    # out_classes ($out, \@entity_names, \%FIXED, $tmpl);
1936    print $out "/* untyped > 500 */\n";
1937    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
1938    print $out "/* unstable */\n";
1939    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
1940    print $out "#ifdef DEBUG_CLASSES\n";
1941    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
1942    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
1943    print $out "#endif\n\n";
1944
1945    $tmpl = "dwg_get_OBJECT (obj_\$lname, \$name)\n";
1946    out_classes ($out, \@object_names, \%FIXED, $tmpl);
1947    print $out "/* untyped > 500 */\n";
1948    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
1949    print $out "/* unstable */\n";
1950    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
1951    print $out "#ifdef DEBUG_CLASSES\n";
1952    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
1953    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
1954    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
1955    print $out "#endif\n";
1956
1957    print $out <<'EOF';
1958
1959/********************************************************************
1960 * Functions to return NULL-terminated array of all owned entities  *
1961 ********************************************************************/
1962
1963/**
1964 * \fn Dwg_Entity_ENTITY* dwg_getall_ENTITY(Dwg_Object_Ref *hdr)
1965 * \code Usage: Dwg_Entity_TEXT* texts = dwg_getall_TEXT(text,
1966 * dwg->header_vars.mspace_block); \endcode \param[in]    hdr Dwg_Object_Ref *
1967 * to a BLOCK_CONTROL obj \return       malloced NULL-terminated array
1968 *
1969 * Extracts all entities of this type from a block header (mspace or pspace),
1970 * and returns a malloced NULL-terminated array.
1971 */
1972//< \fn Dwg_Entity_TEXT* dwg_getall_TEXT (Dwg_Object_Ref *hdr)
1973EOF
1974
1975    $tmpl = "DWG_GETALL_ENTITY (\$name)\n";
1976    out_classes ($out, \@entity_names, \%STABLE, $tmpl);
1977    print $out "/* unstable */\n";
1978    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
1979    print $out "/* debugging */\n";
1980    out_classes ($out, \@entity_names, \%DEBUGGING, $tmpl);
1981    out_classes ($out, \@entity_names, \%UNHANDLED, "//".$tmpl);
1982
1983    print $out <<'EOF';
1984
1985/********************************************************************
1986 *     Functions to return NULL-terminated array of all objects     *
1987 ********************************************************************/
1988
1989/**
1990 * \fn Dwg_Object_OBJECT dwg_getall_OBJECT(Dwg_Data *dwg)
1991 * Extracts all objects of this type from a dwg, and returns a malloced
1992 * NULL-terminated array.
1993 */
1994
1995EOF
1996
1997    $tmpl = "DWG_GETALL_OBJECT (\$name)\n";
1998    out_classes ($out, \@object_names, \%STABLE, $tmpl);
1999    print $out "/* unstable */\n";
2000    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2001    print $out "#ifdef DEBUG_CLASSES\n";
2002    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2003    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2004    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2005    print $out "#endif\n";
2006
2007    print $out <<'EOF';
2008
2009/*******************************************************************
2010 *     Functions created from macro to cast dwg_object to entity     *
2011 *                 Usage :- dwg_object_to_ENTITY(),                  *
2012 *                where ENTITY can be LINE or CIRCLE                 *
2013 ********************************************************************/
2014
2015/**
2016 * \fn Dwg_Entity_ENTITY *dwg_object_to_ENTITY(Dwg_Object *obj)
2017 * cast a Dwg_Object to Entity
2018 */
2019/* fixed <500 */
2020EOF
2021
2022    $tmpl = "CAST_DWG_OBJECT_TO_ENTITY (\$name)\n";
2023    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2024    print $out "/* untyped > 500 */\n";
2025    $tmpl = "CAST_DWG_OBJECT_TO_ENTITY_BYNAME (\$name)\n";
2026    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2027    print $out "/* unstable */\n";
2028    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2029    print $out "#ifdef DEBUG_CLASSES\n";
2030    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2031    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2032    print $out "#endif\n";
2033
2034    print $out <<'EOF';
2035
2036/*******************************************************************
2037 *     Functions created from macro to cast dwg object to object     *
2038 *                 Usage :- dwg_object_to_OBJECT(),                  *
2039 *            where OBJECT can be LAYER or BLOCK_HEADER              *
2040 ********************************************************************/
2041/**
2042 * \fn Dwg_Object_OBJECT *dwg_object_to_OBJECT(Dwg_Object *obj)
2043 * cast a Dwg_Object to Object
2044 */
2045EOF
2046
2047    $tmpl = "CAST_DWG_OBJECT_TO_OBJECT (\$name)\n";
2048    out_classes ($out, \@object_names, \%STABLE, $tmpl);
2049    print $out "/* unstable */\n";
2050    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2051    print $out "#ifdef DEBUG_CLASSES\n";
2052    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2053    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2054    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2055    print $out "#endif\n";
2056    print $out "// clang-format: on\n";
2057    print $out "/* End auto-generated content */\n";
2058    $gen = 1;
2059  }
2060  if (!$gen) {
2061    print $out $_;
2062  }
2063  if (m/^\/\* End auto-generated/) {
2064    $gen = 0;
2065  }
2066}
2067close $in;
2068close $out;
2069mv_if_not_same ("$api_c.tmp", $api_c);
2070
2071my $api_h = "$topdir/include/dwg_api.h";
2072open $in, "<", $api_h or die "$api_h: $!";
2073open $out, ">", "$api_h.tmp" or die "$api_h.tmp: $!";
2074$gen = 0;
2075while (<$in>) {
2076  if (m/^\/\* Start auto-generated/) {
2077    print $out $_;
2078
2079    $tmpl = "typedef struct _dwg_entity_\$name\t\tdwg_ent_\$lname;\n";
2080    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2081    print $out "/* untyped > 500 */\n";
2082    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2083    print $out "/* unstable */\n";
2084    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2085    print $out "/* debugging */\n";
2086    out_classes ($out, \@entity_names, \%DEBUGGING, $tmpl);
2087    out_classes ($out, \@entity_names, \%UNHANDLED, "//".$tmpl);
2088
2089    $tmpl = "typedef struct _dwg_object_\$name\t\tdwg_obj_\$lname;\n";
2090    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2091    print $out "/* untyped > 500 */\n";
2092    # without UNDERLAYDEFINITION
2093    my %STABLEVAR1 = %STABLEVAR;
2094    delete %STABLEVAR1{qw(PDFDEFINITION DGNDEFINITION DWFDEFINITION)};
2095    out_classes ($out, \@object_names, \%STABLEVAR1, $tmpl);
2096    my %STABLEVAR2 = map {$_ => 1} qw(PDFDEFINITION DGNDEFINITION DWFDEFINITION);
2097    $tmpl = "typedef struct _dwg_abstractobject_UNDERLAYDEFINITION\t\tdwg_obj_\$lname;\n";
2098    out_classes ($out, \@object_names, \%STABLEVAR2, $tmpl);
2099    $tmpl = "typedef struct _dwg_object_\$name\t\tdwg_obj_\$lname;\n";
2100
2101    print $out "/* unstable */\n";
2102    # without ASSOCARRAYPARAMETERS
2103    my %UNSTABLE1 = %UNSTABLE;
2104    delete %UNSTABLE1{qw(ASSOCARRAYMODIFYPARAMETERS ASSOCARRAYPATHPARAMETERS
2105                         ASSOCARRAYPOLARPARAMETERS ASSOCARRAYRECTANGULARPARAMETERS)};
2106    out_classes ($out, \@object_names, \%UNSTABLE1, $tmpl);
2107    my %UNSTABLE2 = map {$_ => 1} qw(ASSOCARRAYMODIFYPARAMETERS ASSOCARRAYPATHPARAMETERS
2108                                       ASSOCARRAYPOLARPARAMETERS ASSOCARRAYRECTANGULARPARAMETERS);
2109    $tmpl = "typedef struct _dwg_abstractobject_ASSOCARRAYPARAMETERS\t\tdwg_obj_\$lname;\n";
2110    out_classes ($out, \@object_names, \%UNSTABLE2, $tmpl);
2111    #out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2112    print $out "/* debugging */\n";
2113    $tmpl = "typedef struct _dwg_object_\$name\t\tdwg_obj_\$lname;\n";
2114    out_classes ($out, \@object_names, \%DEBUGGING, $tmpl);
2115    out_classes ($out, \@object_names, \%UNHANDLED, "//".$tmpl);
2116    out_classes ($out, \@unhandled_names, \%UNHANDLED, "//".$tmpl);
2117    print $out "\n\n";
2118
2119    $tmpl = "dwg_get_OBJECT_DECL (ent_\$lname, \$name);\n";
2120    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2121    print $out "/* untyped > 500 */\n";
2122    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2123    print $out "/* unstable */\n";
2124    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2125    print $out "#ifdef DEBUG_CLASSES\n";
2126    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2127    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2128    print $out "#endif\n\n";
2129
2130    $tmpl = "dwg_get_OBJECT_DECL (obj_\$lname, \$name);\n";
2131    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2132    print $out "/* untyped > 500 */\n";
2133    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
2134    print $out "/* unstable */\n";
2135    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2136    print $out "#ifdef DEBUG_CLASSES\n";
2137    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2138    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2139    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2140    print $out "#endif\n";
2141
2142    print $out <<'EOF';
2143
2144/********************************************************************
2145 * Functions to return NULL-terminated array of all owned entities  *
2146 ********************************************************************/
2147
2148/// extract all owned entities from a block header (mspace or pspace)
2149EOF
2150
2151    $tmpl = "DWG_GETALL_ENTITY_DECL (\$name);\n";
2152    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2153    print $out "/* untyped > 500 */\n";
2154    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2155    print $out "/* unstable */\n";
2156    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2157    print $out "/* debugging */\n";
2158    out_classes ($out, \@entity_names, \%DEBUGGING, $tmpl);
2159    out_classes ($out, \@entity_names, \%UNHANDLED, "//".$tmpl);
2160
2161    print $out <<'EOF';
2162
2163/********************************************************************
2164 *     Functions to return NULL-terminated array of all objects     *
2165 ********************************************************************/
2166
2167/**
2168 * \fn Dwg_Object_OBJECT dwg_getall_OBJECT(Dwg_Data *dwg)
2169 * Extracts all objects of this type from a dwg, and returns a malloced
2170 * NULL-terminated array.
2171 */
2172
2173EOF
2174
2175    $tmpl = "DWG_GETALL_OBJECT_DECL (\$name);\n";
2176    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2177    print $out "/* untyped > 500 */\n";
2178    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
2179    print $out "/* unstable */\n";
2180    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2181    print $out "#ifdef DEBUG_CLASSES\n";
2182    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2183    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2184    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2185    print $out "#endif\n";
2186
2187    print $out <<'EOF';
2188
2189/*******************************************************************
2190 *     Functions created from macro to cast dwg_object to entity     *
2191 *                 Usage :- dwg_object_to_ENTITY(),                  *
2192 *                where ENTITY can be LINE or CIRCLE                 *
2193 ********************************************************************/
2194
2195/**
2196 * \fn Dwg_Entity_ENTITY *dwg_object_to_ENTITY(Dwg_Object *obj)
2197 * cast a Dwg_Object to Entity
2198 */
2199/* fixed <500 */
2200EOF
2201
2202    $tmpl = "CAST_DWG_OBJECT_TO_ENTITY_DECL (\$name);\n";
2203    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2204    print $out "/* untyped > 500 */\n";
2205    $tmpl = "CAST_DWG_OBJECT_TO_ENTITY_BYNAME_DECL (\$name);\n";
2206    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2207    print $out "/* unstable */\n";
2208    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2209    print $out "#ifdef DEBUG_CLASSES\n";
2210    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2211    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2212    print $out "#endif\n";
2213
2214    print $out <<'EOF';
2215
2216/*******************************************************************
2217 *     Functions created from macro to cast dwg object to object     *
2218 *                 Usage :- dwg_object_to_OBJECT(),                  *
2219 *            where OBJECT can be LAYER or BLOCK_HEADER              *
2220 ********************************************************************/
2221/**
2222 * \fn Dwg_Object_OBJECT *dwg_object_to_OBJECT(Dwg_Object *obj)
2223 * cast a Dwg_Object to Object
2224 */
2225EOF
2226
2227    $tmpl = "CAST_DWG_OBJECT_TO_OBJECT_DECL (\$name);\n";
2228    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2229    print $out "/* untyped > 500 */\n";
2230    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
2231    print $out "/* unstable */\n";
2232    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2233    print $out "#ifdef DEBUG_CLASSES\n";
2234    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2235    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2236    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2237    print $out "#endif\n";
2238
2239    print $out "/* End auto-generated content */\n";
2240    $gen = 1;
2241  }
2242  if (!$gen) {
2243    print $out $_;
2244  }
2245  if (m/^\/\* End auto-generated/) {
2246    $gen = 0;
2247  }
2248}
2249close $in;
2250close $out;
2251mv_if_not_same ("$api_h.tmp", $api_h);
2252
2253my $dwg_h = "$topdir/include/dwg.h";
2254open $in, "<", $dwg_h or die "$dwg_h: $!";
2255open $out, ">", "$dwg_h.tmp" or die "$dwg_h.tmp: $!";
2256$gen = 0;
2257my $enum = 0;
2258my (@VARTYPES, %VARTYPES);
2259while (<$in>) {
2260  # generated sorted DWG_TYPE_ enum as array and hash.
2261  # from PROXY_OBJECT to FREED
2262  if ($enum and /^\s+DWG_TYPE_([^\t ,]+)/) {
2263    my $n = $1;
2264    if ($n =~ /^FREED\s?/) {
2265      $enum = 0;
2266      print $out $_; # because of the next shortcut
2267      next;
2268    }
2269    push @VARTYPES, $n;
2270    $VARTYPES{$n} = $enum++;
2271  }
2272  if (/^\s+DWG_TYPE_PROXY_OBJECT = 0x1f3/) {
2273    $enum = 500;
2274  }
2275  if (/^} Dwg_Object_Type/) {
2276    $enum = 0;
2277  }
2278  if (m/    \/\* Start auto-generated entity-union/) {
2279    print $out $_;
2280    $tmpl = "    Dwg_Entity_\$name *\$name;\n";
2281    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2282    print $out "    /* untyped > 500 */\n";
2283    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2284    print $out "    /* unstable */\n";
2285    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2286    print $out "    /* debugging */\n";
2287    #print $out "#ifdef DEBUG_CLASSES\n";
2288    out_classes ($out, \@entity_names, \%DEBUGGING, $tmpl);
2289    out_classes ($out, \@entity_names, \%UNHANDLED, "//".$tmpl);
2290    #print $out "#endif\n";
2291    print $out "    /* End auto-generated entity-union */\n";
2292    $gen = 1;
2293  }
2294  elsif (m/    \/\* Start auto-generated object-union/) {
2295    print $out $_;
2296    $tmpl = "    Dwg_Object_\$name *\$name;\n";
2297    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2298    print $out "    /* untyped > 500 */\n";
2299    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
2300    print $out "    /* unstable */\n";
2301    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2302    print $out "    /* debugging */\n";
2303    out_classes ($out, \@object_names, \%DEBUGGING, $tmpl);
2304    out_classes ($out, \@object_names, \%UNHANDLED, "//".$tmpl);
2305    out_classes ($out, \@unhandled_names, \%UNHANDLED, "//".$tmpl);
2306    print $out "    /* End auto-generated object-union */\n";
2307    $gen = 1;
2308  }
2309  elsif (m/^\/\* Start auto-generated content/) {
2310    print $out $_;
2311
2312    $tmpl = "EXPORT int dwg_setup_\$name (Dwg_Object *obj);\n";
2313    out_classes ($out, \@entity_names, \%FIXED, $tmpl);
2314    out_classes ($out, \@object_names, \%FIXED, $tmpl);
2315    print $out "/* untyped > 500 */\n";
2316    out_classes ($out, \@entity_names, \%STABLEVAR, $tmpl);
2317    out_classes ($out, \@object_names, \%STABLEVAR, $tmpl);
2318    print $out "/* unstable */\n";
2319    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2320    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2321    print $out "#ifdef DEBUG_CLASSES\n";
2322    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2323    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2324    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2325    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2326    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2327    print $out "#endif\n";
2328
2329    print $out "/* End auto-generated content */\n";
2330    $gen = 1;
2331  }
2332  if (!$gen) {
2333    print $out $_;
2334  }
2335  if (m/^\s*\/\* End auto-generated/) {
2336    $gen = 0;
2337  }
2338}
2339close $in;
2340close $out;
2341mv_if_not_same ("$dwg_h.tmp", $dwg_h);
2342
2343if (0) {
2344my $free_h = "$topdir/src/free.h";
2345open $in, "<", $free_h or die "$free_h: $!";
2346open $out, ">", "$free_h.tmp" or die "$free_h.tmp: $!";
2347$gen = 0;
2348while (<$in>) {
2349  if (m/^\/\* Start auto-generated content/) {
2350    print $out $_;
2351
2352    $tmpl = "int dwg_free_\$name (Bit_Chain *restrict dat, Dwg_Object *restrict obj);\n" .
2353      "int dwg_free_\$name_private (Bit_Chain *dat, Bit_Chain *hdl_dat, Bit_Chain *str_dat, Dwg_Object *restrict obj);\n";
2354    out_classes ($out, \@entity_names, \%STABLE, $tmpl);
2355    out_classes ($out, \@object_names, \%STABLE, $tmpl);
2356    print $out "/* unstable */\n";
2357    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2358    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2359    print $out "/* DEBUG_CLASSES */\n";
2360    my $dbgtmpl = $tmpl;
2361    $dbgtmpl =~ s/^int dwg_free/  int dwg_free/gmaa;
2362    out_classes ($out, \@entity_names, \%DEBUGGING, $dbgtmpl);
2363    out_classes ($out, \@object_names, \%DEBUGGING, $dbgtmpl);
2364    my $unhtmpl = $tmpl;
2365    $unhtmpl =~ s{^int dwg_free}{//  int dwg_free}gmaa;
2366    out_classes ($out, \@entity_names, \%UNHANDLED, $unhtmpl);
2367    out_classes ($out, \@object_names, \%UNHANDLED, $unhtmpl);
2368    out_classes ($out, \@unhandled_names, \%UNHANDLED, $unhtmpl);
2369    print $out "/* End auto-generated content */\n";
2370    $gen = 1;
2371  }
2372  if (!$gen) {
2373    print $out $_;
2374  }
2375  if (m/^\s*\/\* End auto-generated/) {
2376    $gen = 0;
2377  }
2378}
2379close $in;
2380close $out;
2381mv_if_not_same ("$free_h.tmp", $free_h);
2382}
2383
2384if (1) {
2385  my $file = "$topdir/src/classes.c";
2386  open $in, "<", $file or die "$file: $!";
2387  open $out, ">", "$file.tmp" or die "$file.tmp: $!";
2388  $gen = 0;
2389  while (<$in>) {
2390    if (m/^\s+\/\* Start auto-generated variable/) {
2391      print $out $_;
2392      my $e = 500;
2393      for (@VARTYPES) {
2394        printf $out "    %-40s	/* %d */\n", "\"$_\",", $e++;
2395      }
2396      print $out "  /* End auto-generated variable */\n";
2397      $gen = 1;
2398    }
2399    if (m/^\s+\/\* Start auto-generated dxfnames/) {
2400      print $out $_;
2401      my $e = 500;
2402      for (@VARTYPES) {
2403        my $dxfname = dxfname $_;
2404        printf $out "    %-40s	/* %d */\n", "\"$dxfname\",", $e++;
2405      }
2406      print $out "  /* End auto-generated dxfnames */\n";
2407      $gen = 1;
2408    }
2409    if (!$gen) {
2410      print $out $_;
2411    }
2412    if (m/^\s*\/\* End auto-generated/) {
2413      $gen = 0;
2414    }
2415  }
2416  close $in;
2417  close $out;
2418  mv_if_not_same ("$file.tmp", $file);
2419}
2420
2421my $done = 0;
2422my $ifile = "$topdir/bindings/dwg.i";
2423open $in, "<", $ifile or die "$ifile: $!";
2424open $out, ">", "$ifile.tmp" or die "$ifile.tmp: $!";
2425while (<$in>) {
2426  if (m/^\/\* Start auto-generated/) {
2427    print $out $_;
2428    print $out "/* dwg_getall_ API */\n";
2429    $tmpl = "EXPORT Dwg_Entity_\$name** dwg_getall_\$name (Dwg_Object_Ref* hdr);\n";
2430    out_classes ($out, \@entity_names, \%STABLE, $tmpl);
2431    print $out "/* unstable */\n";
2432    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2433    print $out "#ifdef DEBUG_CLASSES\n";
2434    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2435    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2436    print $out "#endif\n";
2437    print $out "\n";
2438
2439    $tmpl = "EXPORT Dwg_Object_\$name** dwg_getall_\$name (Dwg_Data* dwg);\n";
2440    out_classes ($out, \@object_names, \%STABLE, $tmpl);
2441    print $out "/* unstable */\n";
2442    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2443    print $out "#ifdef DEBUG_CLASSES\n";
2444    out_classes ($out, \@object_names, \%DEBUGGING, $tmpl);
2445    out_classes ($out, \@object_names, \%UNHANDLED, "//".$tmpl);
2446    out_classes ($out, \@unhandled_names, \%UNHANDLED, "//".$tmpl);
2447    print $out "#endif\n";
2448
2449    print $out "\n/* dwg_object_to_ API */\n";
2450    $tmpl = "EXPORT Dwg_Entity_\$name* dwg_object_to_\$name (Dwg_Object* obj);\n";
2451    out_classes ($out, \@entity_names, \%STABLE, $tmpl);
2452    print $out "/* unstable */\n";
2453    out_classes ($out, \@entity_names, \%UNSTABLE, $tmpl);
2454    print $out "#ifdef DEBUG_CLASSES\n";
2455    out_classes ($out, \@entity_names, \%DEBUGGING, "  ".$tmpl);
2456    out_classes ($out, \@entity_names, \%UNHANDLED, "  //".$tmpl);
2457    print $out "#endif\n";
2458
2459    $tmpl = "EXPORT Dwg_Object_\$name* dwg_object_to_\$name (Dwg_Object* obj);\n";
2460    out_classes ($out, \@object_names, \%STABLE, $tmpl);
2461    print $out "/* unstable */\n";
2462    out_classes ($out, \@object_names, \%UNSTABLE, $tmpl);
2463    print $out "#ifdef DEBUG_CLASSES\n";
2464    out_classes ($out, \@object_names, \%DEBUGGING, "  ".$tmpl);
2465    out_classes ($out, \@object_names, \%UNHANDLED, "  //".$tmpl);
2466    out_classes ($out, \@unhandled_names, \%UNHANDLED, "  //".$tmpl);
2467    print $out "#endif\n";
2468    print $out "/* End auto-generated content */\n";
2469    close $out;
2470    $done++;
2471    last;
2472  }
2473  if (!$done) {
2474    print $out $_;
2475  }
2476}
2477close $in;
2478close $out;
2479mv_if_not_same ("$ifile.tmp", $ifile);
2480
2481# NOTE: in the 2 #line's below use __LINE__ + 1
2482__DATA__
2483/* ex: set ro ft=c: -*- mode: c; buffer-read-only: t -*- */
2484#line 2458 "gen-dynapi.pl"
2485/*****************************************************************************/
2486/*  LibreDWG - free implementation of the DWG file format                    */
2487/*                                                                           */
2488/*  Copyright (C) 2018-2020 Free Software Foundation, Inc.                   */
2489/*                                                                           */
2490/*  This library is free software, licensed under the terms of the GNU       */
2491/*  General Public License as published by the Free Software Foundation,     */
2492/*  either version 3 of the License, or (at your option) any later version.  */
2493/*  You should have received a copy of the GNU General Public License        */
2494/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
2495/*****************************************************************************/
2496
2497/*
2498 * dynapi.c: dynamic access to all object and field names and types
2499 * written by Reini Urban
2500 * generated by src/gen-dynapi.pl from include/dwg.h, do not modify.
2501 */
2502
2503#include "config.h"
2504#include <string.h>
2505#include <stdlib.h>
2506#include <assert.h>
2507#include "common.h"
2508#include "dynapi.h"
2509#define DWG_LOGLEVEL loglevel
2510#include "logging.h"
2511#include "decode.h"
2512#include "dwg.h"
2513#include "bits.h"
2514
2515#ifndef _DWG_API_H_
2516Dwg_Object *dwg_obj_generic_to_object (const void *restrict obj,
2517                                       int *restrict error);
2518#endif
2519
2520@@struct _dwg_header_variables@@
2521@@for dwg_entity_ENTITY@@
2522@@for dwg_object_OBJECT@@
2523@@for dwg_subclasses@@
2524
2525/* common fields: */
2526@@struct _dwg_object_entity@@
2527@@struct _dwg_object_object@@
2528
2529@@struct _dwg_summaryinfo@@
2530
2531/* FIXME: Remove name. Get type via dwg_object_name() */
2532struct _name_type_fields {
2533  const char *const name;
2534  const enum DWG_OBJECT_TYPE type;
2535  const Dwg_DYNAPI_field *const fields;
2536  const int size;
2537};
2538
2539struct _name_subclass_fields {
2540  const char *const name;
2541  const int type;
2542  const char *const subclass;
2543  const Dwg_DYNAPI_field *const fields;
2544  const int size;
2545};
2546
2547/* Generated fields for all the objects, sorted for bsearch. from enum DWG_OBJECT_TYPE.
2548   FIXME: Replace name by type. Get type via dwg_object_name().
2549   Make it an array of type for O(1) lookup.
2550 */
2551static const struct _name_type_fields dwg_name_types[] = {
2552@@enum DWG_OBJECT_TYPE@@
2553};
2554
2555/* Generated fields for all the subclasses, sorted for bsearch */
2556static const struct _name_subclass_fields dwg_list_subclasses[] = {
2557@@list subclasses@@
2558};
2559
2560struct _name_subclasses {
2561  const char *const name;
2562  const char *const subclasses[@@scalar max_subclasses@@];
2563};
2564
2565/* List of all allowed subclasses per class. sorted for bsearch. */
2566static const struct _name_subclasses dwg_name_subclasses[] = {
2567@@list name_subclasses@@
2568};
2569
2570#line 2544 "gen-dynapi.pl"
2571struct _name
2572{
2573  const char *const name;
2574};
2575
2576static int
2577_name_struct_cmp (const void *restrict key, const void *restrict elem)
2578{
2579  //https://en.cppreference.com/w/c/algorithm/bsearch
2580  const struct _name *f = (struct _name *)elem;
2581  return strcmp ((const char *)key, f->name); //deref
2582}
2583
2584#define NUM_NAME_TYPES  ARRAY_SIZE(dwg_name_types)
2585#define NUM_SUBCLASSES  ARRAY_SIZE(dwg_list_subclasses)
2586
2587static
2588const struct _name_type_fields*
2589 __nonnull ((1))
2590// FIXME: use type arg only
2591_find_entity (const char *name)
2592{
2593  const char *p = (const char *)bsearch (name, dwg_name_types, NUM_NAME_TYPES,
2594                           sizeof (dwg_name_types[0]),
2595                           _name_struct_cmp);
2596  if (p)
2597    {
2598      const int i = (p - (char *)dwg_name_types) / sizeof (dwg_name_types[0]);
2599      return &dwg_name_types[i];
2600    }
2601  else
2602    return NULL;
2603}
2604
2605static
2606const struct _name_subclass_fields*
2607 __nonnull ((1))
2608_find_subclass (const char *name)
2609{
2610  const char *p = (const char *)bsearch (name, dwg_list_subclasses, NUM_SUBCLASSES,
2611                           sizeof (dwg_list_subclasses[0]),
2612                           _name_struct_cmp);
2613  if (p)
2614    {
2615      const int i = (p - (char *)dwg_list_subclasses) / sizeof (dwg_list_subclasses[0]);
2616      return &dwg_list_subclasses[i];
2617    }
2618  else
2619    return NULL;
2620}
2621
2622EXPORT bool
2623is_dwg_entity (const char *name)
2624{
2625  int isent;
2626  return dwg_object_name (name, NULL, NULL, &isent, NULL)
2627         && isent;
2628}
2629
2630EXPORT bool
2631is_dwg_object (const char *name)
2632{
2633  int isent;
2634  return dwg_object_name (name, NULL, NULL, &isent, NULL)
2635         && !isent;
2636}
2637
2638EXPORT const Dwg_DYNAPI_field *
2639dwg_dynapi_entity_fields (const char *name)
2640{
2641  const struct _name_type_fields *f = _find_entity (name);
2642  return f ? f->fields : NULL;
2643}
2644
2645EXPORT const Dwg_DYNAPI_field *
2646dwg_dynapi_subclass_fields (const char *restrict name)
2647{
2648  const struct _name_subclass_fields *f = _find_subclass (name);
2649  return f ? f->fields : NULL;
2650}
2651
2652EXPORT const Dwg_DYNAPI_field *
2653dwg_dynapi_common_entity_fields (void)
2654{
2655  return _dwg_object_entity_fields;
2656}
2657
2658EXPORT const Dwg_DYNAPI_field *
2659dwg_dynapi_common_object_fields (void)
2660{
2661  return _dwg_object_object_fields;
2662}
2663
2664EXPORT const Dwg_DYNAPI_field *
2665dwg_dynapi_entity_field (const char *restrict name, const char *restrict field)
2666{
2667  const Dwg_DYNAPI_field *fields = dwg_dynapi_entity_fields (name);
2668  if (fields)
2669    { /* linear search (unsorted) */
2670      Dwg_DYNAPI_field *f = (Dwg_DYNAPI_field *)fields;
2671      for (; f->name; f++)
2672        {
2673          if (strEQ (f->name, field))
2674            return f;
2675        }
2676    }
2677  return NULL;
2678}
2679
2680EXPORT const Dwg_DYNAPI_field *
2681dwg_dynapi_subclass_field (const char *restrict name, const char *restrict field)
2682{
2683  const Dwg_DYNAPI_field *fields = dwg_dynapi_subclass_fields (name);
2684  if (fields)
2685    { /* linear search (unsorted) */
2686      Dwg_DYNAPI_field *f = (Dwg_DYNAPI_field *)fields;
2687      for (; f->name; f++)
2688        {
2689          if (strEQ (f->name, field))
2690            return f;
2691        }
2692    }
2693  return NULL;
2694}
2695
2696EXPORT const Dwg_DYNAPI_field *
2697dwg_dynapi_header_field (const char *restrict fieldname)
2698{
2699  return (Dwg_DYNAPI_field *)bsearch (
2700              fieldname, _dwg_header_variables_fields,
2701              ARRAY_SIZE (_dwg_header_variables_fields) - 1, /* NULL terminated */
2702              sizeof (_dwg_header_variables_fields[0]), _name_struct_cmp);
2703}
2704
2705EXPORT const Dwg_DYNAPI_field *
2706dwg_dynapi_common_entity_field (const char *restrict fieldname)
2707{
2708  return (Dwg_DYNAPI_field *)bsearch (
2709              fieldname, _dwg_object_entity_fields,
2710              ARRAY_SIZE (_dwg_object_entity_fields) - 1, /* NULL terminated */
2711              sizeof (_dwg_object_entity_fields[0]), _name_struct_cmp);
2712}
2713
2714EXPORT const Dwg_DYNAPI_field *
2715dwg_dynapi_common_object_field (const char *restrict fieldname)
2716{
2717  return (Dwg_DYNAPI_field *)bsearch (
2718              fieldname, _dwg_object_object_fields,
2719              ARRAY_SIZE (_dwg_object_object_fields) - 1, /* NULL terminated */
2720              sizeof (_dwg_object_object_fields[0]), _name_struct_cmp);
2721}
2722
2723// search field by dxf
2724EXPORT const Dwg_DYNAPI_field *
2725dwg_dynapi_field_dxf (const Dwg_DYNAPI_field *restrict fields, const int dxf, int *restrict unique)
2726{
2727  const Dwg_DYNAPI_field *retval = NULL;
2728  if (fields)
2729    { /* linear search (unsorted) */
2730      Dwg_DYNAPI_field *f = (Dwg_DYNAPI_field *)fields;
2731      *unique = 1;
2732      for (; f->name; f++)
2733        {
2734          if (f->dxf == dxf)
2735            {
2736              if (retval)
2737                unique = 0;
2738              else
2739                retval = f;
2740            }
2741        }
2742    }
2743  return retval;
2744}
2745
2746EXPORT int
2747dwg_dynapi_entity_size (const char *restrict name)
2748{
2749  const struct _name_type_fields *f = _find_entity (name);
2750  return f ? f->size : 0;
2751}
2752
2753EXPORT int
2754dwg_dynapi_subclass_size (const char *restrict name)
2755{
2756  const struct _name_subclass_fields *f = _find_subclass (name);
2757  return f ? f->size : 0;
2758}
2759
2760/* generic field getters */
2761EXPORT bool
2762dwg_dynapi_entity_value (void *restrict _obj, const char *restrict name,
2763                         const char *restrict fieldname,
2764                         void *restrict out, Dwg_DYNAPI_field *restrict fp)
2765{
2766#ifndef HAVE_NONNULL
2767  if (!_obj || !name || !fieldname || !out)
2768    return false;
2769#endif
2770  {
2771    int error;
2772    const Dwg_Object* obj = dwg_obj_generic_to_object (_obj, &error);
2773    // Here we need to ignore errors, because we allow subentities via
2774    // CHK_SUBCLASS_* e.g. layout->plotsetting via PLOTSETTING
2775    if (obj && strNE (obj->name, name)) // objid may be 0
2776      {
2777        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL;
2778        LOG_ERROR ("%s: Invalid entity type %s, wanted %s", __FUNCTION__,
2779                   obj->name, name);
2780        return false;
2781      }
2782    {
2783      const Dwg_DYNAPI_field *f = dwg_dynapi_entity_field (name, fieldname);
2784      if (!f)
2785        {
2786          int loglevel = (obj && obj->parent) ? obj->parent->opts & DWG_OPTS_LOGLEVEL
2787                                              : DWG_LOGLEVEL_ERROR;
2788          LOG_ERROR ("%s: Invalid %s field %s", __FUNCTION__, name, fieldname);
2789          return false;
2790        }
2791      if (fp)
2792        memcpy (fp, f, sizeof (Dwg_DYNAPI_field));
2793      memcpy (out, &((char *)_obj)[f->offset], f->is_malloc ? sizeof(char*) : f->size);
2794      return true;
2795    }
2796  }
2797}
2798
2799EXPORT bool
2800dwg_dynapi_entity_utf8text (void *restrict _obj, const char *restrict name,
2801                            const char *restrict fieldname,
2802                            char **restrict out, int *isnew,
2803                            Dwg_DYNAPI_field *restrict fp)
2804{
2805  if (isnew)
2806    *isnew = 0;
2807#ifndef HAVE_NONNULL
2808  if (!_obj || !name || !fieldname || !out)
2809    return false;
2810#endif
2811  {
2812    int error;
2813    const Dwg_Object* obj = dwg_obj_generic_to_object (_obj, &error);
2814    // Here we need to ignore errors, because we allow subentities via
2815    // CHK_SUBCLASS_* e.g. layout->plotsetting via PLOTSETTING
2816    if (obj && strNE (obj->name, name)) // objid may be 0
2817      {
2818        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL;
2819        LOG_ERROR ("%s: Invalid entity type %s, wanted %s", __FUNCTION__,
2820                   obj->name, name);
2821        return false;
2822      }
2823    {
2824      const Dwg_DYNAPI_field *f = dwg_dynapi_entity_field (name, fieldname);
2825      const Dwg_Data *dwg = obj ? obj->parent : NULL;
2826      const bool is_tu = dwg ? IS_FROM_TU_DWG (dwg) : false;
2827
2828      if (!f || !f->is_string)
2829        {
2830          int loglevel = dwg ? dwg->opts & DWG_OPTS_LOGLEVEL : DWG_LOGLEVEL_ERROR;
2831          LOG_ERROR ("%s: Invalid %s text field %s", __FUNCTION__, name, fieldname);
2832          return false;
2833        }
2834      if (fp)
2835        memcpy (fp, f, sizeof (Dwg_DYNAPI_field));
2836
2837      if (is_tu && strNE (f->type, "TF")) /* not TF */
2838        {
2839          BITCODE_TU wstr = *(BITCODE_TU*)((char*)_obj + f->offset);
2840          char *utf8 = bit_convert_TU (wstr);
2841          if (wstr && !utf8) // some conversion error, invalid wchar (nyi)
2842            return false;
2843          *out = utf8;
2844          if (isnew)
2845            *isnew = 1;
2846        }
2847      else
2848        {
2849          char *utf8 = *(char **)((char*)_obj + f->offset);
2850          *out = utf8;
2851        }
2852
2853      return true;
2854    }
2855  }
2856}
2857
2858EXPORT bool
2859dwg_dynapi_header_value (const Dwg_Data *restrict dwg,
2860                         const char *restrict fieldname, void *restrict out,
2861                         Dwg_DYNAPI_field *restrict fp)
2862{
2863#ifndef HAVE_NONNULL
2864  if (!dwg || !fieldname || !out)
2865    return false;
2866#endif
2867  {
2868    const Dwg_DYNAPI_field *f = dwg_dynapi_header_field (fieldname);
2869    if (f)
2870      {
2871        const Dwg_Header_Variables *const _obj = &dwg->header_vars;
2872        if (fp)
2873          memcpy (fp, f, sizeof (Dwg_DYNAPI_field));
2874        memcpy (out, &((char*)_obj)[f->offset], f->size);
2875        return true;
2876      }
2877    else
2878      {
2879        const int loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
2880        LOG_ERROR ("%s: Invalid header field %s", __FUNCTION__, fieldname);
2881        return false;
2882      }
2883  }
2884}
2885
2886EXPORT bool
2887dwg_dynapi_header_utf8text (const Dwg_Data *restrict dwg,
2888                            const char *restrict fieldname,
2889                            char **restrict out, int *isnew,
2890                            Dwg_DYNAPI_field *restrict fp)
2891{
2892  if (isnew)
2893    *isnew = 0;
2894#ifndef HAVE_NONNULL
2895  if (!dwg || !fieldname || !out)
2896    return false;
2897#endif
2898  {
2899    const Dwg_DYNAPI_field *f = dwg_dynapi_header_field (fieldname);
2900    if (f && f->is_string)
2901      {
2902        const Dwg_Header_Variables *const _obj = &dwg->header_vars;
2903        const bool is_tu = IS_FROM_TU_DWG (dwg);
2904
2905        if (fp)
2906          memcpy (fp, f, sizeof (Dwg_DYNAPI_field));
2907
2908        if (is_tu && strNE (f->type, "TF")) /* not TF */
2909          {
2910            BITCODE_TU wstr = *(BITCODE_TU*)((char*)_obj + f->offset);
2911            char *utf8 = bit_convert_TU (wstr);
2912            if (wstr && !utf8) // some conversion error, invalid wchar (nyi)
2913              return false;
2914            *out = utf8;
2915            if (isnew)
2916              *isnew = 1;
2917          }
2918        else
2919          {
2920            char *utf8 = *(char **)((char*)_obj + f->offset);
2921            *out = utf8;
2922          }
2923
2924        return true;
2925      }
2926    else
2927      {
2928        const int loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
2929        LOG_ERROR ("%s: Invalid header text field %s", __FUNCTION__, fieldname);
2930        return false;
2931      }
2932  }
2933}
2934
2935EXPORT bool
2936dwg_dynapi_common_value(void *restrict _obj, const char *restrict fieldname,
2937                        void *restrict out, Dwg_DYNAPI_field *restrict fp)
2938{
2939#ifndef HAVE_NONNULL
2940  if (!_obj || !fieldname || !out)
2941    return false;
2942#endif
2943  {
2944    const Dwg_DYNAPI_field *f;
2945    int error;
2946    const Dwg_Object *obj = dwg_obj_generic_to_object (_obj, &error);
2947    if (!obj || error)
2948      {
2949        const int loglevel = DWG_LOGLEVEL_ERROR;
2950        LOG_ERROR ("%s: dwg_obj_generic_to_object failed", __FUNCTION__);
2951        return false;
2952      }
2953
2954    if (obj->supertype == DWG_SUPERTYPE_ENTITY)
2955      {
2956        f = dwg_dynapi_common_entity_field (fieldname);
2957        _obj = obj->tio.entity;
2958      }
2959    else if (obj->supertype == DWG_SUPERTYPE_OBJECT)
2960      {
2961        f = dwg_dynapi_common_object_field (fieldname);
2962        _obj = obj->tio.object;
2963      }
2964    else
2965      {
2966        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL; // DWG_LOGLEVEL_ERROR;
2967        LOG_ERROR ("%s: Unhandled %s.supertype ", __FUNCTION__, obj->name);
2968        return false;
2969      }
2970
2971    if (f)
2972      {
2973        int size = f->size;
2974        if (fp)
2975          memcpy (fp, f, sizeof(Dwg_DYNAPI_field));
2976        if (f->dxf == 160 && strEQc (fieldname, "preview_size")
2977            && obj->parent->header.version < R_2010)
2978          size = 4;
2979        memcpy (out, &((char *)_obj)[f->offset], size);
2980        return true;
2981      }
2982    else
2983      {
2984        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL;
2985        LOG_ERROR ("%s: Invalid common field %s", __FUNCTION__, fieldname);
2986        return false;
2987      }
2988  }
2989}
2990
2991EXPORT bool
2992dwg_dynapi_common_utf8text(void *restrict _obj, const char *restrict fieldname,
2993                           char **restrict out, int *isnew, Dwg_DYNAPI_field *restrict fp)
2994{
2995  if (isnew)
2996    *isnew = 0;
2997#ifndef HAVE_NONNULL
2998  if (!_obj || !fieldname || !out)
2999    return false;
3000#endif
3001  {
3002    Dwg_DYNAPI_field *f;
3003    int error;
3004    const Dwg_Object *obj = dwg_obj_generic_to_object (_obj, &error);
3005    Dwg_Data *dwg = NULL;
3006
3007    if (!obj || error)
3008      {
3009        const int loglevel = DWG_LOGLEVEL_ERROR;
3010        LOG_ERROR ("%s: dwg_obj_generic_to_object failed", __FUNCTION__);
3011        return false;
3012      }
3013    if (obj->supertype == DWG_SUPERTYPE_ENTITY)
3014      {
3015        dwg = obj ? obj->parent : ((Dwg_Entity_UNKNOWN_ENT *)_obj)->parent->dwg;
3016        _obj = obj->tio.entity;
3017        f = (Dwg_DYNAPI_field *)bsearch (
3018            fieldname, _dwg_object_entity_fields,
3019            ARRAY_SIZE (_dwg_object_entity_fields) - 1, /* NULL terminated */
3020            sizeof (_dwg_object_entity_fields[0]), _name_struct_cmp);
3021      }
3022    else if (obj->supertype == DWG_SUPERTYPE_OBJECT)
3023      {
3024        dwg = obj ? obj->parent : ((Dwg_Object_UNKNOWN_OBJ *)_obj)->parent->dwg;
3025        _obj = obj->tio.object;
3026        f = (Dwg_DYNAPI_field *)bsearch (
3027            fieldname, _dwg_object_object_fields,
3028            ARRAY_SIZE (_dwg_object_object_fields) - 1, /* NULL terminated */
3029            sizeof (_dwg_object_object_fields[0]), _name_struct_cmp);
3030      }
3031    else
3032      {
3033        const int loglevel = DWG_LOGLEVEL_ERROR;
3034        LOG_ERROR ("%s: Unhandled %s.supertype ", __FUNCTION__, obj->name);
3035        return false;
3036      }
3037
3038    if (f && f->is_string)
3039      {
3040        const bool is_tu = IS_FROM_TU_DWG (dwg);
3041
3042        if (fp)
3043          memcpy (fp, f, sizeof(Dwg_DYNAPI_field));
3044
3045        if (is_tu && strNE (f->type, "TF")) /* not TF */
3046          {
3047            BITCODE_TU wstr = *(BITCODE_TU*)((char*)_obj + f->offset);
3048            char *utf8 = bit_convert_TU (wstr);
3049            if (wstr && !utf8) // some conversion error, invalid wchar (nyi)
3050              return false;
3051            *out = utf8;
3052            if (isnew)
3053              *isnew = 1;
3054          }
3055        else
3056          {
3057            char *utf8 = *(char **)((char*)_obj + f->offset);
3058            *out = utf8;
3059          }
3060
3061        return true;
3062      }
3063    else
3064      {
3065        const int loglevel = dwg ? dwg->opts & DWG_OPTS_LOGLEVEL : DWG_LOGLEVEL_ERROR;
3066        LOG_ERROR ("%s: Invalid common text field %s", __FUNCTION__, fieldname);
3067        return false;
3068      }
3069  }
3070}
3071
3072// create a fresh string
3073static void
3074dynapi_set_helper (void *restrict old, const Dwg_DYNAPI_field *restrict f,
3075                   const Dwg_Version_Type dwg_version,
3076                   const void *restrict value, const bool is_utf8)
3077{
3078  // TODO: sanity checks. is_malloc (TF)
3079  // if text strcpy or wcscpy, or do utf8 conversion.
3080  //if ((char*)old && f->is_malloc)
3081  //  free (old);
3082  if (f->is_malloc)
3083    {
3084      // NULL ptr
3085      if (!*(char**)value)
3086        memcpy (old, value, f->size);
3087      // ascii
3088      else if (strEQc (f->type, "TF") || (f->is_string && dwg_version < R_2007))
3089        {
3090          char *str = (char *)malloc (strlen (*(char**)value)+1);
3091          strcpy (str, *(char**)value);
3092          memcpy (old, &str, sizeof (char*)); // size of ptr
3093        }
3094      // or wide
3095      else if (strNE (f->type, "TF") && (f->is_string && dwg_version >= R_2007))
3096        {
3097          BITCODE_TU wstr;
3098          if (is_utf8)
3099            wstr = bit_utf8_to_TU (*(char **)value, 0);
3100          else // source is already TU
3101            {
3102#if defined(HAVE_WCHAR_H) && defined(SIZEOF_WCHAR_T) && SIZEOF_WCHAR_T == 2
3103              wstr = (BITCODE_TU)malloc (2 * (wcslen (*(wchar_t **)value) + 1));
3104              wcscpy ((wchar_t *)wstr, *(wchar_t **)value);
3105#else
3106              int length = 0;
3107              for (; (*(BITCODE_TU*)value)[length]; length++)
3108                ;
3109              length++;
3110              wstr = (BITCODE_TU)malloc (2 * length);
3111              memcpy (wstr, value, length * 2);
3112#endif
3113            }
3114          memcpy (old, &wstr, sizeof (char*)); // size of ptr
3115        }
3116      else
3117        memcpy (old, value, sizeof (char*));
3118    }
3119  else
3120    memcpy (old, value, f->size);
3121}
3122
3123/* generic field setters */
3124EXPORT bool
3125dwg_dynapi_entity_set_value (void *restrict _obj, const char *restrict name,
3126                             const char *restrict fieldname,
3127                             const void *restrict value, const bool is_utf8)
3128{
3129#ifndef HAVE_NONNULL
3130  if (!_obj || !fieldname || !value) // cannot set NULL value
3131    return false;
3132#endif
3133  {
3134    int error;
3135    const Dwg_Object *obj = dwg_obj_generic_to_object (_obj, &error);
3136    if (error)
3137      {
3138        const int loglevel = DWG_LOGLEVEL_ERROR;
3139        LOG_ERROR ("%s: dwg_obj_generic_to_object failed", __FUNCTION__);
3140        return false;
3141      }
3142    if (obj && strNE (obj->name, name))
3143      {
3144        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL;
3145        LOG_ERROR ("%s: Invalid entity type %s, wanted %s", __FUNCTION__,
3146                   obj->name, name);
3147        return false;
3148      }
3149    {
3150      void *old;
3151      const Dwg_DYNAPI_field *f = dwg_dynapi_entity_field (name, fieldname);
3152      const Dwg_Data *dwg
3153        = obj ? obj->parent
3154              : ((Dwg_Object_UNKNOWN_OBJ *)_obj)->parent->dwg;
3155      const Dwg_Version_Type dwg_version = dwg ? dwg->header.from_version : R_INVALID;
3156
3157      if (!f)
3158        {
3159          const int loglevel = dwg ? dwg->opts & DWG_OPTS_LOGLEVEL : 0;
3160          LOG_ERROR ("%s: Invalid %s field %s", __FUNCTION__, name, fieldname);
3161          return false;
3162        }
3163
3164      old = &((char*)_obj)[f->offset];
3165      dynapi_set_helper (old, f, dwg_version, value, is_utf8);
3166      return true;
3167    }
3168  }
3169}
3170
3171EXPORT bool
3172dwg_dynapi_header_set_value (Dwg_Data *restrict dwg,
3173                             const char *restrict fieldname,
3174                             const void *restrict value, const bool is_utf8)
3175{
3176#ifndef HAVE_NONNULL
3177  if (!dwg || !fieldname || !value) // cannot set NULL value
3178    return false;
3179#endif
3180  {
3181    Dwg_DYNAPI_field *f = (Dwg_DYNAPI_field *)bsearch (
3182        fieldname, _dwg_header_variables_fields,
3183        ARRAY_SIZE (_dwg_header_variables_fields) - 1, /* NULL terminated */
3184        sizeof (_dwg_header_variables_fields[0]), _name_struct_cmp);
3185    if (f)
3186      {
3187        void *old;
3188        // there are no malloc'd fields in the HEADER, so no need to free().
3189        const Dwg_Header_Variables *const _obj = &dwg->header_vars;
3190
3191        old = &((char*)_obj)[f->offset];
3192        dynapi_set_helper (old, f, dwg->header.version, value, is_utf8);
3193
3194        // Set also FLAGS
3195        if (strEQc (fieldname, "CELWEIGHT"))
3196          {
3197            dwg->header_vars.FLAGS &= ~0x1f; // delete old, and set new
3198            dwg->header_vars.FLAGS |= dxf_revcvt_lweight (dwg->header_vars.CELWEIGHT);
3199          }
3200#define SET_HDR_FLAGS(name, bit, inverse)          \
3201        else if (strEQc (fieldname, #name))        \
3202          {                                        \
3203            if (dwg->header_vars.name && !inverse) \
3204              dwg->header_vars.FLAGS |= bit;       \
3205            else                                   \
3206              dwg->header_vars.FLAGS &= ~bit;      \
3207          }
3208        SET_HDR_FLAGS (ENDCAPS, 0x60, 0)
3209        SET_HDR_FLAGS (JOINSTYLE, 0x180, 0)
3210        SET_HDR_FLAGS (LWDISPLAY, 0x200, 1)
3211        SET_HDR_FLAGS (XEDIT, 0x400, 1)
3212        SET_HDR_FLAGS (EXTNAMES, 0x800, 0)
3213        SET_HDR_FLAGS (PSTYLEMODE, 0x2000, 0)
3214        SET_HDR_FLAGS (OLESTARTUP, 0x4000, 0)
3215
3216        return true;
3217      }
3218    else
3219      {
3220        const int loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
3221        LOG_ERROR ("%s: Invalid header field %s", __FUNCTION__, fieldname);
3222        return false;
3223      }
3224  }
3225}
3226
3227EXPORT bool
3228dwg_dynapi_common_set_value (void *restrict _obj,
3229                             const char *restrict fieldname,
3230                             const void *restrict value, const bool is_utf8)
3231{
3232#ifndef HAVE_NONNULL
3233  if (!_obj || !fieldname || !value)
3234    return false;
3235#endif
3236  {
3237    Dwg_DYNAPI_field *f;
3238    int error;
3239    void *old;
3240    const Dwg_Object *obj = dwg_obj_generic_to_object (_obj, &error);
3241    Dwg_Data *dwg;
3242    if (!obj || error)
3243      {
3244        const int loglevel = DWG_LOGLEVEL_ERROR;
3245        LOG_ERROR ("%s: dwg_obj_generic_to_object failed", __FUNCTION__);
3246        return false;
3247      }
3248    dwg = obj->parent;
3249    if (obj->supertype == DWG_SUPERTYPE_ENTITY)
3250      {
3251        _obj = obj->tio.entity;
3252        f = (Dwg_DYNAPI_field *)bsearch (
3253            fieldname, _dwg_object_entity_fields,
3254            ARRAY_SIZE (_dwg_object_entity_fields) - 1, /* NULL terminated */
3255            sizeof (_dwg_object_entity_fields[0]), _name_struct_cmp);
3256      }
3257    else if (obj->supertype == DWG_SUPERTYPE_OBJECT)
3258      {
3259        _obj = obj->tio.object;
3260        f = (Dwg_DYNAPI_field *)bsearch (
3261            fieldname, _dwg_object_object_fields,
3262            ARRAY_SIZE (_dwg_object_object_fields) - 1, /* NULL terminated */
3263            sizeof (_dwg_object_object_fields[0]), _name_struct_cmp);
3264      }
3265    else
3266      {
3267        const int loglevel = DWG_LOGLEVEL_ERROR;
3268        LOG_ERROR ("%s: Unhandled %s.supertype ", __FUNCTION__, obj->name);
3269        return false;
3270      }
3271
3272    if (!f)
3273      {
3274        const int loglevel = obj->parent->opts & DWG_OPTS_LOGLEVEL;
3275        LOG_ERROR ("%s: Invalid %s common field %s", __FUNCTION__, obj->name, fieldname);
3276        return false;
3277      }
3278
3279    old = &((char*)_obj)[f->offset];
3280    if (f->dxf == 160 && strEQc (fieldname, "preview_size"))
3281      {
3282        int size = f->size;
3283        if (dwg && dwg->header.version < R_2010)
3284          size = 4;
3285        memcpy (old, value, size);
3286      }
3287    else
3288      dynapi_set_helper (old, f, dwg ? dwg->header.version : R_INVALID, value, is_utf8);
3289
3290    if (dwg && obj->supertype == DWG_SUPERTYPE_ENTITY && strEQc (fieldname, "ltype"))
3291      { // set also isbylayerlt and ltype_flags
3292        BITCODE_H ltype = *(BITCODE_H*)value;
3293        Dwg_Object_Entity *ent = obj->tio.entity;
3294        if (!dwg->header_vars.LTYPE_BYLAYER || !ent->ltype)
3295          ;
3296        else if (ent->ltype->absolute_ref == dwg->header_vars.LTYPE_BYLAYER->absolute_ref)
3297          {
3298            ent->isbylayerlt = 1; // r13-r14 only
3299            ent->ltype_flags = 0;
3300          }
3301        else if (dwg->header_vars.LTYPE_BYBLOCK
3302                 && ent->ltype->absolute_ref == dwg->header_vars.LTYPE_BYBLOCK->absolute_ref)
3303          {
3304            ent->isbylayerlt = 0;
3305            ent->ltype_flags = 1;
3306          }
3307        else if (dwg->header_vars.LTYPE_CONTINUOUS
3308                 && ent->ltype->absolute_ref == dwg->header_vars.LTYPE_CONTINUOUS->absolute_ref)
3309          {
3310            ent->isbylayerlt = 0;
3311            ent->ltype_flags = 2;
3312          }
3313        else
3314          {
3315            ent->isbylayerlt = 0;
3316            ent->ltype_flags = 3;
3317          }
3318      }
3319    return true;
3320  }
3321}
3322
3323// arbitrary structs, no text
3324EXPORT bool
3325dwg_dynapi_subclass_value (const void *restrict ptr,
3326                           const char *restrict subclass,
3327                           const char *restrict fieldname,
3328                           void *restrict out, Dwg_DYNAPI_field *restrict fp)
3329{
3330  const Dwg_DYNAPI_field *f;
3331#ifndef HAVE_NONNULL
3332  if (!ptr || !subclass || !fieldname || !out)
3333    return false;
3334#endif
3335  f = dwg_dynapi_subclass_field (subclass, fieldname);
3336  if (!f) // TODO maybe search via dwg_dynapi_subclass_name ()
3337    return false;
3338  memcpy (out, &((char*)ptr)[f->offset], f->size);
3339  if (fp)
3340    memcpy (fp, f, sizeof(Dwg_DYNAPI_field));
3341  return true;
3342}
3343
3344// arbitrary structs, no text
3345EXPORT bool
3346dwg_dynapi_field_get_value (const void *restrict ptr,
3347                            const Dwg_DYNAPI_field *restrict field,
3348                            void *restrict out)
3349{
3350#ifndef HAVE_NONNULL
3351  if (!ptr || !field || !out)
3352    return false;
3353#endif
3354  memcpy (out, &((char*)ptr)[field->offset], field->size);
3355  return true;
3356}
3357
3358// can do arbitrary structs, like subclasses
3359EXPORT bool
3360dwg_dynapi_field_set_value (const Dwg_Data *restrict dwg, /* only needed if unicode strings */
3361                            void *restrict ptr,
3362                            const Dwg_DYNAPI_field *restrict field,
3363                            const void *restrict value,
3364                            const bool is_utf8)
3365{
3366  void *off;
3367#ifndef HAVE_NONNULL
3368  if (!ptr || !field || !value)
3369    return false;
3370#endif
3371  off = &((char*)ptr)[field->offset];
3372  dynapi_set_helper (off, field, dwg ? dwg->header.version : R_INVALID, value, is_utf8);
3373  return true;
3374}
3375
3376// check if the handle points to an object with a name.
3377// see also dwg_obj_table_get_name, which only supports tables.
3378EXPORT char*
3379dwg_dynapi_handle_name (const Dwg_Data *restrict dwg, Dwg_Object_Ref *restrict hdl)
3380{
3381  const bool is_tu = IS_FROM_TU_DWG (dwg);
3382  Dwg_Object *obj;
3383
3384#ifndef HAVE_NONNULL
3385  if (!dwg || !hdl)
3386    return NULL;
3387#endif
3388
3389  obj = dwg_ref_object_silent (dwg, hdl);
3390  if (!obj)
3391    return NULL;
3392  {
3393    const Dwg_DYNAPI_field *f = dwg_dynapi_entity_field (obj->name, "name");
3394    // just some random type is enough.
3395    Dwg_Object_STYLE *_obj = obj->tio.object->tio.STYLE;
3396
3397    if (!f || !f->is_string)
3398      return NULL;
3399    if (is_tu && strNE (f->type, "TF")) /* not TF */
3400      {
3401        BITCODE_TU wstr = *(BITCODE_TU *)((char *)_obj + f->offset);
3402        return bit_convert_TU (wstr);
3403      }
3404    else
3405      {
3406        return *(char **)((char *)_obj + f->offset);
3407      }
3408  }
3409}
3410
3411// The sum of the size of all fields
3412int
3413_fields_size_sum (const Dwg_DYNAPI_field *restrict fields)
3414{
3415  Dwg_DYNAPI_field *f = (Dwg_DYNAPI_field *)fields;
3416  int sum = 0;
3417  if (!f)
3418    return 0;
3419  for (; f->name; f++)
3420    {
3421      sum += f->size;
3422    }
3423  return sum;
3424}
3425
3426// The size of the entity or subclass struct, or the sum of the size of all fields.
3427EXPORT int
3428dwg_dynapi_fields_size (const char *restrict name)
3429{
3430  const struct _name_type_fields *f;
3431#ifndef HAVE_NONNULL
3432  if (!name)
3433    return 0;
3434#endif
3435
3436  f = _find_entity (name);
3437  // TODO PROXY_LWPOLYLINE
3438  if (f)
3439    {
3440      if (f->size)
3441        return (int)f->size;
3442      else
3443        return _fields_size_sum (f->fields); // VERTEX_PFACE is not entity nor object yet
3444    }
3445  else
3446    {
3447      int size = dwg_dynapi_subclass_size (name);
3448      if (size)
3449        return size;
3450      else
3451        return _fields_size_sum (dwg_dynapi_subclass_fields (name));
3452    }
3453}
3454
3455// Converts from the fields type, like "Dwg_MLINESTYLE_line*" to the
3456// subclass name, like "MLINESTYLE_line".
3457ATTRIBUTE_MALLOC char*
3458dwg_dynapi_subclass_name (const char *restrict type)
3459{
3460  char *name = NULL;
3461  int len = strlen (type);
3462  if (memBEGINc (type, "Dwg_Object_"))
3463    {
3464      const int off = strlen ("Dwg_Object_"); // PLOTSETTINGS
3465      name = strdup (&type[off]);
3466      if (type[len - 1] == '*')
3467        name[len - off - 1] = '\0';
3468    }
3469  else if (memBEGINc (type, "Dwg_Entity_"))
3470    {
3471      const int off = strlen ("Dwg_Entity_");
3472      name = strdup (&type[off]);
3473      if (type[len - 1] == '*')
3474        name[len - off - 1] = '\0';
3475    }
3476  else if (memBEGINc (type, "Dwg_"))
3477    {
3478      name = strdup (&type[4]);
3479      if (type[len - 1] == '*')
3480        name[len - 5] = '\0';
3481    }
3482  else if (memBEGINc (type, "struct _dwg_entity_"))
3483    {
3484      const int off = strlen ("struct _dwg_entity_"); // TABLE
3485      name = strdup (&type[off]);
3486      if (type[len - 1] == '*')
3487        name[len - off - 1] = '\0';
3488    }
3489  else if (memBEGINc (type, "struct _dwg_object_"))
3490    {
3491      const int off = strlen ("struct _dwg_object_"); // CELLSTYLEMAP*, EVALUATION_GRAPH, ...
3492      name = strdup (&type[off]);
3493      if (type[len - 1] == '*')
3494        name[len - off - 1] = '\0';
3495    }
3496  else if (memBEGINc (type, "struct _dwg_")) // CellStyle*
3497    {
3498      const int off = strlen ("struct _dwg_");
3499      name = strdup (&type[off]);
3500      if (type[len - 1] == '*')
3501        name[len - off - 1] = '\0';
3502    }
3503  return name;
3504}
3505
3506EXPORT bool
3507dwg_has_subclass (const char *restrict classname, const char *restrict subclass)
3508{
3509  struct _name_subclasses *f;
3510#ifndef HAVE_NONNULL
3511  if (!classname || !name)
3512    return false;
3513#endif
3514  f = (struct _name_subclasses *)bsearch (classname, dwg_name_subclasses,
3515                                          ARRAY_SIZE (dwg_name_subclasses),
3516                                          sizeof (dwg_name_subclasses[0]),
3517                                          _name_struct_cmp);
3518  if (f) {
3519    for (unsigned i = 0; i < @@scalar max_subclasses@@ /* max_subclasses */; i++) {
3520      if (!f->subclasses[i]) // already at NULL
3521        return false;
3522      if (strEQ (subclass, f->subclasses[i]))
3523        return true;
3524    }
3525  }
3526  return false;
3527}
3528/* Local Variables: */
3529/* mode: c */
3530/* End: */
3531