FreeBASIC  0.91.0
objinfo.bas
Go to the documentation of this file.
1 ''
2 '' FB compile time information section (.fbctinf) reader
3 ''
4 ''
5 '' Old format (supported for backwards compatibility with FB <= 0.24):
6 ''
7 '' The first byte of the .fbctinf section is the version number &h10.
8 ''
9 '' Multiple data sections can be following behind it:
10 ''
11 '' Each section starts with 1 byte holding one of the FB_INFOSEC_* values,
12 '' describing the content of the section.
13 '' FB_INFOSEC_LIB = library names, from -l or #inclib
14 '' FB_INFOSEC_PTH = library search paths, from -p or #libpath
15 '' FB_INFOSEC_CMD = -lang mode and/or -mt setting
16 ''
17 '' The strings for FB_INFOSEC_LIB and FB_INFOSEC_PTH are stored like this:
18 '' 1 byte = string length without null terminator
19 '' followed by <raw string with null terminator>
20 ''
21 '' In FB_INFOSEC_CMD, there can be
22 '' -lang:
23 '' 1 byte = 5 = len( "-lang" )
24 '' "-lang" with null terminator
25 '' "qb"|"deprecated"|"fblite"|"fb" with null terminator
26 '' (strings encoded much like described above, but no length byte
27 '' in front of the dialect name)
28 '' -mt:
29 '' 1 byte = 3 = len( "-mt" )
30 '' "-mt" with null terminator
31 '' (string encoded like described above)
32 ''
33 '' Each of these 3 sections is terminated with a 0 byte.
34 ''
35 '' Finally, there's 1 byte holding FB_INFOSEC_EOL (= 0).
36 ''
37 ''
38 '' New format:
39 ''
40 '' The .fbctinf section's content is a string similar to the fbc command line,
41 '' except the strings (options/arguments) are each null-terminated on their own,
42 '' instead of being separated with spaces and only having a null at the end.
43 '' This prevents us from having to worry about escaping any special chars.
44 ''
45 '' For example:
46 '' -l\0mylib\0-p\0mylibpath\0-mt\0-lang\0qb\0
47 ''
48 '' The following "entries" can be encoded:
49 '' -l followed by a library name
50 '' -p followed by a library search path
51 '' -mt this can be included or left out (boolean)
52 '' -lang followed by the -lang mode (fb/fblite/qb/...) used for this object
53 ''
54 '' Technically it's ok for all entries to appear multiple times,
55 '' although it only makes sense for -l and -p.
56 ''
57 ''
58 '' The FB backends can add that section containing the data to the output
59 '' files they generate. There is no unified writer interface at the moment,
60 '' since it's different for each backend, that's why it's best to keep the
61 '' format simple.
62 ''
63 ''
64 '' The fbc frontend uses the reading interface to extract the objinfo data
65 '' from the .fbctinf sections of object files it's going to link together.
66 ''
67 '' objinfoReadObj():
68 '' reads in an .o file,
69 '' then calls hLoadFbctinfFromObj().
70 ''
71 '' objinfoReadLibfile():
72 '' reads in a lib*.a archive file,
73 '' looks for the fbctinf object file added to static libraries by fbc -lib,
74 '' and if found, calls hLoadFbctinfFromObj().
75 ''
76 '' objinfoReadLib():
77 '' searches a libfile for the given libname in the given libpaths,
78 '' then calls objinfoReadLibfile().
79 ''
80 '' hLoadFbctinfFromObj():
81 '' reads the currently loaded object file,
82 '' using either the COFF (Win32, DOS) or ELF32 (Linux, *BSD) format.
83 '' looks for the .fbctinf section,
84 '' and if found, parses its content, and tells the frontend about the
85 '' found libraries etc. by using the callbacks.
86 ''
87 
88 #include once "objinfo.bi"
89 #include once "fb.bi"
90 #include once "hlp.bi"
91 
92 '#define DEBUG_OBJINFO
93 
94 #ifdef DEBUG_OBJINFO
95  #define INFO( s ) print "objinfo: " + s
96 #else
97  #define INFO( s )
98 #endif
99 
100 type DATABUFFER
101  p as ubyte ptr
102  size as integer
103 end type
104 
105 dim shared as DATABUFFER _
106  ardata, _ '' current .a file content (if any)
107  objdata, _ '' current .o file content (can point into ardata)
108  fbctinf '' .fbctinf section content, points into objdata
109 
110 enum
111  FB_INFOSEC_EOL = 0
115 end enum
116 
117 type OBJINFOPARSERCTX
118  i as integer
119  filename as string
120  is_old as integer '' old .fbctinf format?
121  old_section as integer '' FB_INFOSEC_* or -1
122 end type
123 
124 dim shared as OBJINFOPARSERCTX parser
125 
126 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
127 '' archive/object reading code
128 
129 dim shared as zstring * 9 fbctinfname = ".fbctinf"
130 
131 '' ELF main headers
132 type ELF32_H field = 1
133  e_ident(0 to 15) as ubyte
134  e_type as ushort
135  e_machine as ushort
136  e_version as ulong
137  e_entry as ulong
138  e_phoff as ulong
139  e_shoff as ulong
140  e_flags as ulong
141  e_ehsize as ushort
142  e_phentsize as ushort
143  e_phnum as ushort
144  e_shentsize as ushort
145  e_shnum as ushort
146  e_shstrndx as ushort
147 end type
148 type ELF64_H field = 1
149  e_ident(0 to 15) as ubyte
150  e_type as ushort
151  e_machine as ushort
152  e_version as ulong
153  e_entry as ulongint
154  e_phoff as ulongint
155  e_shoff as ulongint
156  e_flags as ulong
157  e_ehsize as ushort
158  e_phentsize as ushort
159  e_phnum as ushort
160  e_shentsize as ushort
161  e_shnum as ushort
162  e_shstrndx as ushort
163 end type
164 
165 dim shared as ubyte elfmagic(0 to 15) = _
166 { _
167  &h7f, &h45, &h4c, &h46, 0, &h01, _ '' index 4 is set to 1 (32bit) or 2 (64bit)
168  &h01, &h00, &h00, &h00, &h00, &h00 _
169 }
170 
171 const ET_REL = 1
172 const EM_386 = 3
173 const EM_X86_64 = 62
174 
175 '' ELF section headers
176 type ELF32_SH field = 1
177  sh_name as ulong
178  sh_type as ulong
179  sh_flags as ulong
180  sh_addr as ulong
181  sh_offset as ulong
182  sh_size as ulong
183  sh_link as ulong
184  sh_info as ulong
185  sh_addralign as ulong
186  sh_entsize as ulong
187 end type
188 type ELF64_SH field = 1
189  sh_name as ulong
190  sh_type as ulong
191  sh_flags as ulongint
192  sh_addr as ulongint
193  sh_offset as ulongint
194  sh_size as ulongint
195  sh_link as ulong
196  sh_info as ulong
197  sh_addralign as ulongint
198  sh_entsize as ulongint
199 end type
200 
201 '' Template for ELF32/64 loading code, which can be shared at the source level
202 '' with almost no differences. Only the typenames need to be re-#defined, to
203 '' make this code use either the 32bit or 64bit ELF headers (which have
204 '' different field offsets and sizes), and there are a few constant values that
205 '' are different too.
206 
207 #macro ELFLOADINGCODE(ELF_H, ELF_SH, ELF_MAGIC_4, ELF_MACHINE)
208 
209 private function hCheck##ELF_SH _
210  ( _
211  byval h as ELF_H ptr, _
212  byval index as integer _
213  ) as ELF_SH ptr
214 
215  dim as ELF_SH ptr sh = any
216  dim as integer headeroffset = any
217 
218  headeroffset = h->e_shoff + (index * sizeof( ELF_SH ))
219 
220  '' Enough room for the header?
221  if( (culngint( headeroffset ) + sizeof( ELF_SH )) > objdata.size ) then
222  INFO( "elf: no room for section header" )
223  exit function
224  end if
225 
226  sh = cptr( any ptr, objdata.p ) + headeroffset
227 
228  '' Verify the sh_offset/sh_size fields
229  if( (culngint( sh->sh_offset ) + sh->sh_size) > objdata.size ) then
230  INFO( "elf: invalid sh_offset/sh_size fields" )
231  exit function
232  end if
233 
234  function = sh
235 end function
236 
237 private function hGetSectionName##ELF_SH _
238  ( _
239  byval h as ELF_H ptr, _
240  byval index as integer, _
241  byval nametb as integer _
242  ) as zstring ptr
243 
244  const MAXNAMELEN = 32
245  static as zstring * MAXNAMELEN+1 sectionname
246 
247  dim as ELF_SH ptr sh = any
248  dim as integer i = any, j = any, ch = any
249 
250  sh = hCheck##ELF_SH( h, index )
251  if( sh = NULL ) then
252  exit function
253  end if
254 
255  '' null-terminated string starting at the given name offset relative to
256  '' the beginning of the name tb
257  i = nametb + sh->sh_name
258  j = 0
259  while( (i < objdata.size) and (j < MAXNAMELEN) )
260  ch = objdata.p[i]
261  if( ch = 0 ) then
262  exit while
263  end if
264  sectionname[j] = ch
265  i += 1
266  j += 1
267  wend
268  sectionname[j] = 0
269 
270  function = @sectionname
271 end function
272 
273 private sub hLoadFbctinfFrom##ELF_H( )
274  dim as ELF_H ptr h = any
275  dim as ELF_SH ptr sh = any, nametb = any
276  dim as zstring ptr sectionname = any
277 
278  fbctinf.p = NULL
279  fbctinf.size = 0
280 
281  if( objdata.size < sizeof( ELF_H ) ) then
282  INFO( "elf: no room for main header" )
283  exit sub
284  end if
285 
286  h = cptr( any ptr, objdata.p )
287 
288  elfmagic(4) = ELF_MAGIC_4
289  for i as integer = 0 to 15
290  if( h->e_ident(i) <> elfmagic(i) ) then
291  INFO( "elf: magic mismatch" )
292  exit sub
293  end if
294  next
295 
296  '' matching header size?
297  if( h->e_ehsize <> sizeof( ELF_H ) ) then
298  INFO( "elf: header size mismatch" )
299  exit sub
300  end if
301 
302  '' relocatable .o?
303  if( h->e_type <> ET_REL ) then
304  INFO( "elf: not a relocatable .o" )
305  exit sub
306  end if
307 
308  '' x86/x86_64?
309  if( h->e_machine <> ELF_MACHINE ) then
310  INFO( "elf: machine mismatch" )
311  exit sub
312  end if
313 
314  '' section header tb entry size
315  if( h->e_shentsize <> sizeof( ELF_SH ) ) then
316  INFO( "elf: invalid e_shentsize" )
317  exit sub
318  end if
319 
320  '' number of section headers
321  if( (culngint( h->e_shnum ) * sizeof( ELF_SH )) > objdata.size ) then
322  INFO( "elf: invalid e_shnum" )
323  exit sub
324  end if
325 
326  '' index of .shstrtab's section header
327  if( (h->e_shstrndx < 0) or (h->e_shstrndx >= h->e_shnum) ) then
328  INFO( "elf: invalid e_shstrndx" )
329  exit sub
330  end if
331 
332  '' section header tb file offset
333  if( (culngint( h->e_shoff ) + (h->e_shnum * sizeof( ELF_SH ))) > objdata.size ) then
334  INFO( "elf: invalid e_shoff" )
335  exit sub
336  end if
337 
338  '' Look up the section header for .shstrtab (index in section header tb
339  '' is given as head->e_shstrndx), and find the offset to the section's content.
340  nametb = hCheck##ELF_SH( h, h->e_shstrndx )
341  if( nametb = NULL ) then
342  INFO( "elf: can't read string table" )
343  exit sub
344  end if
345 
346  '' Look up section names (relies on knowing the .shstrtab data, the section name tb)
347  '' Starting at section header 1 because 0 always is an empty (NULL) section header
348  for i as integer = 1 to h->e_shnum - 1
349  sectionname = hGetSectionName##ELF_SH( h, i, nametb->sh_offset )
350  if( sectionname ) then
351  INFO( "elf: seeing section '" + *sectionname + "'" )
352  if( *sectionname = fbctinfname ) then
353  sh = hCheck##ELF_SH( h, i )
354  if( sh ) then
355  fbctinf.p = objdata.p + sh->sh_offset
356  fbctinf.size = sh->sh_size
357  exit for
358  end if
359  end if
360  end if
361  next
362 end sub
363 
364 #endmacro
365 
366 ELFLOADINGCODE( ELF32_H, ELF32_SH, 1, EM_386 )
367 ELFLOADINGCODE( ELF64_H, ELF64_SH, 2, EM_X86_64 )
368 
369 '' COFF main header
370 type COFF_H field = 1
371  magic as ushort '' magic number (type of target machine)
372  seccount as ushort '' number of sections/entries in section tb
373  '' (which follows behind the headers)
374  timestamp as ulong '' creation time (time_t)
375  symtboffset as ulong '' file offset of symbol table
376  symcount as ulong '' entries in the symtb
377  optheadsize as ushort '' size of optional headers; 0 in obj file
378  flags as ushort
379 end type
380 
381 '' COFF section header
382 type COFF_SH field = 1
383  name(0 to 7) as ubyte '' section name
384  paddr as ulong '' physical address
385  vaddr as ulong '' virtual address
386  size as ulong '' section size
387  dataoffset as ulong '' offset to section data
388  reloffset as ulong '' offset to relocation data
389  lnoffset as ulong '' offset to line number data
390  relcount as ushort '' number of relocation entries
391  lncount as ushort '' number of line number entries
392  flags as ulong
393 end type
394 
395 sub hLoadFbctinfFromCOFF( byval magic as ushort )
396  dim as COFF_H ptr h = any
397  dim as COFF_SH ptr sh = any, shbase = any
398 
399  fbctinf.p = NULL
400  fbctinf.size = 0
401 
402  if( objdata.size < sizeof( COFF_H ) ) then
403  INFO( "coff: no room for main header" )
404  exit sub
405  end if
406 
407  h = cptr( any ptr, objdata.p )
408 
409  '' COFF magic
410  if( h->magic <> magic ) then
411  INFO( "coff: magic mismatch" )
412  exit sub
413  end if
414 
415  '' Should be 0 for relocatable obj file
416  if( h->optheadsize <> 0 ) then
417  INFO( "coff: optheadsize field <> 0" )
418  exit sub
419  end if
420 
421  '' Enough room for whole section header table?
422  if( (culngint( h->seccount ) * sizeof( COFF_SH )) > objdata.size ) then
423  INFO( "coff: no room for section header table" )
424  exit sub
425  end if
426 
427  shbase = cptr( any ptr, h ) + sizeof( COFF_H )
428 
429  for i as integer = 0 to (h->seccount - 1)
430  sh = shbase + i
431 
432  #ifdef DEBUG_OBJINFO
433  dim temp as zstring * 9
434  for j as integer = 0 to 7
435  temp[j] = sh->name(j)
436  next
437  INFO( "coff: seeing section '" + temp + "'" )
438  #endif
439 
440  '' The section name can hold 8 chars. Unused chars should be
441  '' padded with nulls. If it takes up all 8 chars, there's no
442  '' null padding at the end. Since ".fbctinf" takes up all 8
443  '' chars we can simply compare each char 1 by 1.
444  for j as integer = 0 to 7
445  if( sh->name(j) <> fbctinfname[j] ) then
446  continue for, for
447  end if
448  next
449 
450  if( (culngint( sh->dataoffset ) + sh->size) > objdata.size ) then
451  INFO( "coff: invalid section header data offset" )
452  exit sub
453  end if
454 
455  fbctinf.p = objdata.p + sh->dataoffset
456  fbctinf.size = sh->size
457  exit for
458  next
459 end sub
460 
461 '' .a archive entry header
462 type AR_H field = 1
463  '' (all values right-padded with ASCII spaces)
464  name(0 to 15) as ubyte '' ASCII
465  modifytime(0 to 11) as ubyte '' decimal
466  ownerid(0 to 5) as ubyte '' decimal
467  groupid(0 to 5) as ubyte '' decimal
468  mode(0 to 7) as ubyte '' octal
469  size(0 to 9) as ubyte '' decimal
470  magic(0 to 1) as ubyte '' &h60 &h0A
471 end type
472 
473 dim shared as ubyte armagic(0 to 7) = _
474 { _
475  asc( "!" ), asc( "<" ), asc( "a" ), asc( "r" ), _
476  asc( "c" ), asc( "h") , asc( ">" ), asc( !"\n" ) _
477 }
478 
479 function hLoadArString _
480  ( _
481  byval p as ubyte ptr, _
482  byval length as integer _
483  ) as zstring ptr
484 
485  '' Biggest field in the ar header is 16 bytes
486  const MAXLEN = 16
487  static as zstring * MAXLEN+1 s
488  dim as integer last = any, i = any
489 
490  assert( length <= MAXLEN )
491 
492  '' Find last char (skipping right-padding spaces)
493  last = length - 1
494  while( last >= 0 )
495  if( p[last] <> asc( " " ) ) then
496  exit while
497  end if
498  last -= 1
499  wend
500 
501  '' Copy into string
502  i = 0
503  while( i <= last )
504  s[i] = p[i]
505  i += 1
506  wend
507  s[i] = 0 '' null terminator
508 
509  function = @s
510 end function
511 
512 sub hLoadObjFromAr( )
513  dim as AR_H ptr h = any
514  dim as string filename
515  dim as integer i = any, filesize = any
516 
517  objdata.p = NULL
518  objdata.size = 0
519 
520  '' 8 magic bytes
521  if( ardata.size < 8 ) then
522  exit sub
523  end if
524 
525  for i = 0 to 7
526  if( ardata.p[i] <> armagic(i) ) then
527  exit sub
528  end if
529  next
530 
531  i = 8
532  do
533  '' Enough room for header?
534  if( ((i + sizeof( AR_H )) > ardata.size) ) then
535  exit sub
536  end if
537 
538  h = cptr( any ptr, ardata.p ) + i
539  filename = *hLoadArString( @h->name(0), 16 )
540  filesize = val( *hLoadArString( @h->size(0), 10 ) )
541 
542  INFO( "ar: found " + filename + ", " + str( filesize ) + " bytes" )
543 
544  if( filesize < 0 ) then
545  filesize = 0
546  elseif( (filesize > ardata.size) or _
547  (filesize > (ardata.size - i)) ) then
548  filesize = ardata.size - i
549  end if
550 
551  '' Skip over header
552  i += sizeof( AR_H )
553 
554  select case( filename )
555  case "__fb_ct.inf", "__fb_ct.inf/"
556  if( filesize > 0 ) then
557  objdata.p = cptr( any ptr, ardata.p ) + i
558  objdata.size = filesize
559  end if
560  exit do
561 
562  case "/", "//", "__.SYMDEF"
563  '' Special long filename support/symbol table entries
564  '' These will typically appear before any real entries,
565  '' so we need to skip them.
566  '' ("__fb_ct.inf" is short enough, so we don't need to
567  '' worry about supporting long filenames)
568 
569  case else
570  exit do
571  end select
572 
573  '' Skip over file data
574  i += filesize
575 
576  '' Archive headers are 2-byte aligned
577  i += i and 1
578  loop
579 end sub
580 
581 sub hLoadFile _
582  ( _
583  byref filename as string, _
584  byval buf as DATABUFFER ptr _
585  )
586 
587  dim as integer f = any, size = any
588  dim as ubyte ptr p = any
589 
590  buf->p = NULL
591  buf->size = 0
592 
593  f = freefile( )
594  if( open( filename, for binary, access read, as #f ) <> 0 ) then
595  INFO( "file not found: " + filename )
596  exit sub
597  end if
598 
599  size = lof( f )
600 
601  if( size > 0 ) then
602  p = allocate( size )
603  if( get( #f, , *p, size, size ) <> 0 ) then
604  exit sub
605  end if
606  buf->p = p
607  buf->size = size
608  end if
609 
610  close #f
611 end sub
612 
613 sub hLoadFbctinfFromObj( )
614  select case as const( fbGetOption( FB_COMPOPT_TARGET ) )
615  case FB_COMPTARGET_CYGWIN, FB_COMPTARGET_DOS, _
616  FB_COMPTARGET_WIN32, FB_COMPTARGET_XBOX
617  if( fbCpuTypeIs64bit( ) ) then
618  INFO( "reading x86-64 COFF: " + parser.filename )
619  hLoadFbctinfFromCOFF( &h8664 )
620  else
621  INFO( "reading i386 COFF: " + parser.filename )
622  hLoadFbctinfFromCOFF( &h014C )
623  end if
624 
625  case FB_COMPTARGET_DARWIN, FB_COMPTARGET_FREEBSD, _
626  FB_COMPTARGET_LINUX, FB_COMPTARGET_NETBSD, _
627  FB_COMPTARGET_OPENBSD
628  if( fbCpuTypeIs64bit( ) ) then
629  INFO( "reading x86-64 ELF: " + parser.filename )
630  hLoadFbctinfFromELF64_H( )
631  else
632  INFO( "reading i386 ELF: " + parser.filename )
633  hLoadFbctinfFromELF32_H( )
634  end if
635 
636  end select
637 
638  if( fbctinf.size = 0 ) then
639  INFO( "no .fbctinf found" )
640  exit sub
641  end if
642 
643  INFO( "found .fbctinf (" + str( fbctinf.size ) + " bytes)" )
644 
645  '' Check whether it's the old or new format
646  if( fbctinf.p[0] = &h10 ) then
647  INFO( ".fbctinf is using the old format" )
648  parser.is_old = TRUE
649  parser.i = 1 '' Skip the header byte
650  else
651  parser.is_old = FALSE
652  end if
653 end sub
654 
655 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
656 '' objinfo parsing interface
657 
658 type ENTRYINFO
659  text as zstring ptr
660  has_param as integer
661 end type
662 
663 dim shared as ENTRYINFO entries(0 to (OBJINFO__COUNT - 1)) = _
664 { _
665  ( @"-l" , TRUE ), _
666  ( @"-p" , TRUE ), _
667  ( @"-mt" , FALSE ), _
668  ( @"-lang", TRUE ) _
669 }
670 
671 sub hResetBuffers( )
672  ardata.p = NULL
673  ardata.size = 0
674  objdata.p = NULL
675  objdata.size = 0
676  fbctinf.p = NULL
677  fbctinf.size = 0
678 end sub
679 
680 sub objinfoInit( byref filename as string )
681  hResetBuffers( )
682  parser.i = 0
684  parser.is_old = FALSE
685  parser.old_section = -1
686 end sub
687 
688 sub objinfoReadObj( byref objfile as string )
689  objinfoInit( objfile )
690 
691  hLoadFile( objfile, @objdata )
692  if( objdata.size = 0 ) then
693  exit sub
694  end if
695 
697 end sub
698 
699 sub objinfoReadLibfile( byref libfile as string )
700  objinfoInit( libfile )
701 
702  hLoadFile( libfile, @ardata )
703  if( ardata.size = 0 ) then
704  exit sub
705  end if
706 
707  hLoadObjFromAr( )
708  if( objdata.size = 0 ) then
709  exit sub
710  end if
711 
713 end sub
714 
715 sub objinfoReadLib( byref libname as string, byval libpaths as TLIST ptr )
716  static as string libfile, filename
717  dim as TSTRSETITEM ptr path = any
718 
719  '' only search for static libraries (import are named .dll.a and dynamic .so)
720  filename = "lib" + libname + ".a"
721 
722  '' try finding it at the lib paths
723  path = listGetHead( libpaths )
724  while( path )
725  libfile = path->s + FB_HOST_PATHDIV + filename
726  if( hFileExists( libfile ) ) then
727  exit while
728  end if
729 
730  path = listGetNext( path )
731  wend
732 
733  '' not found?
734  if( path = NULL ) then
735  INFO( "could not find library: " + libname )
736  exit sub
737  end if
738 
739  objinfoReadLibfile( libfile )
740 end sub
741 
742 function hGetNextString( ) as zstring ptr
743  dim as integer begin = any
744 
745  function = @""
746 
747  begin = parser.i
748 
749  '' Skip over the next null-terminated string, if any
750  while( parser.i < fbctinf.size )
751  parser.i += 1
752 
753  '' Was it a null terminator?
754  if( fbctinf.p[parser.i-1] = 0 ) then
755  return fbctinf.p + begin
756  end if
757  wend
758 end function
759 
760 function objinfoReadNext( byref dat as string ) as integer
761  if( fbctinf.size <= 0 ) then
762  return -1
763  end if
764 
765  if( parser.is_old ) then
766  while( parser.i < fbctinf.size )
767  '' Not inside any section currently?
768  if( parser.old_section < 0 ) then
769  '' Read next section id byte
771  parser.i += 1
772 
773  '' Validate the section id, and also stop on EOL
774  if( (parser.old_section <= FB_INFOSEC_EOL) or _
776  parser.old_section = -1
777  parser.i = fbctinf.size
778  return -1
779  end if
780  end if
781 
782  '' Read next entry
783 
784  '' Entry string length byte, or 0 section end byte
785  if( parser.i >= fbctinf.size ) then
786  return -1
787  end if
788  parser.i += 1
789 
790  '' If it was a 0 byte, continue on to the next section
791  if( fbctinf.p[parser.i-1] = 0 ) then
792  parser.old_section = -1
793  continue while
794  end if
795 
796  '' Otherwise it's an entry. Parse the null-terminated
797  '' string following behind the length byte, if any.
798  dat = *hGetNextString( )
799 
800  select case( parser.old_section )
801  case FB_INFOSEC_LIB
802  INFO( "lib: " + dat )
803  return OBJINFO_LIB
804  case FB_INFOSEC_PTH
805  INFO( "libpath: " + dat )
806  return OBJINFO_LIBPATH
807  case FB_INFOSEC_CMD
808  select case( dat )
809  case "-lang"
810  '' Read another string, the dialect id
811  dat = *hGetNextString( )
812  if( len( dat ) > 0 ) then
813  INFO( "-lang " + dat )
814  return OBJINFO_LANG
815  end if
816  case "-mt"
817  INFO( "-mt" )
818  return OBJINFO_MT
819  end select
820  end select
821  wend
822  else
823  '' Parse the objinfo data (multiple null-terminated strings)
824  dat = *hGetNextString( )
825 
826  for i as integer = 0 to (OBJINFO__COUNT - 1)
827  if( dat = *entries(i).text ) then
828  if( entries(i).has_param ) then
829  dat = *hGetNextString( )
830  INFO( *entries(i).text + " " + dat )
831  else
832  INFO( dat )
833  end if
834  return i
835  end if
836  next
837  end if
838 
839  function = -1
840 end function
841 
842 function objinfoGetFilename( ) as zstring ptr
843  function = strptr( parser.filename )
844 end function
845 
846 sub objinfoReadEnd( )
847  if( ardata.p ) then
848  '' Archive buffer was allocated, the others point into it
849  deallocate( ardata.p )
850  elseif( objdata.p ) then
851  '' Object buffer was allocated, fbctinf points into it,
852  '' archive buffer is unused
853  deallocate( objdata.p )
854  end if
855 
856  hResetBuffers( )
857 end sub
858 
859 function objinfoEncode( byval entry as integer ) as zstring ptr
860  function = entries(entry).text
861 end function
862