1 /*****************************************************************************/
2 /*  LibreDWG - free implementation of the DWG file format                    */
3 /*                                                                           */
4 /*  Copyright (C) 2009-2010,2018-2021 Free Software Foundation, Inc.         */
5 /*                                                                           */
6 /*  This library is free software, licensed under the terms of the GNU       */
7 /*  General Public License as published by the Free Software Foundation,     */
8 /*  either version 3 of the License, or (at your option) any later version.  */
9 /*  You should have received a copy of the GNU General Public License        */
10 /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
11 /*****************************************************************************/
12 
13 /*
14  * dwg.c: main functions and API
15  * written by Felipe Castro
16  * modified by Felipe Corrêa da Silva Sances
17  * modified by Rodrigo Rodrigues da Silva
18  * modified by Anderson Pierre Cardoso
19  * modified by Reini Urban
20  */
21 
22 #include "config.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdbool.h>
27 #include <sys/stat.h>
28 #include <assert.h>
29 // strings.h or string.h
30 #ifdef AX_STRCASECMP_HEADER
31 #  include AX_STRCASECMP_HEADER
32 #endif
33 #include <libgen.h> // basename
34 
35 #include "bits.h"
36 #include "common.h"
37 #include "decode.h"
38 #include "dwg.h"
39 #include "hash.h"
40 #include "dynapi.h"
41 #ifdef USE_WRITE
42 #  include "encode.h"
43 #endif
44 #include "free.h"
45 
46 /* The logging level per .o */
47 static unsigned int loglevel;
48 #ifdef USE_TRACING
49 /* This flag means we have checked the environment variable
50    LIBREDWG_TRACE and set `loglevel' appropriately.  */
51 static bool env_var_checked_p;
52 #endif /* USE_TRACING */
53 #define DWG_LOGLEVEL loglevel
54 #include "logging.h"
55 
56 /*------------------------------------------------------------------------------
57  * Imports bypassing its headers.
58  * We dont want to import in_dxf.h as it is redefining FORMAT_BD.
59  */
60 #ifndef DISABLE_DXF
61 EXPORT int dwg_read_dxf (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
62 EXPORT int dwg_read_dxfb (Bit_Chain *restrict dat, Dwg_Data *restrict dwg);
63 #endif
64 
65 /*------------------------------------------------------------------------------
66  * Internal functions
67  */
68 // used in in_dxf.c, encode.c
69 BITCODE_H
70 dwg_find_tablehandle_silent (Dwg_Data *restrict dwg, const char *restrict name,
71                              const char *restrict table);
72 // used in encode.c
73 void set_handle_size (Dwg_Handle *restrict hdl);
74 
75 /*------------------------------------------------------------------------------
76  * Public functions
77  */
78 
79 EXPORT int
dat_read_file(Bit_Chain * restrict dat,FILE * restrict fp,const char * restrict filename)80 dat_read_file (Bit_Chain *restrict dat, FILE *restrict fp,
81                const char *restrict filename)
82 {
83   size_t size;
84   if (!dat->size && fp)
85     {
86       struct stat attrib;
87       int fd = fileno (fp);
88       if (fd >= 0 && !fstat (fd, &attrib))
89         dat->size = attrib.st_size;
90     }
91   dat->chain = (unsigned char *)calloc (1, dat->size + 1);
92   if (!dat->chain)
93     {
94       loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
95       LOG_ERROR ("Not enough memory.\n")
96       fclose (fp);
97       return DWG_ERR_OUTOFMEM;
98     }
99 
100   size = fread (dat->chain, sizeof (char), dat->size, fp);
101   if (size != dat->size)
102     {
103       loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
104       LOG_ERROR ("Could not read file (%lu out of %lu): %s\n",
105                  (long unsigned int)size, dat->size, filename)
106       fclose (fp);
107       free (dat->chain);
108       dat->chain = NULL;
109       dat->size = 0;
110       return DWG_ERR_IOERROR;
111     }
112   dat->chain[dat->size] = '\0';  // ensure zero-termination for strstr, strtol, ...
113   return 0;
114 }
115 
116 // fast bulk-read when we known the size
117 EXPORT int
dat_read_size(Bit_Chain * restrict dat)118 dat_read_size (Bit_Chain *restrict dat)
119 {
120   if (!dat->chain)
121     dat->chain = (unsigned char *)calloc (1, dat->size + 2);
122   else
123     dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 2);
124   if (!dat->chain)
125     {
126       loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
127       LOG_ERROR ("Not enough memory");
128       fclose (dat->fh);
129       return DWG_ERR_OUTOFMEM;
130     }
131   if (fread (dat->chain, 1, dat->size, dat->fh) != dat->size)
132     {
133       fclose (dat->fh);
134       free (dat->chain);
135       dat->chain = NULL;
136       return DWG_ERR_IOERROR;
137     }
138   dat->chain[dat->size] = '\0';  // ensure zero-termination
139   return 0;
140 }
141 
142 EXPORT int
dat_read_stream(Bit_Chain * restrict dat,FILE * restrict fp)143 dat_read_stream (Bit_Chain *restrict dat, FILE *restrict fp)
144 {
145   size_t size = 0;
146   loglevel = dat->opts & DWG_OPTS_LOGLEVEL;
147 
148   do
149     {
150       if (dat->chain)
151         dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 4096);
152       else
153         {
154           dat->chain = (unsigned char *)calloc (1, 4096);
155           dat->size = 0;
156         }
157       if (!dat->chain)
158         {
159           LOG_ERROR ("Not enough memory.\n");
160           fclose (fp);
161           return DWG_ERR_OUTOFMEM;
162         }
163       size = fread (&dat->chain[dat->size], sizeof (char), 4096, fp);
164       dat->size += size;
165     }
166   while (size == 4096);
167 
168   if (dat->size == 0)
169     {
170       LOG_ERROR ("Could not read from stream (%lu out of %lu)\n",
171                  (long unsigned int)size, dat->size);
172       fclose (fp);
173       free (dat->chain);
174       dat->chain = NULL;
175       return DWG_ERR_IOERROR;
176     }
177 
178   // clear the slack and realloc
179   size = dat->size & 0xfff;
180   if (size)
181     memset (&dat->chain[dat->size], 0, 0xfff - size);
182   dat->chain = (unsigned char *)realloc (dat->chain, dat->size + 1);
183   // ensure NULL termination, for sscanf, strtol and friends.
184   dat->chain[dat->size] = '\0';
185   return 0;
186 }
187 
188 /** dwg_read_file
189  * returns 0 on success.
190  *
191  * everything in dwg is cleared
192  * and then either read from dat, or set to a default.
193  */
194 EXPORT int
dwg_read_file(const char * restrict filename,Dwg_Data * restrict dwg)195 dwg_read_file (const char *restrict filename, Dwg_Data *restrict dwg)
196 {
197   FILE *fp;
198   struct stat attrib;
199   size_t size;
200   Bit_Chain bit_chain = { 0 };
201   int error;
202 
203   loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
204   memset (dwg, 0, sizeof (Dwg_Data));
205   dwg->opts = loglevel;
206 
207   if (strEQc (filename, "-"))
208     {
209       fp = stdin;
210     }
211   else
212     {
213       if (stat (filename, &attrib))
214         {
215           LOG_ERROR ("File not found: %s\n", filename);
216           return DWG_ERR_IOERROR;
217         }
218       if (!(S_ISREG (attrib.st_mode)
219 #ifndef _WIN32
220             || S_ISLNK (attrib.st_mode)
221 #endif
222             ))
223         {
224           LOG_ERROR ("Illegal input file %s\n", filename);
225           return DWG_ERR_IOERROR;
226         }
227       fp = fopen (filename, "rb");
228     }
229   if (!fp)
230     {
231       LOG_ERROR ("Could not open file: %s\n", filename)
232       return DWG_ERR_IOERROR;
233     }
234 
235   /* Load whole file into memory, even if streamed (for now)
236    */
237   memset (&bit_chain, 0, sizeof (Bit_Chain));
238   if (fp == stdin)
239     {
240       error = dat_read_stream (&bit_chain, fp);
241       if (error >= DWG_ERR_CRITICAL)
242         return error;
243     }
244   else
245     {
246       bit_chain.size = attrib.st_size;
247       error = dat_read_file (&bit_chain, fp, filename);
248       if (error >= DWG_ERR_CRITICAL)
249         return error;
250     }
251   fclose (fp);
252 
253   /* Decode the dwg structure */
254   error = dwg_decode (&bit_chain, dwg);
255   if (error >= DWG_ERR_CRITICAL)
256     {
257       LOG_ERROR ("Failed to decode file: %s 0x%x\n", filename, error)
258       free (bit_chain.chain);
259       bit_chain.chain = NULL;
260       bit_chain.size = 0;
261       return error;
262     }
263 
264   // TODO: does dwg hold any char* pointers to the bit_chain or are they all
265   // copied?
266   free (bit_chain.chain);
267   bit_chain.chain = NULL;
268   bit_chain.size = 0;
269 
270   return error;
271 }
272 
273 #if !defined(DISABLE_DXF) && defined(USE_WRITE)
274 /** dxf_read_file
275  * returns 0 on success.
276  *
277  * detects binary or ascii file.
278  * everything in dwg is cleared
279  * and then either read from dat, or set to a default.
280  */
281 EXPORT int
dxf_read_file(const char * restrict filename,Dwg_Data * restrict dwg)282 dxf_read_file (const char *restrict filename, Dwg_Data *restrict dwg)
283 {
284   int error;
285   FILE *fp;
286   struct stat attrib;
287   size_t size;
288   Bit_Chain dat = { 0 };
289   Dwg_Version_Type version;
290 
291   loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
292 
293   if (!filename || stat (filename, &attrib))
294     {
295       LOG_ERROR ("File not found: %s\n", filename ? filename : "(null)")
296       return DWG_ERR_IOERROR;
297     }
298   if (!(S_ISREG (attrib.st_mode)
299 #  ifndef _WIN32
300         || S_ISLNK (attrib.st_mode)
301 #  endif
302             ))
303     {
304       LOG_ERROR ("Error: %s\n", filename)
305       return DWG_ERR_IOERROR;
306     }
307   fp = fopen (filename, "rb");
308   if (!fp)
309     {
310       LOG_ERROR ("Could not open file: %s\n", filename)
311       return DWG_ERR_IOERROR;
312     }
313 
314   /* Load whole file into memory
315    */
316   version = dwg->header.version;
317   memset (dwg, 0, sizeof (Dwg_Data));
318   dwg->opts = loglevel | DWG_OPTS_INDXF;
319   dwg->header.version = version;
320 
321   memset (&dat, 0, sizeof (Bit_Chain));
322   dat.size = attrib.st_size;
323   dat.chain = (unsigned char *)calloc (1, dat.size + 2);
324   if (!dat.chain)
325     {
326       LOG_ERROR ("Not enough memory.\n");
327       fclose (fp);
328       return DWG_ERR_OUTOFMEM;
329     }
330   dat.byte = 0;
331   dat.bit = 0;
332   dat.from_version = dwg->header.from_version;
333   dat.version = dwg->header.version;
334   dat.opts = dwg->opts;
335 
336   size = fread (dat.chain, sizeof (char), dat.size, fp);
337   fclose (fp);
338   if (size != dat.size)
339     {
340       LOG_ERROR ("Could not read the entire file (%lu out of %lu): %s\n",
341                  (long unsigned int)size, dat.size, filename)
342       free (dat.chain);
343       dat.chain = NULL;
344       dat.size = 0;
345       return DWG_ERR_IOERROR;
346     }
347   if (size < 256)
348     {
349       LOG_ERROR ("File %s too small, %lu byte.\n", filename,
350                  (long unsigned int)size)
351       return DWG_ERR_IOERROR;
352     }
353   // properly end the buffer for strtol()/... readers
354   if (dat.chain[size-1] != '\n')
355     {
356       dat.chain[size] = '\n';
357       dat.size++;
358     }
359   dat.chain[size] = '\0';
360 
361   /* Fail on DWG */
362   if (!memcmp (dat.chain, "AC10", 4) ||
363       !memcmp (dat.chain, "AC1.", 4) ||
364       !memcmp (dat.chain, "AC2.10", 4) ||
365       !memcmp (dat.chain, "MC0.0", 4))
366     {
367       LOG_ERROR ("This is a DWG, not a DXF file: %s\n", filename)
368       free (dat.chain);
369       dat.chain = NULL;
370       dat.size = 0;
371       return DWG_ERR_INVALIDDWG;
372     }
373   /* See if binary or ascii */
374   if (!memcmp (dat.chain, "AutoCAD Binary DXF",
375                sizeof ("AutoCAD Binary DXF") - 1))
376     {
377       dat.byte = 22;
378       error = dwg_read_dxfb (&dat, dwg);
379     }
380   else
381     error = dwg_read_dxf (&dat, dwg);
382 
383   dwg->opts |= (DWG_OPTS_INDXF | loglevel);
384   if (error >= DWG_ERR_CRITICAL)
385     {
386       LOG_ERROR ("Failed to decode DXF file: %s\n", filename)
387       free (dat.chain);
388       dat.chain = NULL;
389       dat.size = 0;
390       return error;
391     }
392 
393   // TODO: does dwg hold any char* pointers to the dat or are they all copied?
394   free (dat.chain);
395   dat.chain = NULL;
396   dat.size = 0;
397 
398   return 0;
399 }
400 #endif /* DISABLE_DXF */
401 
402 #ifdef USE_WRITE
403 /** Encode the DWG struct into dat (in memory).
404   * Needs 2x DWG heap, dwg + dat.
405   */
406 EXPORT int
dwg_write_file(const char * restrict filename,const Dwg_Data * restrict dwg)407 dwg_write_file (const char *restrict filename, const Dwg_Data *restrict dwg)
408 {
409   FILE *fh;
410   struct stat attrib;
411   Bit_Chain dat = { 0 };
412   int error;
413 
414   loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
415   assert (filename);
416   assert (dwg);
417   dat.opts = dwg->opts;
418   dat.version = (Dwg_Version_Type)dwg->header.version;
419   dat.from_version = (Dwg_Version_Type)dwg->header.from_version;
420 
421   // json HACK. no wide chars from JSON, because we just encode to R_2000
422   if (dwg->opts & (DWG_OPTS_INJSON | DWG_OPTS_INDXF))
423     dat.from_version = dat.version;
424 
425   if (dwg->header.version <= R_2000 && dwg->header.from_version > R_2000)
426     dwg_fixup_BLOCKS_entities ((Dwg_Data *)dwg);
427 
428   dat.size = 0;
429   error = dwg_encode ((Dwg_Data *)dwg, &dat);
430   if (error >= DWG_ERR_CRITICAL)
431     {
432       LOG_ERROR ("Failed to encode Dwg_Data\n");
433       /* In development we want to look at the corpses */
434 #ifdef IS_RELEASE
435       if (dat.size > 0)
436         {
437           free (dat.chain);
438           dat.chain = NULL;
439           dat.size = 0;
440         }
441       return error;
442 #endif
443     }
444 
445   // try opening the output file in write mode
446   if (!stat (filename, &attrib)
447 #ifdef _WIN32
448       && strNE (filename, "NUL")
449 #else
450       && strNE (filename, "/dev/null")
451 #endif
452       )
453     {
454       LOG_ERROR ("The file already exists. We won't overwrite it.")
455       return error | DWG_ERR_IOERROR;
456     }
457   fh = fopen (filename, "wb");
458   if (!fh || !dat.chain)
459     {
460       LOG_ERROR ("Failed to create the file: %s\n", filename)
461       return error | DWG_ERR_IOERROR;
462     }
463 
464   // Write the data into the file
465   if (fwrite (dat.chain, sizeof (char), dat.size, fh) != dat.size)
466     {
467       LOG_ERROR ("Failed to write data into the file: %s\n", filename)
468       fclose (fh);
469       free (dat.chain);
470       dat.chain = NULL;
471       dat.size = 0;
472       return error | DWG_ERR_IOERROR;
473     }
474   fclose (fh);
475 
476   if (dat.size > 0)
477     {
478       free (dat.chain);
479       dat.chain = NULL;
480       dat.size = 0;
481     }
482 
483   return error;
484 }
485 #endif /* USE_WRITE */
486 
487 /* THUMBNAIL IMAGE DATA (R13C3+).
488    Supports multiple preview pictures.
489    Currently 3 types: BMP, WMF and PNG. but returns only the size of the BMP.
490  */
491 EXPORT unsigned char *
dwg_bmp(const Dwg_Data * restrict dwg,BITCODE_RL * restrict size)492 dwg_bmp (const Dwg_Data *restrict dwg, BITCODE_RL *restrict size)
493 {
494   BITCODE_RC i, num_pictures, type;
495   int found;
496   BITCODE_RL header_size, address, osize;
497   Bit_Chain dat = { 0 };
498 
499   loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
500   *size = 0;
501   assert (dwg);
502   // copy the chain data. bit_* needs a full chain with opts and version
503   dat = *(Bit_Chain *)&dwg->thumbnail;
504   if (!dat.size || !dat.chain)
505     {
506       LOG_INFO ("no THUMBNAIL Image Data\n")
507       return NULL;
508     }
509   //dat.byte = 0; sentinel at 16
510   dat.bit = 0;
511   dat.opts = dwg->opts;
512   dat.from_version = dwg->header.from_version;
513   dat.version = dwg->header.version;
514   dat.fh = NULL;
515 
516 #ifdef USE_TRACING
517   /* Before starting, set the logging level, but only do so once.  */
518   if (!env_var_checked_p)
519     {
520       char *probe = getenv ("LIBREDWG_TRACE");
521       if (probe)
522         loglevel = atoi (probe);
523       env_var_checked_p = true;
524     }
525 #endif /* USE_TRACING */
526 
527   osize = bit_read_RL (&dat); /* overall size of all images */
528   LOG_TRACE ("overall size: " FORMAT_RL " [RL]\n", osize);
529   if (osize > (dat.size - 4))
530     {
531       LOG_ERROR ("Preview overflow > %ld", dat.size - 4);
532       return NULL;
533     }
534   num_pictures = bit_read_RC (&dat);
535   LOG_INFO ("num_pictures: %d [RC]\n", (int)num_pictures)
536 
537   found = 0;
538   header_size = 0;
539   for (i = 0; i < num_pictures; i++)
540     {
541       if (dat.byte > dat.size)
542         {
543           LOG_ERROR ("Preview overflow");
544           break;
545         }
546       type = bit_read_RC (&dat);
547       LOG_TRACE ("\t[%i] Code: %i [RC]\n", i, type)
548       address = bit_read_RL (&dat);
549       LOG_TRACE ("\t\tHeader data start: 0x%x [RL]\n", address)
550       if (type == 1)
551         {
552           header_size += bit_read_RL (&dat);
553           LOG_TRACE ("\t\tHeader data size: %i [RL]\n", header_size)
554         }
555       else if (type == 2 && found == 0)
556         {
557           *size = bit_read_RL (&dat);
558           found = 1;
559           LOG_INFO ("\t\tBMP size: %i [RL]\n", *size)
560           if (*size > (dat.size - 4))
561             {
562               LOG_ERROR ("BMP thumbnail overflow > %ld", dat.size - 4);
563               return NULL;
564             }
565         }
566       else if (type == 3)
567         {
568           osize = bit_read_RL (&dat);
569           LOG_INFO ("\t\tWMF size: %i [RL]\n", osize)
570         }
571       else if (type == 4) // type 4?
572         {
573           osize = bit_read_RL (&dat);
574           LOG_INFO ("\t\tPNG size: %i [RL]\n", osize)
575         }
576       else
577         {
578           osize = bit_read_RL (&dat);
579           LOG_TRACE ("\t\tSize of unknown type %i: %i [RL]\n", type, osize)
580         }
581     }
582   dat.byte += header_size;
583   if (*size)
584     LOG_TRACE ("BMP offset: %lu\n", dat.byte);
585   if (dat.byte > dat.size)
586     {
587       *size = 0;
588       LOG_ERROR ("Preview overflow");
589       return NULL;
590     }
591 
592   if (*size > 0)
593     return (dat.chain + dat.byte);
594   else
595     return NULL;
596 }
597 
598 EXPORT double
dwg_model_x_min(const Dwg_Data * dwg)599 dwg_model_x_min (const Dwg_Data *dwg)
600 {
601   assert (dwg);
602   return dwg->header_vars.EXTMIN.x;
603 }
604 
605 EXPORT double
dwg_model_x_max(const Dwg_Data * dwg)606 dwg_model_x_max (const Dwg_Data *dwg)
607 {
608   assert (dwg);
609   return dwg->header_vars.EXTMAX.x;
610 }
611 
612 EXPORT double
dwg_model_y_min(const Dwg_Data * dwg)613 dwg_model_y_min (const Dwg_Data *dwg)
614 {
615   assert (dwg);
616   return dwg->header_vars.EXTMIN.y;
617 }
618 
619 EXPORT double
dwg_model_y_max(const Dwg_Data * dwg)620 dwg_model_y_max (const Dwg_Data *dwg)
621 {
622   assert (dwg);
623   return dwg->header_vars.EXTMAX.y;
624 }
625 
626 EXPORT double
dwg_model_z_min(const Dwg_Data * dwg)627 dwg_model_z_min (const Dwg_Data *dwg)
628 {
629   assert (dwg);
630   return dwg->header_vars.EXTMIN.z;
631 }
632 
633 EXPORT double
dwg_model_z_max(const Dwg_Data * dwg)634 dwg_model_z_max (const Dwg_Data *dwg)
635 {
636   assert (dwg);
637   return dwg->header_vars.EXTMAX.z;
638 }
639 
640 EXPORT double
dwg_page_x_min(const Dwg_Data * dwg)641 dwg_page_x_min (const Dwg_Data *dwg)
642 {
643   assert (dwg);
644   return dwg->header_vars.EXTMIN.x;
645 }
646 
647 EXPORT double
dwg_page_x_max(const Dwg_Data * dwg)648 dwg_page_x_max (const Dwg_Data *dwg)
649 {
650   assert (dwg);
651   return dwg->header_vars.PEXTMAX.x;
652 }
653 
654 EXPORT double
dwg_page_y_min(const Dwg_Data * dwg)655 dwg_page_y_min (const Dwg_Data *dwg)
656 {
657   assert (dwg);
658   return dwg->header_vars.PEXTMIN.y;
659 }
660 
661 EXPORT double
dwg_page_y_max(const Dwg_Data * dwg)662 dwg_page_y_max (const Dwg_Data *dwg)
663 {
664   assert (dwg);
665   return dwg->header_vars.PEXTMAX.y;
666 }
667 
668 EXPORT unsigned int
dwg_get_layer_count(const Dwg_Data * dwg)669 dwg_get_layer_count (const Dwg_Data *dwg)
670 {
671   Dwg_Object *ctrl;
672   assert (dwg);
673   ctrl = dwg_get_first_object (dwg, DWG_TYPE_LAYER_CONTROL);
674   if (ctrl && ctrl->tio.object && ctrl->tio.object->tio.LAYER_CONTROL)
675     return ctrl->tio.object->tio.LAYER_CONTROL->num_entries;
676   else
677     return 0;
678 }
679 
680 EXPORT Dwg_Object_LAYER **
dwg_get_layers(const Dwg_Data * dwg)681 dwg_get_layers (const Dwg_Data *dwg)
682 {
683   unsigned int i;
684   unsigned int num_layers = dwg_get_layer_count (dwg);
685   Dwg_Object_LAYER_CONTROL *_ctrl;
686   Dwg_Object_LAYER **layers;
687   Dwg_Object *ctrl;
688 
689   assert (dwg);
690   ctrl = dwg_get_first_object (dwg, DWG_TYPE_LAYER_CONTROL);
691   if (!ctrl || !ctrl->tio.object || !ctrl->tio.object->tio.LAYER_CONTROL)
692     return NULL;
693   _ctrl = ctrl->tio.object->tio.LAYER_CONTROL;
694   assert (_ctrl);
695   layers = (Dwg_Object_LAYER **)calloc (num_layers, sizeof (Dwg_Object_LAYER *));
696   for (i = 0; i < num_layers; i++)
697     {
698       Dwg_Object *obj = dwg_ref_object (dwg, _ctrl->entries[i]);
699       if (obj && obj->fixedtype == DWG_TYPE_LAYER)
700         layers[i] = obj->tio.object->tio.LAYER;
701     }
702   return layers;
703 }
704 
705 BITCODE_BL
dwg_get_object_num_objects(const Dwg_Data * dwg)706 dwg_get_object_num_objects (const Dwg_Data *dwg)
707 {
708   assert (dwg);
709   return dwg->num_objects - dwg->num_entities;
710 }
711 
712 BITCODE_BL
dwg_get_num_objects(const Dwg_Data * dwg)713 dwg_get_num_objects (const Dwg_Data *dwg)
714 {
715   assert (dwg);
716   return dwg->num_objects;
717 }
718 
719 BITCODE_BL
dwg_get_num_entities(const Dwg_Data * dwg)720 dwg_get_num_entities (const Dwg_Data *dwg)
721 {
722   assert (dwg);
723   return dwg->num_entities;
724 }
725 
726 /** Returns a copy of all entities */
727 EXPORT Dwg_Object_Entity **
dwg_get_entities(const Dwg_Data * dwg)728 dwg_get_entities (const Dwg_Data *dwg)
729 {
730   BITCODE_BL i, ent_count = 0;
731   Dwg_Object_Entity **entities;
732 
733   assert (dwg);
734   entities = (Dwg_Object_Entity **)calloc (dwg_get_num_entities (dwg),
735                                            sizeof (Dwg_Object_Entity *));
736   for (i = 0; i < dwg->num_objects; i++)
737     {
738       if (dwg->object[i].supertype == DWG_SUPERTYPE_ENTITY)
739         {
740           entities[ent_count] = dwg->object[i].tio.entity;
741           ent_count++;
742           assert (ent_count < dwg->num_objects);
743         }
744     }
745   return entities;
746 }
747 
748 EXPORT Dwg_Object_LAYER *
dwg_get_entity_layer(const Dwg_Object_Entity * ent)749 dwg_get_entity_layer (const Dwg_Object_Entity *ent)
750 {
751   // TODO: empty means default layer 0
752   return ent->layer ? ent->layer->obj->tio.object->tio.LAYER : NULL;
753 }
754 
755 EXPORT Dwg_Object *
dwg_next_object(const Dwg_Object * obj)756 dwg_next_object (const Dwg_Object *obj)
757 {
758   Dwg_Data *dwg;
759   if (!obj)
760     return NULL;
761   dwg = obj->parent;
762   if ((obj->index + 1) > (dwg->num_objects - 1))
763     return NULL;
764   return &dwg->object[obj->index + 1];
765 }
766 
767 /**
768  * Find an object given its handle
769  */
770 EXPORT Dwg_Object *
dwg_ref_object(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref)771 dwg_ref_object (const Dwg_Data *restrict dwg, Dwg_Object_Ref *restrict ref)
772 {
773   if (!ref)
774     return NULL;
775   if (ref->obj && !dwg->dirty_refs)
776     return ref->obj;
777   // Without obj we don't get an absolute_ref from relative OFFSETOBJHANDLE
778   // handle types.
779   if ((ref->handleref.code < 6
780        && dwg_resolve_handleref (ref, NULL))
781       || ref->absolute_ref)
782     {
783       Dwg_Object *obj;
784       loglevel = dwg->opts & DWG_OPTS_LOGLEVEL;
785       obj = dwg_resolve_handle (dwg, ref->absolute_ref);
786       if (!dwg->dirty_refs && obj)
787         ref->obj = obj;
788       return obj;
789     }
790   else
791     return NULL;
792 }
793 
794 /**
795  * Find an object given its handle and relative base object.
796  * OFFSETOBJHANDLE, handleref.code > 6.
797  */
798 EXPORT Dwg_Object *
dwg_ref_object_relative(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref,const Dwg_Object * restrict obj)799 dwg_ref_object_relative (const Dwg_Data *restrict dwg,
800                          Dwg_Object_Ref *restrict ref,
801                          const Dwg_Object *restrict obj)
802 {
803   if (ref->obj && !dwg->dirty_refs)
804     return ref->obj;
805   if (dwg_resolve_handleref (ref, obj))
806     {
807       Dwg_Object *o = dwg_resolve_handle (dwg, ref->absolute_ref);
808       if (!dwg->dirty_refs && o)
809         ref->obj = o;
810       return o;
811     }
812   else
813     return NULL;
814 }
815 
816 /**
817  * Find a pointer to an object given it's absolute id (handle).
818  * TODO: Check and update each handleref obj cache.
819  * Note that absref 0 is illegal here, I think.
820  */
821 EXPORT Dwg_Object *
dwg_resolve_handle(const Dwg_Data * dwg,const unsigned long absref)822 dwg_resolve_handle (const Dwg_Data *dwg, const unsigned long absref)
823 {
824   uint32_t i;
825   if (!absref) // illegal usage
826     return NULL;
827   i = hash_get (dwg->object_map, (uint32_t)absref);
828   if (i != HASH_NOT_FOUND)
829     LOG_HANDLE ("object_map{%lX} => %u\n", absref, i);
830   if (i == HASH_NOT_FOUND
831       || (BITCODE_BL)i >= dwg->num_objects) // the latter being an invalid
832                                             // handle (read from DWG)
833     {
834       // ignore warning on invalid handles. These are warned earlier already
835       if (absref && absref < dwg->num_objects)
836         {
837           LOG_WARN ("Object handle not found, %lu/%lX in " FORMAT_BL
838                     " objects",
839                     absref, absref, dwg->num_objects);
840         }
841       return NULL;
842     }
843   return &dwg->object[i]; // allow value 0
844 }
845 
846 /**
847  * Silent variant of dwg_resolve_handle
848  */
849 EXPORT Dwg_Object *
dwg_resolve_handle_silent(const Dwg_Data * dwg,const BITCODE_BL absref)850 dwg_resolve_handle_silent (const Dwg_Data *dwg, const BITCODE_BL absref)
851 {
852   uint32_t i;
853   if (!absref) // illegal usage
854     return NULL;
855   i = hash_get (dwg->object_map, (uint32_t)absref);
856   if (i == HASH_NOT_FOUND
857       || (BITCODE_BL)i >= dwg->num_objects) // the latter being an invalid
858                                             // handle (read from DWG)
859     return NULL;
860   return &dwg->object[i]; // allow value 0
861 }
862 
863 EXPORT Dwg_Object *
dwg_ref_object_silent(const Dwg_Data * restrict dwg,Dwg_Object_Ref * restrict ref)864 dwg_ref_object_silent (const Dwg_Data *restrict dwg,
865                        Dwg_Object_Ref *restrict ref)
866 {
867   if (!ref)
868     return NULL;
869   if (ref->obj && !dwg->dirty_refs)
870     return ref->obj;
871   if ((ref->handleref.code < 6
872        && dwg_resolve_handleref ((Dwg_Object_Ref *)ref, NULL))
873       || ref->absolute_ref)
874     {
875       Dwg_Object *obj = dwg_resolve_handle_silent (dwg, ref->absolute_ref);
876       if (!dwg->dirty_refs && obj)
877         ref->obj = obj;
878       return obj;
879     }
880   else
881     return NULL;
882 }
883 
884 /* set ref->absolute_ref from obj, for a subsequent dwg_resolve_handle() */
885 EXPORT int
dwg_resolve_handleref(Dwg_Object_Ref * restrict ref,const Dwg_Object * restrict obj)886 dwg_resolve_handleref (Dwg_Object_Ref *restrict ref,
887                        const Dwg_Object *restrict obj)
888 {
889   /*
890    * With TYPEDOBJHANDLE 2-5 the code indicates the type of ownership:
891    *   2 Soft owner
892    *   3 Hard owner
893    *   4 Soft pointer
894    *   5 Hard pointer
895    * With OFFSETOBJHANDLE >5 the code 4 handle is stored as an offset from some
896    * other handle.
897    */
898   switch (ref->handleref.code)
899     {
900     // implicit code: 4
901     case 6:
902       ref->absolute_ref = (obj->handle.value + 1);
903       break;
904     case 8:
905       ref->absolute_ref = (obj->handle.value - 1);
906       break;
907     case 10:
908       ref->absolute_ref = (obj->handle.value + ref->handleref.value);
909       break;
910     case 12:
911       ref->absolute_ref = (obj->handle.value - ref->handleref.value);
912       break;
913     case 2:
914     case 3:
915     case 4:
916     case 5:
917       ref->absolute_ref = ref->handleref.value;
918       break;
919     case 0: // ignore?
920       ref->absolute_ref = ref->handleref.value;
921       break;
922     default:
923       ref->absolute_ref = ref->handleref.value;
924       LOG_WARN ("Invalid handle pointer code %d", ref->handleref.code);
925       return 0;
926     }
927   return 1;
928 }
929 
930 /** Returns the block_control for the DWG,
931     containing the list of all blocks headers.
932 */
933 EXPORT Dwg_Object_BLOCK_CONTROL *
dwg_block_control(Dwg_Data * dwg)934 dwg_block_control (Dwg_Data *dwg)
935 {
936   if (!dwg->block_control.parent)
937     {
938       Dwg_Object *obj;
939       Dwg_Object_Ref *ctrl = dwg->header_vars.BLOCK_CONTROL_OBJECT;
940       if (!ctrl || !(obj = dwg_ref_object (dwg, ctrl)) || obj->type != DWG_TYPE_BLOCK_CONTROL)
941         {
942           LOG_ERROR ("dwg.block_control and HEADER.BLOCK_CONTROL_OBJECT missing");
943           return NULL;
944         }
945       else
946         {
947           dwg->block_control = *obj->tio.object->tio.BLOCK_CONTROL;
948         }
949     }
950   return &(dwg->block_control);
951 }
952 
953 /** Returns the model space block object for the DWG.
954     On r2010 and r2013 it could be different to the canonical
955    dwg->block_control.model_space.
956 */
957 EXPORT Dwg_Object_Ref *
dwg_model_space_ref(Dwg_Data * dwg)958 dwg_model_space_ref (Dwg_Data *dwg)
959 {
960   Dwg_Object *obj;
961   Dwg_Object_BLOCK_CONTROL *block_control;
962   if (dwg->header_vars.BLOCK_RECORD_MSPACE
963       && dwg->header_vars.BLOCK_RECORD_MSPACE->obj)
964     return dwg->header_vars.BLOCK_RECORD_MSPACE;
965   if (dwg->block_control.model_space && dwg->block_control.model_space->obj)
966     {
967       dwg->header_vars.BLOCK_RECORD_MSPACE = dwg->block_control.model_space;
968       return dwg->block_control.model_space;
969     }
970   block_control = dwg_block_control (dwg);
971   if (block_control && block_control->model_space && block_control->model_space->obj)
972     {
973       dwg->block_control.model_space = block_control->model_space;
974       dwg->header_vars.BLOCK_RECORD_MSPACE = block_control->model_space;
975       return block_control->model_space;
976     }
977   obj = dwg_get_first_object (dwg, DWG_TYPE_BLOCK_CONTROL);
978   if (!obj)
979     return NULL;
980   block_control = obj->tio.object->tio.BLOCK_CONTROL;
981   if (block_control && block_control->model_space && block_control->model_space->obj)
982     {
983       dwg->block_control.model_space = block_control->model_space;
984       dwg->header_vars.BLOCK_RECORD_MSPACE = block_control->model_space;
985       return block_control->model_space;
986     }
987   return NULL;
988 }
989 
990 /** Returns the paper space block object for the DWG.
991  */
992 EXPORT Dwg_Object_Ref *
dwg_paper_space_ref(Dwg_Data * dwg)993 dwg_paper_space_ref (Dwg_Data *dwg)
994 {
995   if (dwg->header_vars.BLOCK_RECORD_PSPACE
996       && dwg->header_vars.BLOCK_RECORD_PSPACE->obj)
997     return dwg->header_vars.BLOCK_RECORD_PSPACE;
998   return dwg->block_control.paper_space && dwg->block_control.paper_space->obj
999              ? dwg->block_control.paper_space
1000              : NULL;
1001 }
1002 
1003 /** Returns the model space block object for the DWG.
1004  */
1005 EXPORT Dwg_Object *
dwg_model_space_object(Dwg_Data * dwg)1006 dwg_model_space_object (Dwg_Data *dwg)
1007 {
1008   Dwg_Object_Ref *msref = dwg_model_space_ref (dwg);
1009   Dwg_Object_BLOCK_CONTROL *ctrl;
1010 
1011   if (msref && msref->obj && msref->obj->type == DWG_TYPE_BLOCK_HEADER)
1012     return msref->obj;
1013   ctrl = dwg_block_control (dwg);
1014   if (ctrl && ctrl->model_space && ctrl->model_space->obj)
1015     return ctrl->model_space->obj;
1016   if (dwg->header_vars.BLOCK_RECORD_MSPACE
1017       && dwg->header_vars.BLOCK_RECORD_MSPACE->obj)
1018     return dwg->header_vars.BLOCK_RECORD_MSPACE->obj;
1019   if (!dwg->object_map) // for dwg_add_document()
1020     dwg->object_map = hash_new (100);
1021   return dwg_resolve_handle (dwg, dwg->header.version >= R_2000 ? 0x1F : 0x17);
1022 }
1023 
1024 /** Returns the paper space block object for the DWG.
1025  */
1026 EXPORT Dwg_Object *
dwg_paper_space_object(Dwg_Data * dwg)1027 dwg_paper_space_object (Dwg_Data *dwg)
1028 {
1029   Dwg_Object_Ref *psref = dwg_paper_space_ref (dwg);
1030   Dwg_Object_BLOCK_CONTROL *ctrl;
1031 
1032   if (psref && psref->obj && psref->obj->type == DWG_TYPE_BLOCK_HEADER)
1033     return psref->obj;
1034   ctrl = dwg_block_control (dwg);
1035   if (ctrl && ctrl->paper_space && ctrl->paper_space->obj)
1036     return ctrl->paper_space->obj;
1037   if (dwg->header_vars.BLOCK_RECORD_PSPACE
1038       && dwg->header_vars.BLOCK_RECORD_PSPACE->obj)
1039     return dwg->header_vars.BLOCK_RECORD_PSPACE->obj;
1040   else
1041     return NULL;
1042 }
1043 
1044 /** Returns the first entity owned by the block hdr, or NULL.
1045  */
1046 EXPORT Dwg_Object *
get_first_owned_entity(const Dwg_Object * hdr)1047 get_first_owned_entity (const Dwg_Object *hdr)
1048 {
1049   unsigned int version = hdr->parent->header.version;
1050   Dwg_Object_BLOCK_HEADER *_hdr = hdr->tio.object->tio.BLOCK_HEADER;
1051   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1052     {
1053       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1054       return NULL;
1055     }
1056 
1057   if (R_13 <= version && version <= R_2000)
1058     {
1059       /* With r2000 we rather follow the next_entity chain */
1060       return _hdr->first_entity ? _hdr->first_entity->obj : NULL;
1061     }
1062   else if (version >= R_2004)
1063     {
1064       _hdr->__iterator = 0;
1065       if (_hdr->entities && _hdr->num_owned && _hdr->entities[0])
1066         {
1067           if (!_hdr->entities[0]->obj)
1068             dwg_resolve_objectrefs_silent (hdr->parent);
1069           return _hdr->entities[0]->obj;
1070         }
1071       else
1072         return NULL;
1073     }
1074 
1075   // TODO: preR13 block table
1076   LOG_ERROR ("Unsupported version: %d\n", version);
1077   return NULL;
1078 }
1079 
1080 /** Returns the next entity owned by mspace or pspace or NULL.
1081  */
1082 EXPORT Dwg_Object *
dwg_next_entity(const Dwg_Object * restrict obj)1083 dwg_next_entity (const Dwg_Object *restrict obj)
1084 {
1085   Dwg_Object_Ref *restrict next;
1086 
1087   if (obj == NULL ||
1088       obj->parent == NULL ||
1089       obj->supertype != DWG_SUPERTYPE_ENTITY)
1090     return NULL;
1091   if (obj->parent->header.version < R_2004)
1092     {
1093       if (!obj->tio.entity) // decoding error
1094         goto next_obj;
1095       next = obj->tio.entity->next_entity;
1096       if (next && next->absolute_ref)
1097         return dwg_ref_object_silent (obj->parent, next);
1098       else
1099         goto next_obj;
1100     }
1101   else
1102     {
1103     next_obj:
1104       obj = dwg_next_object (obj);
1105       while (obj && obj->supertype != DWG_SUPERTYPE_ENTITY)
1106         {
1107           obj = dwg_next_object (obj);
1108         }
1109       return (Dwg_Object*)obj;
1110     }
1111 }
1112 
1113 /** Returns the next entity owned by the block hdr, or NULL.
1114  *  Not subentities: ATTRIB, VERTEX.
1115  */
1116 EXPORT Dwg_Object *
get_next_owned_entity(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1117 get_next_owned_entity (const Dwg_Object *restrict hdr,
1118                        const Dwg_Object *restrict current)
1119 {
1120   Dwg_Data *dwg = hdr->parent;
1121   Dwg_Version_Type version = dwg->header.version;
1122   Dwg_Object_BLOCK_HEADER *_hdr = hdr->tio.object->tio.BLOCK_HEADER;
1123   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1124     {
1125       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1126       return NULL;
1127     }
1128 
1129   if (R_13 <= version && version <= R_2000)
1130     {
1131       Dwg_Object *obj;
1132       if (_hdr->last_entity == NULL
1133           || current->handle.value >= _hdr->last_entity->absolute_ref)
1134         return NULL;
1135       obj = dwg_next_entity (current);
1136       while (obj && (obj->type == DWG_TYPE_ATTDEF
1137                      || obj->type == DWG_TYPE_ATTRIB
1138                      || obj->type == DWG_TYPE_VERTEX_2D
1139                      || obj->type == DWG_TYPE_VERTEX_3D
1140                      || obj->type == DWG_TYPE_VERTEX_MESH
1141                      || obj->type == DWG_TYPE_VERTEX_PFACE
1142                      || obj->type == DWG_TYPE_VERTEX_PFACE_FACE))
1143         {
1144           obj = dwg_next_entity (obj);
1145           // this may happen with r2000 attribs
1146           if (obj
1147               && obj->tio.entity != NULL
1148               && obj->tio.entity->ownerhandle != NULL
1149               && obj->tio.entity->ownerhandle->absolute_ref
1150                      != hdr->handle.value)
1151             obj = NULL;
1152           if (obj == _hdr->last_entity->obj) // early exit
1153             return obj;
1154         }
1155       return obj;
1156     }
1157   else if (version >= R_2004)
1158     {
1159       Dwg_Object_Ref *ref;
1160       _hdr->__iterator++;
1161       if (_hdr->__iterator == _hdr->num_owned)
1162         return NULL;
1163       ref = _hdr->entities ? _hdr->entities[_hdr->__iterator] : NULL;
1164       return ref ? dwg_ref_object (dwg, ref) : NULL;
1165     }
1166 
1167   LOG_ERROR ("Unsupported version: %d\n", version);
1168   return NULL;
1169 }
1170 
1171 /** Returns the first subentity owned by the insert or polyline.
1172  */
1173 EXPORT Dwg_Object *
get_first_owned_subentity(const Dwg_Object * owner)1174 get_first_owned_subentity (const Dwg_Object *owner)
1175 {
1176   Dwg_Data *dwg = owner->parent;
1177   Dwg_Version_Type version = dwg->header.version;
1178   const unsigned int type = owner->type;
1179   if (type == DWG_TYPE_INSERT)
1180     {
1181       Dwg_Entity_INSERT *_obj = owner->tio.entity->tio.INSERT;
1182       if (version <= R_2000)
1183         return _obj->first_attrib ? _obj->first_attrib->obj : NULL;
1184       else
1185         return _obj->attribs && _obj->attribs[0]
1186                    ? dwg_ref_object (dwg, _obj->attribs[0])
1187                    : NULL;
1188     }
1189   else if (type == DWG_TYPE_MINSERT)
1190     {
1191       Dwg_Entity_MINSERT *_obj = owner->tio.entity->tio.MINSERT;
1192       if (version <= R_2000)
1193         return _obj->first_attrib ? dwg_ref_object (dwg, _obj->first_attrib) : NULL;
1194       else
1195         return _obj->attribs && _obj->attribs[0]
1196                    ? dwg_ref_object (dwg, _obj->attribs[0])
1197                    : NULL;
1198     }
1199   else if (type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1200            || type == DWG_TYPE_POLYLINE_PFACE
1201            || type == DWG_TYPE_POLYLINE_MESH)
1202     {
1203       // guaranteed structure
1204       Dwg_Entity_POLYLINE_2D *_obj = owner->tio.entity->tio.POLYLINE_2D;
1205       if (version <= R_2000)
1206         return _obj->first_vertex ? dwg_ref_object (dwg, _obj->first_vertex) : NULL;
1207       else
1208         return _obj->vertex && _obj->vertex[0] ? dwg_ref_object (dwg, _obj->vertex[0]) : NULL;
1209     }
1210   else
1211     {
1212       LOG_ERROR ("Wrong type %d, has no subentity", type);
1213     }
1214   return NULL;
1215 }
1216 
1217 /** Returns the next subentity owned by the object.
1218  */
1219 EXPORT Dwg_Object *
get_next_owned_subentity(const Dwg_Object * restrict owner,const Dwg_Object * restrict current)1220 get_next_owned_subentity (const Dwg_Object *restrict owner,
1221                           const Dwg_Object *restrict current)
1222 {
1223   Dwg_Data *dwg = owner->parent;
1224   Dwg_Version_Type version = dwg->header.version;
1225   const Dwg_Object_Type type = (const Dwg_Object_Type)owner->type;
1226   Dwg_Object_Entity *ent = owner->tio.entity;
1227   Dwg_Object *obj = dwg_next_object (current);
1228 
1229   if (type == DWG_TYPE_INSERT)
1230     {
1231       Dwg_Entity_INSERT *_obj = owner->tio.entity->tio.INSERT;
1232       if (version <= R_2000)
1233         return (_obj->last_attrib && current != _obj->last_attrib->obj
1234                 && obj->type == DWG_TYPE_ATTRIB)
1235                    ? obj
1236                    : NULL;
1237       else
1238         {
1239           ent->__iterator++;
1240           if (ent->__iterator == _obj->num_owned)
1241             {
1242               ent->__iterator = 0;
1243               return NULL;
1244             }
1245           else
1246             return _obj->attribs
1247                        ? dwg_ref_object (dwg, _obj->attribs[ent->__iterator])
1248                        : NULL;
1249         }
1250     }
1251   else if (type == DWG_TYPE_MINSERT)
1252     {
1253       Dwg_Entity_MINSERT *_obj = owner->tio.entity->tio.MINSERT;
1254       if (version <= R_2000)
1255         return (_obj->last_attrib && current != _obj->last_attrib->obj
1256                 && obj->type == DWG_TYPE_ATTRIB)
1257                    ? obj
1258                    : NULL;
1259       else
1260         {
1261           ent->__iterator++;
1262           if (ent->__iterator == _obj->num_owned)
1263             {
1264               ent->__iterator = 0;
1265               return NULL;
1266             }
1267           else
1268             return _obj->attribs
1269                       ? dwg_ref_object (dwg, _obj->attribs[ent->__iterator])
1270                       : NULL;
1271         }
1272     }
1273   else if (type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1274            || type == DWG_TYPE_POLYLINE_PFACE
1275            || type == DWG_TYPE_POLYLINE_MESH)
1276     {
1277       // guaranteed structure
1278       Dwg_Entity_POLYLINE_2D *_obj = owner->tio.entity->tio.POLYLINE_2D;
1279       if (version <= R_2000)
1280         return (_obj->last_vertex && current != _obj->last_vertex->obj) ? obj
1281                                                                         : NULL;
1282       else
1283         {
1284           ent->__iterator++;
1285           if (ent->__iterator == _obj->num_owned)
1286             {
1287               ent->__iterator = 0;
1288               return NULL;
1289             }
1290           else
1291             return _obj->vertex ? dwg_ref_object (dwg, _obj->vertex[ent->__iterator]) : NULL;
1292         }
1293     }
1294   else
1295     {
1296       LOG_ERROR ("Wrong type %d, has no subentity", type);
1297     }
1298   return NULL;
1299 }
1300 
1301 /** Returns the BLOCK entity owned by the block hdr.
1302  *  Only NULL on illegal hdr argument or dwg version.
1303  */
1304 EXPORT Dwg_Object *
get_first_owned_block(const Dwg_Object * hdr)1305 get_first_owned_block (const Dwg_Object *hdr)
1306 {
1307   Dwg_Data *dwg = hdr->parent;
1308   Dwg_Version_Type version = dwg->header.version;
1309   const Dwg_Object_BLOCK_HEADER *restrict _hdr
1310       = hdr->tio.object->tio.BLOCK_HEADER;
1311   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1312     {
1313       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1314       return NULL;
1315     }
1316 
1317   if (version >= R_13)
1318     {
1319       if (_hdr->block_entity)
1320         {
1321           if (!_hdr->block_entity->obj)
1322             dwg_resolve_objectrefs_silent (dwg);
1323           return dwg_ref_object (dwg, _hdr->block_entity);
1324         }
1325       else
1326         {
1327           Dwg_Object *obj = (Dwg_Object *)hdr;
1328           while (obj && obj->type != DWG_TYPE_BLOCK)
1329             obj = dwg_next_object (obj);
1330           return obj;
1331         }
1332     }
1333 
1334   // TODO: preR13 block table
1335   LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1336   return NULL;
1337 }
1338 
1339 /** Returns the next block object after current owned by the block hdr, or
1340  *  NULL.
1341  */
1342 EXPORT Dwg_Object *
get_next_owned_block(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1343 get_next_owned_block (const Dwg_Object *restrict hdr,
1344                       const Dwg_Object *restrict current)
1345 {
1346   Dwg_Data *dwg = hdr->parent;
1347   Dwg_Version_Type version = dwg->header.version;
1348   const Dwg_Object_BLOCK_HEADER *restrict _hdr
1349       = hdr->tio.object->tio.BLOCK_HEADER;
1350   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1351     {
1352       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1353       return NULL;
1354     }
1355 
1356   if (version >= R_13)
1357     {
1358       if (!_hdr->endblk_entity || current->handle.value >= _hdr->endblk_entity->absolute_ref)
1359         return NULL;
1360       return dwg_next_object (current);
1361     }
1362 
1363   LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1364   return NULL;
1365 }
1366 
1367 /** Returns the next block object until last_entity
1368  *  after current owned by the block hdr, or NULL.
1369  */
1370 EXPORT Dwg_Object *
get_next_owned_block_entity(const Dwg_Object * restrict hdr,const Dwg_Object * restrict current)1371 get_next_owned_block_entity (const Dwg_Object *restrict hdr,
1372                              const Dwg_Object *restrict current)
1373 {
1374   Dwg_Data *dwg;
1375   Dwg_Version_Type version;
1376   Dwg_Object_BLOCK_HEADER *restrict _hdr;
1377 
1378   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1379     {
1380       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1381       return NULL;
1382     }
1383 
1384   dwg = hdr->parent;
1385   version = dwg->header.version;
1386   _hdr = hdr->tio.object->tio.BLOCK_HEADER;
1387 
1388   if (R_13 <= version && version <= R_2000)
1389     {
1390       /* With r2000 we rather follow the next_entity chain. It may jump around the linked list. */
1391       if (!_hdr->last_entity
1392           || current->handle.value == _hdr->last_entity->absolute_ref)
1393         return NULL;
1394       return dwg_next_entity (current);
1395     }
1396   if (version > R_2000)
1397     {
1398       Dwg_Object_Ref *ref;
1399       _hdr->__iterator++;
1400       if (_hdr->__iterator == _hdr->num_owned)
1401         return NULL;
1402       ref = _hdr->entities ? _hdr->entities[_hdr->__iterator] : NULL;
1403       return ref ? dwg_ref_object (dwg, ref) : NULL;
1404     }
1405 
1406   LOG_ERROR ("Unsupported version: %s\n", dwg_version_type (version));
1407   return NULL;
1408 }
1409 
1410 /** Returns the last ENDBLK entity owned by the block hdr.
1411  *  Only NULL on illegal hdr argument or dwg version.
1412  */
1413 EXPORT Dwg_Object *
get_last_owned_block(const Dwg_Object * restrict hdr)1414 get_last_owned_block (const Dwg_Object *restrict hdr)
1415 {
1416   Dwg_Data *dwg = hdr->parent;
1417   Dwg_Object_BLOCK_HEADER *restrict _hdr = hdr->tio.object->tio.BLOCK_HEADER;
1418   unsigned int version = dwg->header.version;
1419   if (hdr->type != DWG_TYPE_BLOCK_HEADER)
1420     {
1421       LOG_ERROR ("Invalid BLOCK_HEADER type %d", hdr->type);
1422       return NULL;
1423     }
1424 
1425   if (version >= R_13)
1426     {
1427       if (_hdr->endblk_entity && _hdr->endblk_entity->obj)
1428         return _hdr->endblk_entity->obj;
1429       else
1430         {
1431           Dwg_Object *obj = (Dwg_Object *)hdr;
1432           while (obj && obj->type != DWG_TYPE_ENDBLK)
1433             obj = dwg_next_object (obj);
1434           if (obj && obj->type == DWG_TYPE_ENDBLK)
1435             {
1436               if (!_hdr->endblk_entity)
1437                 {
1438                   _hdr->endblk_entity = (BITCODE_H)calloc (1, sizeof (Dwg_Object_Ref));
1439                   if (_hdr->endblk_entity)
1440                     {
1441                       _hdr->endblk_entity->obj = obj;
1442                       _hdr->endblk_entity->handleref.value
1443                           = _hdr->endblk_entity->absolute_ref
1444                           = obj->handle.value;
1445                     }
1446                 }
1447               else if (!_hdr->endblk_entity->obj)
1448                 _hdr->endblk_entity->obj = obj;
1449             }
1450           return obj;
1451         }
1452     }
1453 
1454   LOG_ERROR ("Unsupported version: %d\n", version);
1455   return NULL;
1456 }
1457 
1458 EXPORT Dwg_Object *
dwg_get_first_object(const Dwg_Data * dwg,const Dwg_Object_Type type)1459 dwg_get_first_object (const Dwg_Data *dwg, const Dwg_Object_Type type)
1460 {
1461   for (unsigned i = 0; i < dwg->num_objects; i++)
1462     {
1463       Dwg_Object *const obj = &dwg->object[i];
1464       if (obj->fixedtype == type &&
1465           obj->tio.object &&
1466           obj->tio.object->tio.APPID)
1467         return obj;
1468     }
1469   return NULL;
1470 }
1471 
1472 EXPORT int
dwg_class_is_entity(const Dwg_Class * restrict klass)1473 dwg_class_is_entity (const Dwg_Class *restrict klass)
1474 {
1475   return (klass != NULL && (int)klass->item_class_id == 0x1f2) ? 1 : 0;
1476 }
1477 
1478 EXPORT int
dwg_obj_is_control(const Dwg_Object * obj)1479 dwg_obj_is_control (const Dwg_Object *obj)
1480 {
1481   const unsigned int type = obj->type;
1482   return (obj->supertype == DWG_SUPERTYPE_OBJECT)
1483          && (type == DWG_TYPE_BLOCK_CONTROL || type == DWG_TYPE_LAYER_CONTROL
1484              || type == DWG_TYPE_STYLE_CONTROL
1485              || type == DWG_TYPE_LTYPE_CONTROL || type == DWG_TYPE_VIEW_CONTROL
1486              || type == DWG_TYPE_UCS_CONTROL || type == DWG_TYPE_VPORT_CONTROL
1487              || type == DWG_TYPE_APPID_CONTROL
1488              || type == DWG_TYPE_DIMSTYLE_CONTROL
1489              || type == DWG_TYPE_VX_CONTROL);
1490 }
1491 
1492 EXPORT int
dwg_obj_is_table(const Dwg_Object * obj)1493 dwg_obj_is_table (const Dwg_Object *obj)
1494 {
1495   const unsigned int type = obj->type;
1496   return (obj->supertype == DWG_SUPERTYPE_OBJECT)
1497          && (type == DWG_TYPE_BLOCK_HEADER || type == DWG_TYPE_LAYER
1498              || type == DWG_TYPE_STYLE || type == DWG_TYPE_LTYPE
1499              || type == DWG_TYPE_VIEW || type == DWG_TYPE_UCS
1500              || type == DWG_TYPE_VPORT || type == DWG_TYPE_APPID
1501              || type == DWG_TYPE_DIMSTYLE
1502              || type == DWG_TYPE_VX_TABLE_RECORD);
1503 }
1504 
1505 EXPORT int
dwg_obj_is_subentity(const Dwg_Object * obj)1506 dwg_obj_is_subentity (const Dwg_Object *obj)
1507 {
1508   const unsigned int type = obj->type;
1509   return (obj->supertype == DWG_SUPERTYPE_ENTITY)
1510          && (type == DWG_TYPE_ATTRIB || type == DWG_TYPE_VERTEX_2D
1511              || type == DWG_TYPE_VERTEX_3D || type == DWG_TYPE_VERTEX_MESH
1512              || type == DWG_TYPE_VERTEX_PFACE
1513              || type == DWG_TYPE_VERTEX_PFACE_FACE);
1514 }
1515 
1516 EXPORT int
dwg_obj_has_subentity(const Dwg_Object * obj)1517 dwg_obj_has_subentity (const Dwg_Object *obj)
1518 {
1519   const unsigned int type = obj->type;
1520   return (obj->supertype == DWG_SUPERTYPE_ENTITY)
1521          && (type == DWG_TYPE_INSERT || type == DWG_TYPE_MINSERT
1522              || type == DWG_TYPE_POLYLINE_2D || type == DWG_TYPE_POLYLINE_3D
1523              || type == DWG_TYPE_POLYLINE_PFACE
1524              || type == DWG_TYPE_POLYLINE_MESH);
1525 }
1526 
1527 // All entities deriving from 3DSOLID/AcDbModelerGeometry
1528 EXPORT int
dwg_obj_is_3dsolid(const Dwg_Object * obj)1529 dwg_obj_is_3dsolid (const Dwg_Object *obj)
1530 {
1531   const Dwg_Object_Type type = obj->fixedtype;
1532   return
1533     (obj->supertype == DWG_SUPERTYPE_OBJECT
1534      && (type == DWG_TYPE_ACSH_BREP_CLASS ||
1535          type == DWG_TYPE_ASSOCASMBODYACTIONPARAM))
1536      ||
1537     (obj->supertype == DWG_SUPERTYPE_ENTITY
1538      && (type == DWG_TYPE__3DSOLID ||
1539          type == DWG_TYPE_REGION ||
1540          type == DWG_TYPE_BODY ||
1541          type == DWG_TYPE_EXTRUDEDSURFACE ||
1542          type == DWG_TYPE_LOFTEDSURFACE ||
1543          type == DWG_TYPE_NURBSURFACE ||
1544          type == DWG_TYPE_PLANESURFACE ||
1545          type == DWG_TYPE_REVOLVEDSURFACE ||
1546          type == DWG_TYPE_SWEPTSURFACE));
1547 }
1548 
1549 EXPORT int
dwg_obj_is_acsh(const Dwg_Object * obj)1550 dwg_obj_is_acsh (const Dwg_Object *obj)
1551 {
1552   const Dwg_Object_Type type = obj->fixedtype;
1553   return (obj->supertype == DWG_SUPERTYPE_OBJECT
1554          && (type == DWG_TYPE_ACSH_BOOLEAN_CLASS
1555              || type == DWG_TYPE_ACSH_BOX_CLASS
1556              || type == DWG_TYPE_ACSH_BREP_CLASS
1557              || type == DWG_TYPE_ACSH_CHAMFER_CLASS
1558              || type == DWG_TYPE_ACSH_CONE_CLASS
1559              || type == DWG_TYPE_ACSH_CYLINDER_CLASS
1560              || type == DWG_TYPE_ACSH_EXTRUSION_CLASS
1561              || type == DWG_TYPE_ACSH_FILLET_CLASS
1562              || type == DWG_TYPE_ACSH_HISTORY_CLASS
1563              || type == DWG_TYPE_ACSH_LOFT_CLASS
1564              || type == DWG_TYPE_ACSH_PYRAMID_CLASS
1565              || type == DWG_TYPE_ACSH_REVOLVE_CLASS
1566              || type == DWG_TYPE_ACSH_SPHERE_CLASS
1567              || type == DWG_TYPE_ACSH_SWEEP_CLASS
1568              || type == DWG_TYPE_ACSH_TORUS_CLASS
1569              || type == DWG_TYPE_ACSH_WEDGE_CLASS));
1570 }
1571 
1572 EXPORT Dwg_Section_Type
dwg_section_type(const char * restrict name)1573 dwg_section_type (const char* restrict name)
1574 {
1575   if (name == NULL)
1576     {
1577       return SECTION_UNKNOWN; // but could also be INFO or SYSTEM_MAP
1578     }
1579   else if (strEQc (name, "AcDb:Header"))
1580     {
1581       return SECTION_HEADER;
1582     }
1583   else if (strEQc (name, "AcDb:Classes"))
1584     {
1585       return SECTION_CLASSES;
1586     }
1587   else if (strEQc (name, "AcDb:SummaryInfo"))
1588     {
1589       return SECTION_SUMMARYINFO;
1590     }
1591   else if (strEQc (name, "AcDb:Preview"))
1592     {
1593       return SECTION_PREVIEW;
1594     }
1595   else if (strEQc (name, "AcDb:VBAProject"))
1596     {
1597       return SECTION_VBAPROJECT;
1598     }
1599   else if (strEQc (name, "AcDb:AppInfo"))
1600     {
1601       return SECTION_APPINFO;
1602     }
1603   else if (strEQc (name, "AcDb:FileDepList"))
1604     {
1605       return SECTION_FILEDEPLIST;
1606     }
1607   else if (strEQc (name, "AcDb:RevHistory"))
1608     {
1609       return SECTION_REVHISTORY;
1610     }
1611   else if (strEQc (name, "AcDb:Security"))
1612     {
1613       return SECTION_SECURITY;
1614     }
1615   else if (strEQc (name, "AcDb:AcDbObjects"))
1616     {
1617       return SECTION_OBJECTS;
1618     }
1619   else if (strEQc (name, "AcDb:ObjFreeSpace"))
1620     {
1621       return SECTION_OBJFREESPACE;
1622     }
1623   else if (strEQc (name, "AcDb:Template"))
1624     {
1625       return SECTION_TEMPLATE;
1626     }
1627   else if (strEQc (name, "AcDb:Handles"))
1628     {
1629       return SECTION_HANDLES;
1630     }
1631   else if (strEQc (name, "AcDb:AcDsPrototype_1b"))
1632     { // r2013+
1633       return SECTION_ACDS; // 0xc or 0xd
1634     }
1635   else if (strEQc (name, "AcDb:AuxHeader"))
1636     {
1637       return SECTION_AUXHEADER;
1638     }
1639   else if (strEQc (name, "AcDb:Signature"))
1640     {
1641       return SECTION_SIGNATURE;
1642     }
1643   else if (strEQc (name, "AcDb:AppInfoHistory"))
1644     { // AC1021
1645       return SECTION_APPINFOHISTORY;
1646     }
1647   return SECTION_UNKNOWN;
1648 }
1649 
1650 EXPORT Dwg_Section_Type
dwg_section_wtype(const DWGCHAR * restrict wname)1651 dwg_section_wtype (const DWGCHAR *restrict wname)
1652 {
1653   DWGCHAR *wp;
1654   char name[24];
1655   uint16_t c;
1656   int i = 0;
1657 
1658   if (wname == NULL)
1659     return SECTION_UNKNOWN; // but could also be INFO or SYSTEM_MAP
1660   wp = (DWGCHAR *)wname;
1661   while ((c = *wp++))
1662     {
1663       name[i++] = (char)(c & 0xff);
1664     }
1665   name[i] = '\0';
1666   return dwg_section_type (name);
1667 }
1668 
1669 static const char * const dwg_section_r2004_names[] =
1670 {
1671   "UNKNOWN",                  // 0
1672   "AcDb:Header",              // 1
1673   "AcDb:AuxHeader",           // 2
1674   "AcDb:Classes",             // 3
1675   "AcDb:Handles",             // 4
1676   "AcDb:Template",            // 5
1677   "AcDb:ObjFreeSpace",        // 6
1678   "AcDb:AcDbObjects",         // 7
1679   "AcDb:RevHistory",          // 8
1680   "AcDb:SummaryInfo",         // 9
1681   "AcDb:Preview",             // 10
1682   "AcDb:AppInfo",             // 11
1683   "AcDb:AppInfoHistory",      // 12
1684   "AcDb:FileDepList",         // 13
1685   "AcDb:Security",            // 14
1686   "AcDb:VBAProject",          // 15
1687   "AcDb:Signature",           // 16
1688   "AcDb:AcDsPrototype_1b",    // 17
1689   "INFO",                     // 18
1690   "SYSTEM_MAP",               // 19
1691 };
1692 static const char * const dwg_section_r13_names[] =
1693 {
1694   "Header",                   // 0
1695   "Classes",                  // 1
1696   "Handles",                  // 2
1697   "2ndHeader",                // 3
1698   "Template",                 // 4
1699   "AuxHeader"                 // 5
1700 };
1701 static const char * const dwg_section_r11_names[] =
1702 {
1703   "HEADER",                   // 0
1704   "BLOCK",                    // 1
1705   "LAYER"                     // 2
1706   "STYLE",                    // 3
1707   "LTYPE",                    // 4
1708   "VIEW",                     // 5
1709   "UCS",                      // 6
1710   "VPORT",                    // 7
1711   "APPID",                    // 8
1712   "DIMSTYLE",                 // 9
1713   "VX"              	      // 10
1714 };
1715 
1716 const char *
dwg_section_name(const Dwg_Data * dwg,const unsigned int sec_id)1717 dwg_section_name (const Dwg_Data *dwg, const unsigned int sec_id)
1718 {
1719   if (dwg->header.version >= R_2004)
1720     {
1721       return (sec_id <= SECTION_SYSTEM_MAP) ? dwg_section_r2004_names[sec_id] : NULL;
1722     }
1723   else if (dwg->header.version > R_11)
1724     {
1725       return (sec_id <= SECTION_AUXHEADER_R2000) ? dwg_section_r13_names[sec_id] : NULL;
1726     }
1727   else
1728     {
1729       return (sec_id <= SECTION_VX) ? dwg_section_r11_names[sec_id] : NULL;
1730     }
1731 }
1732 
1733 EXPORT enum RESBUF_VALUE_TYPE
dwg_resbuf_value_type(short gc)1734 dwg_resbuf_value_type (short gc)
1735 {
1736   if (gc >= 300)
1737     {
1738       if (gc >= 440)
1739         {
1740           if (gc >= 1000) // 1000-1071
1741             {
1742               if (gc == 1004)
1743                 return DWG_VT_BINARY;
1744               if (gc <= 1009)
1745                 return DWG_VT_STRING;
1746               if (gc <= 1059)
1747                 return DWG_VT_REAL;
1748               if (gc <= 1070)
1749                 return DWG_VT_INT16;
1750               if (gc == 1071)
1751                 return DWG_VT_INT32;
1752             }
1753           else // 440-999
1754             {
1755               if (gc <= 459)
1756                 return DWG_VT_INT32;
1757               if (gc <= 469)
1758                 return DWG_VT_REAL;
1759               if (gc <= 479)
1760                 return DWG_VT_STRING;
1761               if (gc <= 998)
1762                 return DWG_VT_INVALID;
1763               if (gc == 999)
1764                 return DWG_VT_STRING; // lgtm [cpp/constant-comparison]
1765             }
1766         }
1767       else // <440
1768         {
1769           if (gc >= 390) // 390-439
1770             {
1771               if (gc <= 399)
1772                 return DWG_VT_HANDLE;
1773               if (gc <= 409)
1774                 return DWG_VT_INT16;
1775               if (gc <= 419)
1776                 return DWG_VT_STRING;
1777               if (gc <= 429)
1778                 return DWG_VT_INT32;
1779               if (gc <= 439)
1780                 return DWG_VT_STRING; // lgtm [cpp/constant-comparison]
1781             }
1782           else // 330-389
1783             {
1784               if (gc <= 309)
1785                 return DWG_VT_STRING;
1786               if (gc <= 319)
1787                 return DWG_VT_BINARY;
1788               if (gc <= 329)
1789                 return DWG_VT_HANDLE;
1790               if (gc <= 369)
1791                 return DWG_VT_OBJECTID;
1792               if (gc <= 389)
1793                 return DWG_VT_INT16; // lgtm [cpp/constant-comparison]
1794             }
1795         }
1796     }
1797   else if (gc >= 105)
1798     {
1799       if (gc >= 210) // 210-299
1800         {
1801           if (gc <= 239)
1802             return DWG_VT_REAL;
1803           if (gc <= 269)
1804             return DWG_VT_INVALID;
1805           if (gc <= 279)
1806             return DWG_VT_INT16;
1807           if (gc <= 289)
1808             return DWG_VT_INT8;
1809           if (gc <= 299)
1810             return DWG_VT_BOOL; // lgtm [cpp/constant-comparison]
1811         }
1812       else // 105-209
1813         {
1814           if (gc == 105)
1815             return DWG_VT_HANDLE;
1816           if (gc <= 109)
1817             return DWG_VT_INVALID;
1818           if (gc <= 149)
1819             return DWG_VT_REAL;
1820           if (gc <= 169) // e.g. REQUIREDVERSIONS 160 r2013+
1821             return DWG_VT_INT64;
1822           if (gc <= 179)
1823             return DWG_VT_INT16;
1824           if (gc <= 209)
1825             return DWG_VT_INVALID; // lgtm [cpp/constant-comparison]
1826         }
1827     }
1828   else // <105
1829     {
1830       if (gc >= 38) // 38-102
1831         {
1832           if (gc <= 59)
1833             return DWG_VT_REAL;
1834           if (gc <= 79)
1835             return DWG_VT_INT16;
1836           if (gc <= 99)
1837             return DWG_VT_INT32;
1838           if (gc <= 101)
1839             return DWG_VT_STRING;
1840           if (gc == 102)
1841             return DWG_VT_STRING;
1842         }
1843       else // 0-37
1844         {
1845           if (gc < 0)
1846             return DWG_VT_HANDLE;
1847           if (gc <= 4)
1848             return DWG_VT_STRING;
1849           if (gc == 5)
1850             return DWG_VT_HANDLE;
1851           if (gc <= 9)
1852             return DWG_VT_STRING; // but 9 never TU
1853           if (gc <= 37)
1854             return DWG_VT_POINT3D; // lgtm [cpp/constant-comparison]
1855         }
1856     }
1857   return DWG_VT_INVALID;
1858 }
1859 
1860 // See acdb.h: 100th of a mm, enum of
1861 const int lweights[] = { 0,
1862                          5,
1863                          9,
1864                          13,
1865                          15,
1866                          18,
1867                          20,
1868                          25,
1869                          30,
1870                          35,
1871                          40,
1872                          50,
1873                          53,
1874                          60,
1875                          70,
1876                          80,
1877                          90,
1878                          100,
1879                          106,
1880                          120,
1881                          /*20:*/ 140,
1882                          158,
1883                          200,
1884                          211,
1885                          /*illegal/reserved:*/ 0,
1886                          0,
1887                          0,
1888                          0,
1889                          /*28:*/ 0,  // 0x1c
1890                          /*29:*/ -1, // 0x1d BYLAYER
1891                          -2,         // BYBLOCK
1892                          -3 };       // BYLWDEFAULT
1893 
1894 EXPORT int
dxf_cvt_lweight(const BITCODE_BSd value)1895 dxf_cvt_lweight (const BITCODE_BSd value)
1896 {
1897   return lweights[value % 32];
1898 }
1899 
1900 EXPORT BITCODE_BSd
dxf_revcvt_lweight(const int lw)1901 dxf_revcvt_lweight (const int lw)
1902 {
1903   for (BITCODE_BSd i = 0; i < (BITCODE_BSd)ARRAY_SIZE (lweights); i++)
1904     if (lweights[i] == lw)
1905       return i;
1906   return 0;
1907 }
1908 
1909 void
set_handle_size(Dwg_Handle * restrict hdl)1910 set_handle_size (Dwg_Handle *restrict hdl)
1911 {
1912   if (hdl->value)
1913     {
1914       int i;
1915       unsigned char *val;
1916       memset (&val, 0, sizeof(val));
1917       val = (unsigned char *)&hdl->value;
1918       // FIXME: little endian only
1919       for (i = sizeof(val) - 1; i >= 0; i--)
1920         if (val[i])
1921           break;
1922       hdl->size = i + 1;
1923     }
1924   else
1925      hdl->size = 0;
1926 }
1927 
1928 /** For encode:
1929  *  May need obj to shorten the code to a relative offset, but not in
1930  *  header_vars. There obj is NULL.
1931  */
1932 EXPORT int
dwg_add_handle(Dwg_Handle * restrict hdl,const BITCODE_RC code,const unsigned long absref,const Dwg_Object * restrict obj)1933 dwg_add_handle (Dwg_Handle *restrict hdl, const BITCODE_RC code,
1934                 const unsigned long absref, const Dwg_Object *restrict obj)
1935 {
1936   int offset = obj ? (absref - (int)obj->handle.value) : 0;
1937   hdl->code = code;
1938   hdl->value = absref;
1939   if (obj && (code == 0 || !offset) && absref) // only if same obj
1940     {
1941       Dwg_Data *dwg = obj->parent;
1942       LOG_HANDLE ("object_map{%lX} = %u\n", absref, obj->index);
1943       assert (dwg);
1944       if (!dwg->object_map) // for dwg_add_document()
1945         dwg->object_map = hash_new (100);
1946       hash_set (dwg->object_map, absref, (uint32_t)obj->index);
1947     }
1948 
1949   set_handle_size (hdl);
1950   if ((code == 4 || code > 5) && obj && absref)
1951     {
1952       // change code to 6.0.0 or 8.0.0
1953       if (offset == 1)
1954         {
1955           hdl->code = 6;
1956           hdl->value = 0;
1957           hdl->size = 0;
1958         }
1959       else if (offset == -1)
1960         {
1961           hdl->code = 8;
1962           hdl->value = 0;
1963           hdl->size = 0;
1964         }
1965       else if (offset > 0)
1966         {
1967           hdl->code = 10;
1968           hdl->value = offset;
1969           set_handle_size (hdl);
1970         }
1971       else if (offset < 0)
1972         {
1973           hdl->code = 12;
1974           hdl->value = -offset;
1975           set_handle_size (hdl);
1976         }
1977     }
1978   return 0;
1979 }
1980 
1981 
1982 // Returns an existing ref with the same ownership (hard/soft, owner/pointer)
1983 // or creates it. May return a freshly allocated ref via dwg_new_ref.
1984 EXPORT Dwg_Object_Ref *
dwg_add_handleref(Dwg_Data * restrict dwg,const BITCODE_RC code,const unsigned long absref,const Dwg_Object * restrict obj)1985 dwg_add_handleref (Dwg_Data *restrict dwg, const BITCODE_RC code,
1986                    const unsigned long absref, const Dwg_Object *restrict obj)
1987 {
1988   Dwg_Object_Ref *ref;
1989   // ENTITY, DICTIONARY, XRECORD or class may need to be relative.
1990   // GROUP needs to be absolute. DICTIONARYVAr absolute
1991   // TODO: prev_entity/next_entity also
1992   // skip the search for existing absolute ref then.
1993   if (code > 5
1994       || (code == 4 && obj
1995           && ((obj->fixedtype == DWG_TYPE_DICTIONARY
1996                || obj->fixedtype == DWG_TYPE_XRECORD
1997                || obj->supertype == DWG_SUPERTYPE_ENTITY
1998                || (obj->type > DWG_TYPE_GROUP
1999                    && obj->fixedtype != DWG_TYPE_DICTIONARYVAR)))))
2000     ;
2001   else
2002     {
2003       // search of this code-absref pair already exists
2004       for (BITCODE_BL i = 0; i < dwg->num_object_refs; i++)
2005         {
2006           Dwg_Object_Ref *refi = dwg->object_ref[i];
2007           if (refi->absolute_ref == absref && refi->handleref.code == code)
2008             return refi;
2009         }
2010     }
2011   // else create a new global ref
2012   ref = dwg_new_ref (dwg);
2013   dwg_add_handle (&ref->handleref, code, absref, obj);
2014   ref->absolute_ref = absref;
2015   // fill ->obj later
2016   return ref;
2017 }
2018 
2019 /** Return a link to the global ref or a new one. Or a NULLHDL. */
2020 EXPORT Dwg_Object_Ref *
dwg_dup_handleref(Dwg_Data * restrict dwg,const Dwg_Object_Ref * restrict ref)2021 dwg_dup_handleref (Dwg_Data *restrict dwg, const Dwg_Object_Ref *restrict ref)
2022 {
2023   if (ref)
2024     return dwg_add_handleref (dwg, ref->handleref.code, ref->absolute_ref, NULL);
2025   else
2026     return dwg_add_handleref (dwg, 5, 0, NULL);
2027 }
2028 
2029 // Creates a non-global, free'able handle ref.
2030 EXPORT Dwg_Object_Ref *
dwg_add_handleref_free(const BITCODE_RC code,const unsigned long absref)2031 dwg_add_handleref_free (const BITCODE_RC code, const unsigned long absref)
2032 {
2033   Dwg_Object_Ref *ref = (Dwg_Object_Ref *)calloc (1, sizeof (Dwg_Object_Ref));
2034   dwg_add_handle (&ref->handleref, code, absref, NULL);
2035   return ref;
2036 }
2037 
2038 // Not checking the header_vars entry, only searching the objects
2039 // Returning a hardowner ref (code 3) to it, as stored in header_vars.
2040 EXPORT BITCODE_H
dwg_find_table_control(Dwg_Data * restrict dwg,const char * restrict table)2041 dwg_find_table_control (Dwg_Data *restrict dwg, const char *restrict table)
2042 {
2043   BITCODE_BL i;
2044   for (i = 0; i < dwg->num_objects; i++)
2045     {
2046       if (dwg->object[i].name && strEQ (dwg->object[i].name, table))
2047         {
2048           Dwg_Handle *hdl = &dwg->object[i].handle;
2049           return dwg_add_handleref (dwg, 3, hdl->value, NULL);
2050         }
2051     }
2052   // if we haven't read all objects yet, ignore this error
2053   LOG_TRACE ("dwg_find_table_control: table control object %s not found\n",
2054              table)
2055   return NULL;
2056 }
2057 
2058 // Searching for a named dictionary entry, name is utf8.
2059 // Returning a hardpointer ref (5) to it, as stored in header_vars.
2060 // Usually another dictionary.
2061 EXPORT BITCODE_H
dwg_find_dictionary(Dwg_Data * restrict dwg,const char * restrict name)2062 dwg_find_dictionary (Dwg_Data *restrict dwg, const char *restrict name)
2063 {
2064   // The NOD (Named Object Dict) is always the very first DICTIONARY
2065   Dwg_Object_DICTIONARY *nod;
2066   Dwg_Object *obj = dwg_get_first_object (dwg, DWG_TYPE_DICTIONARY);
2067   if (!obj || !obj->tio.object || obj->fixedtype != DWG_TYPE_DICTIONARY)
2068     {
2069       LOG_ERROR ("dwg_find_dictionary: 1st NOD DICTIONARY not found")
2070       return NULL;
2071     }
2072   nod = obj->tio.object->tio.DICTIONARY;
2073   for (BITCODE_BL j = 0; j < nod->numitems; j++)
2074     {
2075       char *u8;
2076       if (!nod->texts || !nod->itemhandles)
2077         continue;
2078       u8 = nod->texts[j];
2079       if (!u8)
2080         continue;
2081       if (IS_FROM_TU_DWG (dwg))
2082         u8 = bit_convert_TU ((BITCODE_TU)u8);
2083       if (u8 && strEQ (u8, name))
2084         {
2085           Dwg_Object_Ref *ref = nod->itemhandles[j];
2086           if (!ref)
2087             continue;
2088           // relative? (8.0.0, 6.0.0, ...)
2089           dwg_resolve_handleref (ref, obj);
2090           if (IS_FROM_TU_DWG (dwg))
2091             free (u8);
2092           return dwg_add_handleref (dwg, 5, ref->absolute_ref, NULL);
2093         }
2094       if (IS_FROM_TU_DWG (dwg))
2095         free (u8);
2096     }
2097   LOG_TRACE ("dwg_find_dictionary: DICTIONARY with %s not found\n", name)
2098   return NULL;
2099 }
2100 
2101 // find the named dict entry
2102 EXPORT BITCODE_H
dwg_find_dicthandle(Dwg_Data * restrict dwg,BITCODE_H dict,const char * restrict name)2103 dwg_find_dicthandle (Dwg_Data *restrict dwg, BITCODE_H dict, const char *restrict name)
2104 {
2105   BITCODE_BL i;
2106   Dwg_Object_DICTIONARY *_obj;
2107   Dwg_Object *obj = dwg_resolve_handle (dwg, dict->absolute_ref);
2108 
2109   if (!obj || !obj->tio.object)
2110     {
2111       LOG_TRACE ("dwg_find_dicthandle: Could not resolve dict " FORMAT_REF "\n",
2112                  ARGS_REF(dict));
2113       return NULL;
2114     }
2115   if (obj->type != DWG_TYPE_DICTIONARY)
2116     {
2117       LOG_ERROR ("dwg_find_dicthandle: dict not a DICTIONARY\n");
2118       return NULL;
2119     }
2120 
2121   _obj = obj->tio.object->tio.DICTIONARY;
2122   if (!_obj->numitems)
2123     return NULL;
2124   for (i = 0; i < _obj->numitems; i++)
2125     {
2126       BITCODE_T *texts = _obj->texts;
2127       BITCODE_H *hdlv = _obj->itemhandles;
2128 
2129       if (!hdlv || !texts || !texts[i])
2130         continue;
2131       if (IS_FROM_TU_DWG (dwg))
2132         {
2133           if (bit_eq_TU (name, (BITCODE_TU)texts[i]))
2134             return hdlv[i];
2135         }
2136       else
2137         {
2138           if (strEQ (name, texts[i]))
2139             return hdlv[i];
2140         }
2141     }
2142   return NULL;
2143 }
2144 
2145 // find dict entry and match its name
2146 EXPORT BITCODE_H
dwg_find_dicthandle_objname(Dwg_Data * restrict dwg,BITCODE_H dict,const char * restrict name)2147 dwg_find_dicthandle_objname (Dwg_Data *restrict dwg, BITCODE_H dict, const char *restrict name)
2148 {
2149   BITCODE_BL i;
2150   Dwg_Object_DICTIONARY *_obj;
2151   Dwg_Object *obj = dwg_resolve_handle (dwg, dict->absolute_ref);
2152 
2153   if (!obj || !obj->tio.object)
2154     {
2155       LOG_TRACE ("dwg_find_dicthandle: Could not resolve dict " FORMAT_REF "\n",
2156                  ARGS_REF(dict));
2157       return NULL;
2158     }
2159   if (obj->type != DWG_TYPE_DICTIONARY)
2160     {
2161       LOG_ERROR ("dwg_find_dicthandle: dict not a DICTIONARY\n");
2162       return NULL;
2163     }
2164 
2165   _obj = obj->tio.object->tio.DICTIONARY;
2166   if (!_obj->numitems)
2167     return 0;
2168   for (i = 0; i < _obj->numitems; i++)
2169     {
2170       char *hdlname;
2171       BITCODE_H *hdlv = _obj->itemhandles;
2172       Dwg_Object *hobj;
2173       Dwg_Object_APPID *_o; // just some random type
2174       int isnew = 0;
2175       bool ok;
2176 
2177       if (!hdlv || !hdlv[i])
2178         continue;
2179       hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2180       if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID || !hobj->name)
2181         continue;
2182       _o = hobj->tio.object->tio.APPID;
2183       ok = dwg_dynapi_entity_utf8text (_o, hobj->name, "name", &hdlname, &isnew, NULL);
2184       LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2185                   hobj->name, hdlname ? hdlname : "NULL");
2186       if (ok && hdlname && (strEQ (name, hdlname) || !strcasecmp (name, hdlname)))
2187         {
2188           if (isnew)
2189             free (hdlname);
2190           return hdlv[i];
2191         }
2192       if (ok && isnew && hdlname)
2193         free (hdlname);
2194     }
2195   return NULL;
2196 }
2197 
2198 /* Return the handle of the first object of the given type. */
2199 Dwg_Handle *
dwg_find_first_type_handle(Dwg_Data * restrict dwg,enum DWG_OBJECT_TYPE type)2200 dwg_find_first_type_handle (Dwg_Data *restrict dwg, enum DWG_OBJECT_TYPE type)
2201 {
2202   for (BITCODE_BL i = 0; i < dwg->num_objects; i++)
2203     {
2204       if (dwg->object[i].fixedtype == type)
2205         return &dwg->object[i].handle;
2206     }
2207   return NULL;
2208 }
2209 
2210 BITCODE_H
dwg_find_tablehandle_silent(Dwg_Data * restrict dwg,const char * restrict name,const char * restrict table)2211 dwg_find_tablehandle_silent (Dwg_Data *restrict dwg, const char *restrict name,
2212                              const char *restrict table)
2213 {
2214   BITCODE_H ref;
2215   int oldopts = dwg->opts;
2216   dwg->opts &= ~(DWG_OPTS_LOGLEVEL);
2217   loglevel = 0;
2218 
2219   ref = dwg_find_tablehandle (dwg, name, table);
2220 
2221   dwg->opts = oldopts;
2222   loglevel = oldopts & DWG_OPTS_LOGLEVEL;
2223   return ref;
2224 }
2225 
2226 // return the matching _CONTROL table, DICTIONARY entry, or NULL
2227 EXPORT BITCODE_H
dwg_ctrl_table(Dwg_Data * restrict dwg,const char * restrict table)2228 dwg_ctrl_table (Dwg_Data *restrict dwg, const char *restrict table)
2229 {
2230   BITCODE_H ctrl = NULL;
2231   Dwg_Header_Variables *vars = &dwg->header_vars;
2232 
2233   if (!dwg || !table)
2234     return NULL;
2235   if (strEQc (table, "BLOCK"))
2236     {
2237       if (!(ctrl = vars->BLOCK_CONTROL_OBJECT))
2238         vars->BLOCK_CONTROL_OBJECT = ctrl
2239             = dwg_find_table_control (dwg, "BLOCK_CONTROL");
2240     }
2241   else if (strEQc (table, "LAYER"))
2242     {
2243       if (!(ctrl = vars->LAYER_CONTROL_OBJECT))
2244         vars->LAYER_CONTROL_OBJECT = ctrl
2245             = dwg_find_table_control (dwg, "LAYER_CONTROL");
2246     }
2247   else if (strEQc (table, "STYLE"))
2248     {
2249       if (!(ctrl = vars->STYLE_CONTROL_OBJECT))
2250         vars->STYLE_CONTROL_OBJECT = ctrl
2251             = dwg_find_table_control (dwg, "STYLE_CONTROL");
2252     }
2253   else if (strEQc (table, "LTYPE"))
2254     {
2255       if (!(ctrl = vars->LTYPE_CONTROL_OBJECT))
2256         vars->LTYPE_CONTROL_OBJECT = ctrl
2257             = dwg_find_table_control (dwg, "LTYPE_CONTROL");
2258     }
2259   else if (strEQc (table, "VIEW"))
2260     {
2261       if (!(ctrl = vars->VIEW_CONTROL_OBJECT))
2262         vars->VIEW_CONTROL_OBJECT = ctrl
2263             = dwg_find_table_control (dwg, "VIEW_CONTROL");
2264     }
2265   else if (strEQc (table, "UCS"))
2266     {
2267       if (!(ctrl = vars->UCS_CONTROL_OBJECT))
2268         vars->UCS_CONTROL_OBJECT = ctrl
2269             = dwg_find_table_control (dwg, "UCS_CONTROL");
2270     }
2271   else if (strEQc (table, "VPORT"))
2272     {
2273       if (!(ctrl = vars->VPORT_CONTROL_OBJECT))
2274         vars->VPORT_CONTROL_OBJECT = ctrl
2275             = dwg_find_table_control (dwg, "VPORT_CONTROL");
2276     }
2277   else if (strEQc (table, "APPID"))
2278     {
2279       if (!(ctrl = vars->APPID_CONTROL_OBJECT))
2280         vars->APPID_CONTROL_OBJECT = ctrl
2281             = dwg_find_table_control (dwg, "APPID_CONTROL");
2282     }
2283   // TODO ACAD_DSTYLE_DIM* are probably different handles
2284   else if (strEQc (table, "DIMSTYLE") || memBEGINc (table, "ACAD_DSTYLE_DIM"))
2285     {
2286       if (!(ctrl = vars->DIMSTYLE_CONTROL_OBJECT))
2287         vars->DIMSTYLE_CONTROL_OBJECT = ctrl
2288             = dwg_find_table_control (dwg, "DIMSTYLE_CONTROL");
2289     }
2290   else if (strEQc (table, "VX"))
2291     {
2292       if (!(ctrl = vars->VX_CONTROL_OBJECT))
2293         vars->VX_CONTROL_OBJECT = ctrl
2294             = dwg_find_table_control (dwg, "VX_CONTROL");
2295     }
2296   else if (strEQc (table, "GROUP"))
2297     {
2298       if (!(ctrl = vars->DICTIONARY_ACAD_GROUP))
2299         vars->DICTIONARY_ACAD_GROUP = ctrl
2300             = dwg_find_dictionary (dwg, "ACAD_GROUP");
2301     }
2302   else if (strEQc (table, "MLSTYLE") || strEQc (table, "MLINESTYLE"))
2303     {
2304       if (!(ctrl = vars->DICTIONARY_ACAD_MLINESTYLE))
2305         vars->DICTIONARY_ACAD_MLINESTYLE = ctrl
2306             = dwg_find_dictionary (dwg, "ACAD_MLINESTYLE");
2307     }
2308   else if (strEQc (table, "MLEADERSTYLE") || strEQc (table, "ACAD_MLEADERVER"))
2309     {
2310       ctrl = dwg_find_dictionary (dwg, "ACAD_MLEADERSTYLE");
2311     }
2312   else if (strEQc (table, "NAMED_OBJECT"))
2313     // The very first DICTIONARY 0.1.C with all the names
2314     {
2315       if (!(ctrl = vars->DICTIONARY_NAMED_OBJECT))
2316         vars->DICTIONARY_NAMED_OBJECT = ctrl
2317             = dwg_add_handleref (dwg, 3, 0xC, NULL);
2318     }
2319   else if (strEQc (table, "LAYOUT"))
2320     {
2321       if (!(ctrl = vars->DICTIONARY_LAYOUT))
2322         vars->DICTIONARY_LAYOUT = ctrl
2323             = dwg_find_dictionary (dwg, "ACAD_LAYOUT");
2324     }
2325   else if (strEQc (table, "PLOTSETTINGS"))
2326     {
2327       if (!(ctrl = vars->DICTIONARY_PLOTSETTINGS))
2328         vars->DICTIONARY_PLOTSETTINGS = ctrl
2329             = dwg_find_dictionary (dwg, "ACAD_PLOTSETTINGS");
2330     }
2331   else if (strEQc (table, "PLOTSTYLENAME"))
2332     {
2333       if (!(ctrl = vars->DICTIONARY_PLOTSTYLENAME))
2334         vars->DICTIONARY_PLOTSTYLENAME = ctrl
2335             = dwg_find_dictionary (dwg, "ACAD_PLOTSTYLENAME");
2336     }
2337   // TODO but maybe the mappers are different
2338   else if (strEQc (table, "MATERIAL")
2339            || memBEGINc (table, "ACAD_MATERIAL_MAPPER"))
2340     {
2341       if (!(ctrl = vars->DICTIONARY_MATERIAL))
2342         vars->DICTIONARY_MATERIAL = ctrl
2343             = dwg_find_dictionary (dwg, "ACAD_MATERIAL");
2344     }
2345   else if (strEQc (table, "COLOR"))
2346     {
2347       if (!(ctrl = vars->DICTIONARY_COLOR))
2348         vars->DICTIONARY_COLOR = ctrl
2349             = dwg_find_dictionary (dwg, "ACAD_COLOR");
2350     }
2351   else if (strEQc (table, "VISUALSTYLE"))
2352     {
2353       if (!(ctrl = vars->DICTIONARY_VISUALSTYLE))
2354         vars->DICTIONARY_VISUALSTYLE = ctrl
2355             = dwg_find_dictionary (dwg, "ACAD_VISUALSTYLE");
2356     }
2357   else if (strEQc (table, "LIGHTLIST"))
2358     {
2359       if (!(ctrl = vars->DICTIONARY_LIGHTLIST))
2360         vars->DICTIONARY_LIGHTLIST = ctrl
2361             = dwg_find_dictionary (dwg, "ACAD_LIGHTLIST");
2362     }
2363   else
2364     {
2365       LOG_ERROR ("dwg_ctrl_table: Unsupported table %s", table);
2366       return 0;
2367     }
2368   return ctrl;
2369 }
2370 
2371 // Search for name in associated table, and return its handle.
2372 // Note that newer tables, like MATERIAL are stored in a DICTIONARY instead.
2373 // Note that we cannot set the ref->obj here, as it may still move by realloc
2374 // dwg->object[]
2375 EXPORT BITCODE_H
dwg_find_tablehandle(Dwg_Data * restrict dwg,const char * restrict name,const char * restrict table)2376 dwg_find_tablehandle (Dwg_Data *restrict dwg, const char *restrict name,
2377                       const char *restrict table)
2378 {
2379   BITCODE_BL i, num_entries = 0;
2380   BITCODE_H ctrl = NULL, *hdlv = NULL;
2381   Dwg_Object *obj;
2382   Dwg_Object_APPID_CONTROL *_obj; // just some random generic type
2383   Dwg_Header_Variables *vars = &dwg->header_vars;
2384 
2385   if (!dwg || !name || !table)
2386     return NULL;
2387   // look for the _CONTROL table, and search for name in all entries
2388   ctrl = dwg_ctrl_table (dwg, table);
2389   if (strEQc (table, "LTYPE"))
2390     {
2391       if (strEQc (name, "BYLAYER") || strEQc (name, "ByLayer"))
2392         {
2393           if (vars->LTYPE_BYLAYER)
2394             return vars->LTYPE_BYLAYER;
2395         }
2396       else if (strEQc (name, "BYBLOCK") || strEQc (name, "ByBlock"))
2397         {
2398           if (vars->LTYPE_BYBLOCK)
2399             return vars->LTYPE_BYBLOCK;
2400         }
2401       else if (strEQc (name, "CONTINUOUS") || strEQc (name, "Continuous"))
2402         {
2403           if (vars->LTYPE_CONTINUOUS)
2404             return vars->LTYPE_CONTINUOUS;
2405         }
2406     }
2407   if (!ctrl)
2408     { // TODO: silently search table_control. header_vars can be empty
2409       LOG_TRACE ("dwg_find_tablehandle: Empty header_vars table %s\n", table);
2410       return NULL;
2411     }
2412   obj = dwg_resolve_handle (dwg, ctrl->absolute_ref);
2413   if (!obj)
2414     {
2415       LOG_TRACE ("dwg_find_tablehandle: Could not resolve table %s\n", table);
2416       return NULL;
2417     }
2418   if (obj->type == DWG_TYPE_DICTIONARY)
2419     return dwg_find_dicthandle_objname (dwg, ctrl, name);
2420   if (!dwg_obj_is_control (obj))
2421     {
2422       LOG_ERROR ("dwg_find_tablehandle: Could not resolve CONTROL object %s "
2423                  "for table %s",
2424                  obj->name, table);
2425       return NULL;
2426     }
2427   _obj = obj->tio.object->tio.APPID_CONTROL; // just random type
2428   dwg_dynapi_entity_value (_obj, obj->name, "num_entries", &num_entries, NULL);
2429   if (!num_entries)
2430     return NULL;
2431   dwg_dynapi_entity_value (_obj, obj->name, "entries", &hdlv, NULL);
2432   if (!hdlv)
2433     return NULL;
2434   for (i = 0; i < num_entries; i++)
2435     {
2436       char *hdlname;
2437       Dwg_Object *hobj;
2438       Dwg_Object_APPID *_o;
2439       int isnew = 0;
2440       bool ok;
2441 
2442       if (!hdlv[i])
2443         continue;
2444       hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2445       if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID)
2446         continue;
2447       _o = hobj->tio.object->tio.APPID;
2448       ok = dwg_dynapi_entity_utf8text (_o, hobj->name, "name", &hdlname, &isnew, NULL);
2449       LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2450                   hobj->name, hdlname ? hdlname : "NULL");
2451       if (ok && hdlname && (strEQ (name, hdlname) || !strcasecmp (name, hdlname)))
2452         {
2453           if (isnew)
2454             free (hdlname);
2455           return hdlv[i];
2456         }
2457       if (ok && isnew && hdlname)
2458         free (hdlname);
2459     }
2460 
2461   return NULL;
2462 }
2463 
2464 // Search for handle in associated table, and return its name. (as UTF-8)
2465 // Always returns a copy.
2466 EXPORT char *
dwg_handle_name(Dwg_Data * restrict dwg,const char * restrict table,const BITCODE_H restrict handle)2467 dwg_handle_name (Dwg_Data *restrict dwg, const char *restrict table,
2468                  const BITCODE_H restrict handle)
2469 {
2470   BITCODE_BL i, num_entries = 0;
2471   BITCODE_H ctrl = NULL, *hdlv = NULL;
2472   Dwg_Object *obj;
2473   Dwg_Object_APPID_CONTROL *_obj; // just some random generic type
2474   Dwg_Header_Variables *vars = &dwg->header_vars;
2475 
2476   if (!dwg || !table || !handle)
2477     return NULL;
2478   if (!handle->absolute_ref)
2479     return NULL;
2480   // look for the _CONTROL table, and search for name in all entries
2481   ctrl = dwg_ctrl_table (dwg, table);
2482   if (!ctrl)
2483     { // TODO: silently search table_control. header_vars can be empty
2484       LOG_TRACE ("dwg_handle_name: Empty header_vars table %s\n", table);
2485       return 0;
2486     }
2487   obj = dwg_resolve_handle (dwg, ctrl->absolute_ref);
2488   if (!obj)
2489     {
2490       LOG_TRACE ("dwg_handle_name: Could not resolve table %s\n", table);
2491       return 0;
2492     }
2493   //if (obj->type == DWG_TYPE_DICTIONARY)
2494   //  return dwg_find_dicthandle_objname (dwg, ctrl, name);
2495   if (!dwg_obj_is_control (obj))
2496     {
2497       LOG_ERROR ("dwg_handle_name: Could not resolve CONTROL object %s "
2498                  "for table %s",
2499                  obj->name, table);
2500       return 0;
2501     }
2502   _obj = obj->tio.object->tio.APPID_CONTROL; // just a random type
2503   dwg_dynapi_entity_value (_obj, obj->name, "num_entries", &num_entries, NULL);
2504   if (!num_entries)
2505     return NULL;
2506   dwg_dynapi_entity_value (_obj, obj->name, "entries", &hdlv, NULL);
2507   if (!hdlv)
2508     return NULL;
2509   for (i = 0; i < num_entries; i++)
2510     {
2511       char *hdlname, *name;
2512       Dwg_Object *hobj;
2513       Dwg_Object_APPID *_o;
2514       int isnew = 0;
2515       bool ok;
2516 
2517       if (!hdlv[i])
2518         continue;
2519       hobj = dwg_resolve_handle (dwg, hdlv[i]->absolute_ref);
2520       if (!hobj || !hobj->tio.object || !hobj->tio.object->tio.APPID)
2521         continue;
2522       if (hdlv[i]->absolute_ref != handle->absolute_ref)
2523         continue;
2524       _o = hobj->tio.object->tio.APPID;
2525       name = hobj->name;
2526       /* For BLOCK search the BLOCK entities instead.
2527          The BLOCK_HEADER has only the abbrevated name, but we want "*D30", not "*D" */
2528       if (strEQc (table, "BLOCK") && hobj->fixedtype == DWG_TYPE_BLOCK_HEADER)
2529         {
2530           Dwg_Object_BLOCK_HEADER *_bh = hobj->tio.object->tio.BLOCK_HEADER;
2531           Dwg_Object *bo = dwg_ref_object (dwg, _bh->block_entity);
2532           if (bo != NULL && bo->fixedtype == DWG_TYPE_BLOCK)
2533             {
2534               _o = (Dwg_Object_APPID *)bo->tio.entity->tio.BLOCK;
2535               name = bo->name;
2536             }
2537         }
2538       ok = dwg_dynapi_entity_utf8text (_o, name, "name", &hdlname, &isnew, NULL);
2539       LOG_HANDLE (" %s.%s[%d] => %s.name: %s\n", obj->name, "entries", i,
2540                   hobj->name, hdlname ? hdlname : "NULL");
2541       if (ok)
2542         {
2543           if (!isnew) return strdup (hdlname);
2544           else return hdlname;
2545         }
2546       else
2547         return NULL;
2548     }
2549   return NULL;
2550 }
2551 
2552 /* Returns the string value of the member of the AcDbVariableDictionary.
2553    NULL if not found.
2554    The name is ascii. E.g. LIGHTINGUNITS => "0" */
2555 EXPORT char *
dwg_variable_dict(Dwg_Data * restrict dwg,const char * restrict name)2556 dwg_variable_dict (Dwg_Data *restrict dwg, const char *restrict name)
2557 {
2558   static BITCODE_H var_dict = NULL;
2559   BITCODE_H var;
2560   Dwg_Object *obj;
2561   Dwg_Object_DICTIONARYVAR *_obj;
2562 
2563   if (!var_dict || dwg->dirty_refs)
2564     var_dict = dwg_find_dictionary (dwg, "AcDbVariableDictionary");
2565   if (!var_dict)
2566     return NULL;
2567   var = dwg_find_dicthandle (dwg, var_dict, name);
2568   if (!var)
2569     return NULL;
2570   obj = dwg_ref_object_silent (dwg, var);
2571   if (!obj || obj->fixedtype != DWG_TYPE_DICTIONARYVAR)
2572     return NULL;
2573   _obj = obj->tio.object->tio.DICTIONARYVAR;
2574   return _obj->strvalue;
2575 }
2576 
2577 static bool
xdata_string_match(Dwg_Data * restrict dwg,Dwg_Resbuf * restrict xdata,int type,char * restrict str)2578 xdata_string_match (Dwg_Data *restrict dwg, Dwg_Resbuf *restrict xdata,
2579                     int type, char *restrict str)
2580 {
2581   if (xdata->type != type)
2582     return 0;
2583   if (!IS_FROM_TU_DWG (dwg))
2584     {
2585       return strEQ (xdata->value.str.u.data, str);
2586     }
2587   else
2588     {
2589       return memcmp (xdata->value.str.u.wdata, str, xdata->value.str.size * 2) == 0;
2590     }
2591 }
2592 
2593 static bool
is_extnames_xrecord(Dwg_Data * restrict dwg,Dwg_Object * restrict xrec,Dwg_Object * restrict xdic)2594 is_extnames_xrecord (Dwg_Data *restrict dwg, Dwg_Object *restrict xrec,
2595                      Dwg_Object *restrict xdic)
2596 {
2597   const int16_t w[8] = { 'E', 'X', 'T', 'N', 'A', 'M', 'E', 'S' };
2598   const char *extnames = dwg->header.from_version < R_2007 ? "EXTNAMES" : (const char*)w;
2599 
2600   return (xrec
2601           && xdic
2602           && dwg
2603           && xrec->fixedtype == DWG_TYPE_XRECORD
2604           && xrec->tio.object->ownerhandle
2605           && xrec->tio.object->ownerhandle->absolute_ref
2606                == xdic->handle.value
2607           && xrec->tio.object->tio.XRECORD->num_xdata >= 2
2608           && xrec->tio.object->tio.XRECORD->xdata
2609           && xdata_string_match (dwg, xrec->tio.object->tio.XRECORD->xdata,
2610                                  102, (char *)extnames));
2611 }
2612 
2613 // Return a table EXTNAME or NULL. extnames only exist for r13-r14 dwgs
2614 EXPORT char*
dwg_find_table_extname(Dwg_Data * restrict dwg,Dwg_Object * restrict obj)2615 dwg_find_table_extname (Dwg_Data *restrict dwg, Dwg_Object *restrict obj)
2616 {
2617   char *name;
2618   Dwg_Object *xdic;
2619   Dwg_Object_DICTIONARY *_xdic;
2620   Dwg_Object_Ref *xdicref;
2621   Dwg_Object *xrec = NULL;
2622   Dwg_Object_XRECORD *_xrec;
2623   Dwg_Resbuf *xdata;
2624   BITCODE_BL i;
2625 
2626   // (GH #167) via DICTIONARY ACAD_XREC_ROUNDTRIP to XRECORD EXTNAMES
2627   if (!dwg_obj_is_table (obj))
2628     return NULL;
2629   // HACK: we can guarantee that the table name is always the first field. See
2630   // dwg_obj_table_get_name().
2631   name = obj->tio.object->tio.LAYER->name;
2632   xdicref = obj->tio.object->xdicobjhandle;
2633   if (!xdicref)
2634     return NULL;
2635   xdic = dwg_ref_object (dwg, xdicref);
2636   if (!xdic || xdic->type != DWG_TYPE_DICTIONARY)
2637     return NULL;
2638   _xdic = xdic->tio.object->tio.DICTIONARY;
2639   if (_xdic->numitems < 1 || !_xdic->texts[0])
2640     return NULL;
2641   if (xdic->tio.object->ownerhandle->absolute_ref != obj->handle.value)
2642     return NULL;
2643   for (i = 0; i < _xdic->numitems; i++)
2644     {
2645       if (strEQc (_xdic->texts[i], "ACAD_XREC_ROUNDTRIP"))
2646         break;
2647     }
2648   if (i == _xdic->numitems) // not found
2649     return NULL;
2650   xrec = dwg_ref_object (dwg, _xdic->itemhandles[i]);
2651   if (!xrec || !is_extnames_xrecord (dwg, xrec, xdic))
2652     return NULL;
2653 
2654   _xrec = xrec->tio.object->tio.XRECORD;
2655   xdata = _xrec->xdata;
2656   xdata = xdata->nextrb;
2657   if (xdata->type == 1) // pairs of 1: old name, 2: new name
2658     {
2659       // step to the matching name
2660     cmp:
2661       if (!xdata_string_match (dwg, xdata, 1, name))
2662         {
2663           xdata = xdata->nextrb;
2664           while (xdata && xdata->type != 1 && xdata->type != 102)
2665             xdata = xdata->nextrb;
2666           if (xdata)
2667             goto cmp;
2668         }
2669       if (!xdata)
2670         return NULL;
2671       xdata = xdata->nextrb;
2672       if (xdata->type == 2) // new name
2673         {
2674           if (!IS_FROM_TU_DWG (dwg))
2675             return xdata->value.str.u.data;
2676           else
2677             return (char *)xdata->value.str.u.wdata;
2678         }
2679     }
2680 
2681   return NULL;
2682 }
2683 
2684 static const Dwg_RGB_Palette rgb_palette[256] = {
2685   { 0x00, 0x00, 0x00 }, // 0
2686   { 0xFF, 0x00, 0x00 }, { 0xFF, 0xFF, 0x00 }, { 0x00, 0xFF, 0x00 },
2687   { 0x00, 0xFF, 0xFF }, { 0x00, 0x00, 0xFF }, // 5
2688   { 0xFF, 0x00, 0xFF }, { 0xFF, 0xFF, 0xFF }, { 0x41, 0x41, 0x41 },
2689   { 0x80, 0x80, 0x80 }, { 0xFF, 0x00, 0x00 }, // 10
2690   { 0xFF, 0xAA, 0xAA }, { 0xBD, 0x00, 0x00 }, { 0xBD, 0x7E, 0x7E },
2691   { 0x81, 0x00, 0x00 }, { 0x81, 0x56, 0x56 }, // 15
2692   { 0x68, 0x00, 0x00 }, { 0x68, 0x45, 0x45 }, { 0x4F, 0x00, 0x00 },
2693   { 0x4F, 0x35, 0x35 }, { 0xFF, 0x3F, 0x00 }, // 20
2694   { 0xFF, 0xBF, 0xAA }, { 0xBD, 0x2E, 0x00 }, { 0xBD, 0x8D, 0x7E },
2695   { 0x81, 0x1F, 0x00 }, { 0x81, 0x60, 0x56 }, // 25
2696   { 0x68, 0x19, 0x00 }, { 0x68, 0x4E, 0x45 }, { 0x4F, 0x13, 0x00 },
2697   { 0x4F, 0x3B, 0x35 }, { 0xFF, 0x7F, 0x00 }, // 30
2698   { 0xFF, 0xD4, 0xAA }, { 0xBD, 0x5E, 0x00 }, { 0xBD, 0x9D, 0x7E },
2699   { 0x81, 0x40, 0x00 }, { 0x81, 0x6B, 0x56 }, // 35
2700   { 0x68, 0x34, 0x00 }, { 0x68, 0x56, 0x45 }, { 0x4F, 0x27, 0x00 },
2701   { 0x4F, 0x42, 0x35 }, { 0xFF, 0xBF, 0x00 }, // 40
2702   { 0xFF, 0xEA, 0xAA }, { 0xBD, 0x8D, 0x00 }, { 0xBD, 0xAD, 0x7E },
2703   { 0x81, 0x60, 0x00 }, { 0x81, 0x76, 0x56 }, // 45
2704   { 0x68, 0x4E, 0x00 }, { 0x68, 0x5F, 0x45 }, { 0x4F, 0x3B, 0x00 },
2705   { 0x4F, 0x49, 0x35 }, { 0xFF, 0xFF, 0x00 }, // 50
2706   { 0xFF, 0xFF, 0xAA }, { 0xBD, 0xBD, 0x00 }, { 0xBD, 0xBD, 0x7E },
2707   { 0x81, 0x81, 0x00 }, { 0x81, 0x81, 0x56 }, // 55
2708   { 0x68, 0x68, 0x00 }, { 0x68, 0x68, 0x45 }, { 0x4F, 0x4F, 0x00 },
2709   { 0x4F, 0x4F, 0x35 }, { 0xBF, 0xFF, 0x00 }, // 60
2710   { 0xEA, 0xFF, 0xAA }, { 0x8D, 0xBD, 0x00 }, { 0xAD, 0xBD, 0x7E },
2711   { 0x60, 0x81, 0x00 }, { 0x76, 0x81, 0x56 }, // 65
2712   { 0x4E, 0x68, 0x00 }, { 0x5F, 0x68, 0x45 }, { 0x3B, 0x4F, 0x00 },
2713   { 0x49, 0x4F, 0x35 }, { 0x7F, 0xFF, 0x00 }, // 70
2714   { 0xD4, 0xFF, 0xAA }, { 0x5E, 0xBD, 0x00 }, { 0x9D, 0xBD, 0x7E },
2715   { 0x40, 0x81, 0x00 }, { 0x6B, 0x81, 0x56 }, // 75
2716   { 0x34, 0x68, 0x00 }, { 0x56, 0x68, 0x45 }, { 0x27, 0x4F, 0x00 },
2717   { 0x42, 0x4F, 0x35 }, { 0x3F, 0xFF, 0x00 }, // 80
2718   { 0xBF, 0xFF, 0xAA }, { 0x2E, 0xBD, 0x00 }, { 0x8D, 0xBD, 0x7E },
2719   { 0x1F, 0x81, 0x00 }, { 0x60, 0x81, 0x56 }, // 85
2720   { 0x19, 0x68, 0x00 }, { 0x4E, 0x68, 0x45 }, { 0x13, 0x4F, 0x00 },
2721   { 0x3B, 0x4F, 0x35 }, { 0x00, 0xFF, 0x00 }, // 90
2722   { 0xAA, 0xFF, 0xAA }, { 0x00, 0xBD, 0x00 }, { 0x7E, 0xBD, 0x7E },
2723   { 0x00, 0x81, 0x00 }, { 0x56, 0x81, 0x56 }, // 95
2724   { 0x00, 0x68, 0x00 }, { 0x45, 0x68, 0x45 }, { 0x00, 0x4F, 0x00 },
2725   { 0x35, 0x4F, 0x35 }, { 0x00, 0xFF, 0x3F }, // 100
2726   { 0xAA, 0xFF, 0xBF }, { 0x00, 0xBD, 0x2E }, { 0x7E, 0xBD, 0x8D },
2727   { 0x00, 0x81, 0x1F }, { 0x56, 0x81, 0x60 }, // 105
2728   { 0x00, 0x68, 0x19 }, { 0x45, 0x68, 0x4E }, { 0x00, 0x4F, 0x13 },
2729   { 0x35, 0x4F, 0x3B }, { 0x00, 0xFF, 0x7F }, // 110
2730   { 0xAA, 0xFF, 0xD4 }, { 0x00, 0xBD, 0x5E }, { 0x7E, 0xBD, 0x9D },
2731   { 0x00, 0x81, 0x40 }, { 0x56, 0x81, 0x6B }, // 115
2732   { 0x00, 0x68, 0x34 }, { 0x45, 0x68, 0x56 }, { 0x00, 0x4F, 0x27 },
2733   { 0x35, 0x4F, 0x42 }, { 0x00, 0xFF, 0xBF }, // 120
2734   { 0xAA, 0xFF, 0xEA }, { 0x00, 0xBD, 0x8D }, { 0x7E, 0xBD, 0xAD },
2735   { 0x00, 0x81, 0x60 }, { 0x56, 0x81, 0x76 }, // 125
2736   { 0x00, 0x68, 0x4E }, { 0x45, 0x68, 0x5F }, { 0x00, 0x4F, 0x3B },
2737   { 0x35, 0x4F, 0x49 }, { 0x00, 0xFF, 0xFF }, // 130
2738   { 0xAA, 0xFF, 0xFF }, { 0x00, 0xBD, 0xBD }, { 0x7E, 0xBD, 0xBD },
2739   { 0x00, 0x81, 0x81 }, { 0x56, 0x81, 0x81 }, // 135
2740   { 0x00, 0x68, 0x68 }, { 0x45, 0x68, 0x68 }, { 0x00, 0x4F, 0x4F },
2741   { 0x35, 0x4F, 0x4F }, { 0x00, 0xBF, 0xFF }, // 140
2742   { 0xAA, 0xEA, 0xFF }, { 0x00, 0x8D, 0xBD }, { 0x7E, 0xAD, 0xBD },
2743   { 0x00, 0x60, 0x81 }, { 0x56, 0x76, 0x81 }, // 145
2744   { 0x00, 0x4E, 0x68 }, { 0x45, 0x5F, 0x68 }, { 0x00, 0x3B, 0x4F },
2745   { 0x35, 0x49, 0x4F }, { 0x00, 0x7F, 0xFF }, // 150
2746   { 0xAA, 0xD4, 0xFF }, { 0x00, 0x5E, 0xBD }, { 0x7E, 0x9D, 0xBD },
2747   { 0x00, 0x40, 0x81 }, { 0x56, 0x6B, 0x81 }, // 155
2748   { 0x00, 0x34, 0x68 }, { 0x45, 0x56, 0x68 }, { 0x00, 0x27, 0x4F },
2749   { 0x35, 0x42, 0x4F }, { 0x00, 0x3F, 0xFF }, // 160
2750   { 0xAA, 0xBF, 0xFF }, { 0x00, 0x2E, 0xBD }, { 0x7E, 0x8D, 0xBD },
2751   { 0x00, 0x1F, 0x81 }, { 0x56, 0x60, 0x81 }, // 165
2752   { 0x00, 0x19, 0x68 }, { 0x45, 0x4E, 0x68 }, { 0x00, 0x13, 0x4F },
2753   { 0x35, 0x3B, 0x4F }, { 0x00, 0x00, 0xFF }, // 170
2754   { 0xAA, 0xAA, 0xFF }, { 0x00, 0x00, 0xBD }, { 0x7E, 0x7E, 0xBD },
2755   { 0x00, 0x00, 0x81 }, { 0x56, 0x56, 0x81 }, // 175
2756   { 0x00, 0x00, 0x68 }, { 0x45, 0x45, 0x68 }, { 0x00, 0x00, 0x4F },
2757   { 0x35, 0x35, 0x4F }, { 0x3F, 0x00, 0xFF }, // 180
2758   { 0xBF, 0xAA, 0xFF }, { 0x2E, 0x00, 0xBD }, { 0x8D, 0x7E, 0xBD },
2759   { 0x1F, 0x00, 0x81 }, { 0x60, 0x56, 0x81 }, // 185
2760   { 0x19, 0x00, 0x68 }, { 0x4E, 0x45, 0x68 }, { 0x13, 0x00, 0x4F },
2761   { 0x3B, 0x35, 0x4F }, { 0x7F, 0x00, 0xFF }, // 190
2762   { 0xD4, 0xAA, 0xFF }, { 0x5E, 0x00, 0xBD }, { 0x9D, 0x7E, 0xBD },
2763   { 0x40, 0x00, 0x81 }, { 0x6B, 0x56, 0x81 }, // 195
2764   { 0x34, 0x00, 0x68 }, { 0x56, 0x45, 0x68 }, { 0x27, 0x00, 0x4F },
2765   { 0x42, 0x35, 0x4F }, { 0xBF, 0x00, 0xFF }, // 200
2766   { 0xEA, 0xAA, 0xFF }, { 0x8D, 0x00, 0xBD }, { 0xAD, 0x7E, 0xBD },
2767   { 0x60, 0x00, 0x81 }, { 0x76, 0x56, 0x81 }, // 205
2768   { 0x4E, 0x00, 0x68 }, { 0x5F, 0x45, 0x68 }, { 0x3B, 0x00, 0x4F },
2769   { 0x49, 0x35, 0x4F }, { 0xFF, 0x00, 0xFF }, // 210
2770   { 0xFF, 0xAA, 0xFF }, { 0xBD, 0x00, 0xBD }, { 0xBD, 0x7E, 0xBD },
2771   { 0x81, 0x00, 0x81 }, { 0x81, 0x56, 0x81 }, // 215
2772   { 0x68, 0x00, 0x68 }, { 0x68, 0x45, 0x68 }, { 0x4F, 0x00, 0x4F },
2773   { 0x4F, 0x35, 0x4F }, { 0xFF, 0x00, 0xBF }, // 220
2774   { 0xFF, 0xAA, 0xEA }, { 0xBD, 0x00, 0x8D }, { 0xBD, 0x7E, 0xAD },
2775   { 0x81, 0x00, 0x60 }, { 0x81, 0x56, 0x76 }, // 225
2776   { 0x68, 0x00, 0x4E }, { 0x68, 0x45, 0x5F }, { 0x4F, 0x00, 0x3B },
2777   { 0x4F, 0x35, 0x49 }, { 0xFF, 0x00, 0x7F }, // 230
2778   { 0xFF, 0xAA, 0xD4 }, { 0xBD, 0x00, 0x5E }, { 0xBD, 0x7E, 0x9D },
2779   { 0x81, 0x00, 0x40 }, { 0x81, 0x56, 0x6B }, // 235
2780   { 0x68, 0x00, 0x34 }, { 0x68, 0x45, 0x56 }, { 0x4F, 0x00, 0x27 },
2781   { 0x4F, 0x35, 0x42 }, { 0xFF, 0x00, 0x3F }, // 240
2782   { 0xFF, 0xAA, 0xBF }, { 0xBD, 0x00, 0x2E }, { 0xBD, 0x7E, 0x8D },
2783   { 0x81, 0x00, 0x1F }, { 0x81, 0x56, 0x60 }, // 245
2784   { 0x68, 0x00, 0x19 }, { 0x68, 0x45, 0x4E }, { 0x4F, 0x00, 0x13 },
2785   { 0x4F, 0x35, 0x3B }, { 0x33, 0x33, 0x33 }, // 250
2786   { 0x50, 0x50, 0x50 }, { 0x69, 0x69, 0x69 }, { 0x82, 0x82, 0x82 },
2787   { 0xBE, 0xBE, 0xBE }, { 0xFF, 0xFF, 0xFF } // 255
2788 };
2789 
dwg_rgb_palette(void)2790 EXPORT const Dwg_RGB_Palette *dwg_rgb_palette (void)
2791 {
2792   return rgb_palette;
2793 }
2794 
dwg_rgb_palette_index(BITCODE_BS index)2795 EXPORT BITCODE_BL dwg_rgb_palette_index (BITCODE_BS index)
2796 {
2797   // BS in unsigned
2798   if (index >= 256)
2799     return 0;
2800   else
2801     {
2802       Dwg_RGB_Palette rgb;
2803       assert (index < 256);
2804       rgb = rgb_palette[index];
2805       return (rgb.r << 16) | (rgb.g << 8) | rgb.b;
2806     }
2807 }
2808 
dwg_find_color_index(BITCODE_BL rgb)2809 EXPORT BITCODE_BS dwg_find_color_index (BITCODE_BL rgb)
2810 {
2811   Dwg_RGB_Palette pal;
2812   rgb &= 0x00ffffff;
2813   pal.r = rgb & 0xff0000;
2814   pal.g = rgb & 0x00ff00;
2815   pal.b = rgb & 0x0000ff;
2816   // linear search is good enough for 256. the palette is unsorted.
2817   for (BITCODE_BS i = 0; i < 256; i++)
2818     {
2819       if (memcmp (&pal, &rgb_palette[i], 3) == 0)
2820         return i;
2821     }
2822   return 256;
2823 }
2824 
2825 // map [rVER] to our enum number, not the dwg->header.dwgversion
2826 // Acad 2018 offers SaveAs DWG: 2018,2013,2010,2007,2004,2004,2000,r14
2827 //                         DXF: 2018,2013,2010,2007,2004,2004,2000,r12
2828 // libdxfrw dwg2dxf offers R12, v2000, v2004, v2007, v2010
2829 EXPORT Dwg_Version_Type
dwg_version_as(const char * version)2830 dwg_version_as (const char *version)
2831 {
2832   if (strEQc (version, "r2000"))
2833     return R_2000;
2834   else if (strEQc (version, "r2004"))
2835     return R_2004;
2836   else if (strEQc (version, "r2007"))
2837     return R_2007;
2838   else if (strEQc (version, "r2010"))
2839     return R_2010;
2840   else if (strEQc (version, "r2013"))
2841     return R_2013;
2842   else if (strEQc (version, "r2018"))
2843     return R_2018;
2844   else if (strEQc (version, "r14"))
2845     return R_14;
2846   else if (strEQc (version, "r13"))
2847     return R_13;
2848   else if (strEQc (version, "r13c3"))
2849     return R_13c3;
2850   else if (strEQc (version, "r11") || strEQc (version, "r12"))
2851     return R_11;
2852   else if (strEQc (version, "r10"))
2853     return R_10;
2854   else if (strEQc (version, "r9"))
2855     return R_9;
2856   else if (strEQc (version, "r2.6"))
2857     return R_2_6;
2858   else if (strEQc (version, "r2.5"))
2859     return R_2_5;
2860   else if (strEQc (version, "r2.4"))
2861     return R_2_4;
2862   else if (strEQc (version, "r2.1"))
2863     return R_2_1;
2864   else if (strEQc (version, "r2.0"))
2865     return R_2_0;
2866   else if (strEQc (version, "r1.4"))
2867     return R_1_4;
2868   else if (strEQc (version, "r1.3"))
2869     return R_1_3;
2870   else if (strEQc (version, "r1.2"))
2871     return R_1_2;
2872   else if (strEQc (version, "r1.1"))
2873     return R_1_1;
2874   else
2875     return R_INVALID;
2876 }
2877 
2878 /** The reverse of dwg_version_as(char*) */
2879 const char *
dwg_version_type(const Dwg_Version_Type version)2880 dwg_version_type (const Dwg_Version_Type version)
2881 {
2882   switch (version)
2883     {
2884     case R_INVALID:
2885       return "invalid version";
2886     case R_1_1:
2887       return "r1.1";
2888     case R_1_2:
2889       return "r1.2";
2890     case R_1_3:
2891       return "r1.3";
2892     case R_1_4:
2893       return "r1.4";
2894     case R_1_402b:
2895       return "r1.402b";
2896     case R_2_0:
2897       return "r2.0";
2898     case R_2_1:
2899       return "r2.1";
2900     case R_2_21:
2901       return "r2.21";
2902     case R_2_22:
2903       return "r2.22";
2904     case R_2_4:
2905       return "r2.4";
2906     case R_2_5:
2907       return "r2.5";
2908     case R_2_6:
2909       return "r2.6";
2910     case R_9:
2911       return "r9";
2912     case R_9c1:
2913       return "r9c1";
2914     case R_10:
2915       return "r10";
2916     case R_10c1:
2917       return "r10c1";
2918     case R_10c2:
2919       return "r10c2";
2920     case R_11:
2921       return "r11";
2922     case R_12:
2923       return "r12";
2924     case R_12c1:
2925       return "r12c1";
2926     case R_13:
2927       return "r13";
2928     case R_13c3:
2929       return "r13c3";
2930     case R_14:
2931       return "r14";
2932     case R_2000:
2933       return "r2000";
2934     case R_2004:
2935       return "r2004";
2936     case R_2007:
2937       return "r2007";
2938     case R_2010:
2939       return "r2010";
2940     case R_2013:
2941       return "r2013";
2942     case R_2018:
2943       return "r2018";
2944     case R_AFTER:
2945       return "invalid after";
2946     default:
2947       return "";
2948     }
2949 }
2950 
2951 // print errors as string to stderr
2952 EXPORT void
dwg_errstrings(int error)2953 dwg_errstrings (int error)
2954 {
2955   if (error & 1)
2956     HANDLER (OUTPUT, "WRONGCRC ");
2957   if (error & 2)
2958     HANDLER (OUTPUT, "NOTYETSUPPORTED ");
2959   if (error & 4)
2960     HANDLER (OUTPUT, "UNHANDLEDCLASS ");
2961   if (error & 8)
2962     HANDLER (OUTPUT, "INVALIDTYPE ");
2963   if (error & 16)
2964     HANDLER (OUTPUT, "INVALIDHANDLE ");
2965   if (error & 32)
2966     HANDLER (OUTPUT, "INVALIDEED ");
2967   if (error & 64)
2968     HANDLER (OUTPUT, "VALUEOUTOFBOUNDS ");
2969   // -- critical --
2970   if (error > 127)
2971     HANDLER (OUTPUT, "\nCritical: ");
2972   if (error & 128)
2973     HANDLER (OUTPUT, "CLASSESNOTFOUND ");
2974   if (error & 256)
2975     HANDLER (OUTPUT, "SECTIONNOTFOUND ");
2976   if (error & 512)
2977     HANDLER (OUTPUT, "PAGENOTFOUND ");
2978   if (error & 1024)
2979     HANDLER (OUTPUT, "INTERNALERROR ");
2980   if (error & 2048)
2981     HANDLER (OUTPUT, "INVALIDDWG ");
2982   if (error & 4096)
2983     HANDLER (OUTPUT, "IOERROR ");
2984   if (error & 8192)
2985     HANDLER (OUTPUT, "OUTOFMEM ");
2986   HANDLER (OUTPUT, "\n");
2987 }
2988 
2989 // return name of color.method
2990 EXPORT const char*
dwg_color_method_name(unsigned m)2991 dwg_color_method_name (unsigned m)
2992 {
2993   switch (m) {
2994   case 0xc0: return "ByLayer";
2995   case 0xc1: return "ByBlock";
2996   case 0xc2: return "entity (default)";
2997   case 0xc3: return "Truecolor";
2998   case 0xc5: return "Foreground";
2999   case 0xc8: return "none";
3000   default: return "";
3001   }
3002 }
3003 
3004 EXPORT unsigned long
dwg_next_handle(const Dwg_Data * dwg)3005 dwg_next_handle (const Dwg_Data *dwg)
3006 {
3007   BITCODE_H last_hdl;
3008   unsigned long seed = 0;
3009   // check the object map for the next available handle
3010   last_hdl = dwg->num_object_refs ? dwg->object_ref[ dwg->num_object_refs - 1] : NULL;
3011   if (last_hdl)
3012     {
3013       // find the largest handle
3014       seed = last_hdl->absolute_ref;
3015       //LOG_TRACE ("compute HANDSEED %lu ", seed);
3016       for (unsigned i = 0; i < dwg->num_object_refs; i++)
3017         {
3018           Dwg_Object_Ref *ref = dwg->object_ref[i];
3019           if (ref->absolute_ref > seed)
3020             seed = ref->absolute_ref;
3021         }
3022       return seed + 1;
3023     }
3024   else
3025     {
3026       Dwg_Object *obj = &dwg->object[dwg->num_objects - 1];
3027       return obj->handle.value + 1;
3028     }
3029 }
3030 
3031 // on init some handles have holes on purpose.
3032 void dwg_set_next_hdl (Dwg_Data *dwg, const unsigned long value);
3033 void dwg_set_next_objhandle (Dwg_Object *obj);
3034 
dwg_set_next_hdl(Dwg_Data * dwg,const unsigned long value)3035 void dwg_set_next_hdl (Dwg_Data *dwg, const unsigned long value)
3036 {
3037   dwg->next_hdl = value;
3038 }
3039 
dwg_set_next_objhandle(Dwg_Object * obj)3040 void dwg_set_next_objhandle (Dwg_Object *obj)
3041 {
3042   Dwg_Data *dwg = obj->parent;
3043   if (!dwg->object_map)
3044     dwg->object_map = hash_new (200);
3045   if (dwg->next_hdl)
3046     {
3047       obj->handle.value = dwg->next_hdl;
3048       set_handle_size (&obj->handle);
3049       hash_set (dwg->object_map, obj->handle.value, (uint32_t)obj->index);
3050       dwg->next_hdl = 0;
3051       return;
3052     }
3053   if (!dwg->num_objects)
3054     {
3055       obj->handle.size = 1;
3056       obj->handle.value = 1UL;
3057     }
3058   else
3059     {
3060       Dwg_Object *lastobj = &dwg->object[dwg->num_objects - 1];
3061       /* ADD_OBJECT might have just added a zeroed object */
3062       if (!lastobj->handle.value && dwg->num_objects > 1)
3063         lastobj = &dwg->object[dwg->num_objects - 2];
3064       obj->handle.value = lastobj->handle.value + 1;
3065       set_handle_size (&obj->handle);
3066     }
3067   hash_set (dwg->object_map, obj->handle.value, (uint32_t)obj->index);
3068   dwg->next_hdl = 0;
3069   return;
3070 }
3071 
3072 // <path-to>/dxf.ext => copy of "dxf", "ext"
3073 // returns a malloc'ed copy of basename without extension, and
3074 // sets ext to the char behind the last "." of basename
split_filepath(const char * filepath,char ** extp)3075 char *split_filepath (const char *filepath, char **extp)
3076 {
3077   char *copy, *base, *dot;
3078   //int len;
3079   if (!filepath)
3080     return NULL;
3081   copy = strdup (filepath);
3082 #ifdef HAVE_BASENAME
3083   base = basename (copy);
3084 #else
3085   base = strrchr (copy, '/');
3086 #endif
3087   if (!base)
3088     base = copy;
3089   //len = strlen (base);
3090   if ((dot = strrchr (base, '.')) && *dot)
3091     {
3092       *extp = dot + 1;
3093       *dot = '\0';
3094     }
3095   return base;
3096 }
3097