Subroutine SRS.UV.HEADER( Result, Record, Field ) *************************************************************************** * * Version 3.2 * * This subroutine will return information about a UniVerse BASIC object code * file. It reads the object code header record and converts the data into * a form suitable for reporting with the UniVerse Retreive report writer. * * This version will work with UniVerse BASIC object code that is prefixed * with a catalog header structure. This allows the catdir to be examined. * Typically this subroutine will be called from an I-Type descriptor placed * in the dictionary for a .O file. For example, to find the number of source * lines in the original program: * * 0001: I * 0002: SUBR( "SRS.UV.HEADER", @RECORD, 18 ) * 0003: * 0004: Lines * 0005: 6R * 0006: S * * This I-Type assumes you will catalog the program as SRS.UV.HEADER. * * This program was written by Gyle Iverson and is provided as a free tool * to help programmers view the header information contained inside * of a UniVerse BASIC object code file. * * Copyright 2002 - 2005 Gyle Tad Marton-Iverson. All rights reserved. * * This program may be freely distributed in source form. * * Should you find errors or make improvements, please let me know. You can * reach me at mailto:info@srs4uv.com. * *************************************************************************** * Define some constants Equate ENDIANTAG To 44256 ;* 0xACE0 Equate ENDIANMASK To 65520 ;* 0xFFF0 * Define our field names and values for the basic header * These are 16-bit values Equate ENDIANHINT To 1 Equate COMPILERVERSION To 2 ;* Special processing Equate OBJECTLEVEL To 3 Equate MACHINETYPE To 4 Equate NUMBEROFLOCALVARIABLES To 5 Equate NUMBEROFSUBROUTINEARGUMENTS To 6 Equate NUMBEROFUNNAMEDCOMMONVARIABLES To 7 Equate NUMBEROFNAMEDCOMMONSEGMENTS To 8 Equate NUMBEROFUSERDEFINEDCONSTANTS To 9 Equate NUMBEROFPRELOADEDREFERENCES To 10 Equate TOTALNUMBEROFREFERENCES To 11 Equate NUMBEROFOPERANDS To 12 Equate NUMBEROFSYMBOLS To 13 Equate NUMBEROFCONSTANTS To 14 Equate NUMBEROFREGISTERS To 15 Equate RESERVEDSHORT1 To 16 * These are 32-bit values Equate OBJECTSIZE To 17 Equate NUMBEROFSOURCELINES To 18 Equate LINECONCOORDANCETABLESIZE To 19 Equate CONSTANTTABLESIZE To 20 Equate CHECKSUM To 21 Equate NAMEDCOMMONOFFSET To 22 Equate CONSTANTTABLEOFFSET To 23 Equate SYMBOLTABLEOFFSET To 24 Equate LINECONCOORDANCEOFFSET To 25 Equate SOURCEPATHNAMEOFFSET To 26 Equate RESERVEDLONG1 To 27 Equate RESERVEDLONG2 To 28 * These are 1-bit values Equate CLEARCOMMONFLAGBIT To 29 Equate QUICKLOADFLAGBIT To 30 Equate USESGETSTATEMENTBIT To 31 Equate NEWBASICOBJECTFORMATFLAGBIT To 32 Equate RESERVEDBIT1 To 33 Equate INHIBITRAIDANDVLISTBIT To 34 Equate RESERVEDBIT2 To 35 Equate RESERVEDBIT3 To 36 Equate RESERVEDCHAR1 To 37 ;* Not processed Equate RESERVEDCHAR2 To 38 ;* Not processed Equate RESERVEDCHAR3 To 39 ;* Not processed * These are virtual values that are derived by digging into the * lower level structures of the object code. Equate LISTOFNAMEDCOMMONS To 40 Equate PROGRAMPATHNAME To 41 Equate COPYRIGHTTEXT To 42 Equate SRSUVSEARCHINFO To 43 ;* Returns numerous fields Equate SRSUVHEADERVERSION To 44 Equate COMMENTS To 45 * Reserve 46 through 49 for future use * These values are only valid if the object code is cataloged Equate REFERENCECOUNT To 50 Equate CATALOGEDBY To 51 Equate CATALOGFLAGS To 52 Equate SHAREDMEMORYADDRESS To 53 * Update this with the last member Equate MAXIMUMSRSVALUES To 53 *************************************************************************** * We keep track of some things in our named common storage area to keep * from re-processing the same fields again and again. Common /SRSUVHDR/ SRSRecord, SRSBigEndian, SRSCatalogOffset, SRSBasicHeader, SRSValues( MAXIMUMSRSVALUES ) If Field = SRSUVHEADERVERSION Then Result = "3.2" Return End If Record # SRSRecord Then * Null the SRSValues Mat SRSValues = "" * keep a copy of the record SRSRecord = Record * assume that the object code is not cataloged SRSCatalogOffset = 0 * assume that the object code is not a gci SRSBasicHeader = @TRUE * Determine if we need to flip the order of the bytes * If we are dealing with big-endian object code, the byte order * needs to be reversed to put it right with the world. SRSBigEndian = @FALSE string = SRSRecord[ SRSCatalogOffset + 1, 2 ] Gosub GetShort: If BitAnd( Result, ENDIANMASK ) # ENDIANTAG Then SRSBigEndian = @TRUE Gosub GetShort: If BitAnd( Result, ENDIANMASK ) # ENDIANTAG Then * assume that the object code is cataloged and try the tests again SRSBigEndian = @FALSE SRSCatalogOffset = 40 * reset us back to little endian SRSBigEndian = @FALSE string = SRSRecord[ SRSCatalogOffset + 1, 2 ] Gosub GetShort: If BitAnd( Result, ENDIANMASK ) # ENDIANTAG Then SRSBigEndian = @TRUE Gosub GetShort: If BitAnd( Result, ENDIANMASK ) # ENDIANTAG Then * This doesn't appear to be a UniVerse BASIC object code record * however, it could be a cataloged GCI entry If Len( SRSRecord ) = 40 Then SRSBasicHeader = @FALSE End Else SRSRecord = "" SRSCatalogOffset = 0 End End End End End End *************************************************************************** GetField: Result = "" If SRSRecord Then If Field >= ENDIANHINT And Field <= MAXIMUMSRSVALUES Then If Len( SRSValues( Field ) ) Then Result = SRSValues( Field ) End Else Begin Case Case SRSBasicHeader And Field >= ENDIANHINT And Field <= RESERVEDSHORT1 * Process the short fields string = SRSRecord[ SRSCatalogOffset + ( ( Field - 1 ) * 2 ) + 1, 2 ] Gosub GetShort: If Field = COMPILERVERSION Then * Convert version into a string delimited with periods version1 = Int( Result / 4096 ) Result -= version1 * 4096 version2 = Int( Result / 256 ) Result -= version2 * 256 version3 = Int( Result / 16 ) Result -= version3 * 16 Result = version1 : "." : version2 : "." : version3 : "." : Result End Case SRSBasicHeader And Field >= OBJECTSIZE And Field <= RESERVEDLONG2 * Process the long fields string = SRSRecord[ SRSCatalogOffset + ( ( Field - OBJECTSIZE ) * 4 ) + 33, 4 ] Gosub GetLong: Case SRSBasicHeader And Field >= CLEARCOMMONFLAGBIT And Field <= RESERVEDBIT3 * Process the bit fields value = Seq( SRSRecord[ SRSCatalogOffset + 81, 1 ] ) bit = 2 ^ ( Field - CLEARCOMMONFLAGBIT ) Result = BitAnd( value, bit ) = bit Case SRSBasicHeader And Field = LISTOFNAMEDCOMMONS listOfCommonNames = "" * save the present value of Field saveField = Field * Get the number of named common segments Field = NUMBEROFNAMEDCOMMONSEGMENTS Gosub GetField: numberOfNamedCommonStorageAreas = Result * Find out if the unnamed common is used Field = NUMBEROFUNNAMEDCOMMONVARIABLES Gosub GetField: usesUnnamedCommon = Result # 0 If usesUnnamedCommon Then listOfCommonNames<1, -1> = "unnamed" End If numberOfNamedCommonStorageAreas > 0 Then * Get the version of the program Field = COMPILERVERSION Gosub GetField: majorVersion = Int( Field( Result, ".", 1, 1 ) ) * Get the named common offset Field = NAMEDCOMMONOFFSET Gosub GetField: namedCommonOffset = Result If majorVersion < 6 Then For j = 1 To numberOfNamedCommonStorageAreas name = SRSRecord[ SRSCatalogOffset + namedCommonOffset + ( 36 * ( j - 1) ), 34 ] nullPosition = Index( name, Char( 0 ), 1 ) If nullPosition Then name = name[ 1, nullPosition - 1 ] End listOfCommonNames< 1, -1 > = name Next j End Else Field = CONSTANTTABLEOFFSET Gosub GetField: constantTableOffset = Result For j = 1 To numberOfNamedCommonStorageAreas offset = namedCommonOffset + ( 8 * ( j - 1 ) ) + 1 string = SRSRecord[ SRSCatalogOffset + offset, 4 ] Gosub GetLong: offset = constantTableOffset + Result + 1 name = SRSRecord[ SRSCatalogOffset + offset, 128 ] nullPosition = Index( name, Char( 0 ), 1 ) name = name[ 1, nullPosition - 1 ] listOfCommonNames< 1, -1 > = name Next j End ;* majorVersion < 6 End ;* numberOfNamedCommonStorageAreas > 0 * restore the value of Field Field = saveField Result = listOfCommonNames Case SRSBasicHeader And Field = PROGRAMPATHNAME or Field = COPYRIGHTTEXT or Field = COMMENTS * save the present value of Field saveField = Field * Get the program pathname offset Field = SOURCEPATHNAMEOFFSET Gosub GetField: programPathnameOffset = Result If programPathnameOffset Then * make a copy of the program name area length = ( Len( SRSRecord ) - SRSCatalogOffset ) - ( programPathnameOffset + 1 ) name = SRSRecord[ SRSCatalogOffset + programPathnameOffset + 1, length ] * find the first null character nullPosition = Index( name, Char( 0 ), 1 ) If nullPosition Then programPathName = name[ 1, nullPosition - 1 ] * remove the program name from our area length -= nullPosition name = name[ nullPosition + 1, length ] * find the next null character nullPosition = Index( name, Char( 0 ), 1 ) If nullPosition Then copyrightText = name[ 1, nullPosition - 1 ] * remove any trailing carrage returns from the copyright text Loop While copyrightText[ 1 ] = Char( 13 ) copyrightText[ 1 ] = "" Repeat * remove the copyright text from our area length -= nullPosition name = name[ nullPosition + 1, length ] * what remains in the name variable is all of the comments * remove any trailing carrage returns from the name Loop While name[ 1 ] = Char( 13 ) name[ 1 ] = "" Repeat * multiple comments are delimited with field marks * lower the delimiters to value marks comments = Lower( name ) End Else copyrightText = name comments = "" End End Else programPathName = name copyrightText = "" comments = "" End * update the values SRSValues( PROGRAMPATHNAME ) = programPathName SRSValues( COPYRIGHTTEXT ) = copyrightText SRSValues( COMMENTS ) = comments End Else * there is no program name, copyright text nor comments programPathName = "" copyrightText = "" comments = "" End * restore the value of Field Field = saveField * assign the appropriate value to Result Begin Case Case Field = PROGRAMPATHNAME Result = programPathName Case Field = COPYRIGHTTEXT Result = copyrightText Case Field = COMMENTS Result = comments End Case Case SRSBasicHeader And Field = SRSUVSEARCHINFO * Get all of the fields needed by the SRS.UV.SEARCH program Field = COMPILERVERSION Gosub GetField: Field = OBJECTLEVEL Gosub GetField: Field = MACHINETYPE Gosub GetField: Field = NUMBEROFLOCALVARIABLES Gosub GetField: Field = NUMBEROFSUBROUTINEARGUMENTS Gosub GetField: Field = NUMBEROFUNNAMEDCOMMONVARIABLES Gosub GetField: Field = NUMBEROFNAMEDCOMMONSEGMENTS Gosub GetField: Field = NUMBEROFUSERDEFINEDCONSTANTS Gosub GetField: Field = NUMBEROFPRELOADEDREFERENCES Gosub GetField: Field = TOTALNUMBEROFREFERENCES Gosub GetField: Field = NUMBEROFOPERANDS Gosub GetField: Field = NUMBEROFSYMBOLS Gosub GetField: Field = NUMBEROFCONSTANTS Gosub GetField: Field = NUMBEROFREGISTERS Gosub GetField: Field = OBJECTSIZE Gosub GetField: Field = NUMBEROFSOURCELINES Gosub GetField: Field = LINECONCOORDANCETABLESIZE Gosub GetField: Field = CONSTANTTABLESIZE Gosub GetField: Field = CHECKSUM Gosub GetField: Field = CLEARCOMMONFLAGBIT Gosub GetField: Field = QUICKLOADFLAGBIT Gosub GetField: Field = USESGETSTATEMENTBIT Gosub GetField: Field = NEWBASICOBJECTFORMATFLAGBIT Gosub GetField: Field = INHIBITRAIDANDVLISTBIT Gosub GetField: Field = LISTOFNAMEDCOMMONS Gosub GetField: Field = PROGRAMPATHNAME Gosub GetField: MatBuild Result From SRSValues Using @FM * an expedited return here prevents the updating of the SRSValues Return Case Field = SRSUVHEADERVERSION Result = "3.0" Case Field = REFERENCECOUNT If SRSCatalogOffset Then string = SRSRecord[ 1, 4 ] Gosub GetLong: End Else Result = "" End Case Field = CATALOGEDBY If SRSCatalogOffset Then name = SRSRecord[ 5, 12 ] nullPosition = Index( name, Char( 0 ), 1 ) If nullPosition Then Result = name[ 1, nullPosition - 1 ] End Else Result = name End End Else Result = "" End Case Field = CATALOGFLAGS If SRSCatalogOffset Then string = SRSRecord[ 17, 4 ] Gosub GetLong: End Else Result = "" End Case Field = SHAREDMEMORYADDRESS If SRSCatalogOffset Then string = SRSRecord[ 21, 4 ] Gosub GetLong: End Else Result = "" End End Case * Save the Result so we don't have to do the work again. SRSValues( Field ) = Result End ;* Len( SRSValues( Field ) ) End ;* Field >= ENDIANHINT And Field <= RESERVEDBIT3 End ;* Len( SRSRecord ) Return *************************************************************************** GetShort: * Get the value of the two bytes and convert them into a short. * The value is passed in the string variable and the Result is returned. If SRSBigEndian Then Result = ( Seq( string[ 2, 1 ] ) * 256 ) + Seq( string[ 1, 1 ] ) End Else Result = ( Seq( string[ 1, 1 ] ) * 256 ) + Seq( string[ 2, 1 ] ) End Return *************************************************************************** GetLong: * Get the value of the four bytes and convert them into a long. * The value is passed in the string variable and the Result is returned. Result = 0 If SRSBigEndian Then For i = 4 To 1 Step -1 Result = ( ( Result * 256 ) + Seq( string[ i, 1 ] ) ) Next i End Else For i = 1 To 4 Result = ( ( Result * 256 ) + Seq( string[ i, 1 ] ) ) Next i End Return *************************************************************************** End