*********************************************************************** * Program....: LISTSTRU.PRG * Author.....: Andy Kramek * Date.......: 24 September 2001 * Notice.....: Copyright (c) 2001 Tightline Computers Ltd, All Rights Reserved. * Compiler...: Visual FoxPro 07.00.0000.9262 for Windows * Purpose....: Replacement for the List Structure Command *********************************************************************** LPARAMETERS tcTable, tlNoShow LOCAL lcTable, lnSelect, lnFields, lcFile, lnH, lnNextByte LOCAL lcCDX, lcMem, lcDBC, lWasOpen, lcStr, lcAlias, llIsDBC, lcDBCName *** Clean parameters and ensure a table is available lcTable = IIF(EMPTY(tcTable) OR VARTYPE(tcTable)#'C', ALIAS(), FORCEEXT( UPPER(ALLTRIM(tcTable)), 'dbf')) IF EMPTY(lcTable) lcTable = GETFILE( 'dbf', 'Structure For', 'Open' ) IF EMPTY( lcTable ) RETURN ENDIF ENDIF *** Open/Select Table if not the selected Alias lnSelect = SELECT() lcAlias = JUSTSTEM(lcTable) lWasOpen = .T. IF ! ALIAS() == lcAlias IF ! USED( lcAlias ) IF FILE( lcTable ) USE (lcTable) AGAIN IN 0 lWasOpen = .F. ELSE MESSAGEBOX('Table Name ' + lcTable + ' Not Found', 16, 'Duff Name!' ) RETURN ENDIF ENDIF SELECT (lcAlias) ENDIF *** Get field count and File Name lnFields = FCOUNT() lcFile = DBF() *** Open an output file lnH = FCREATE( lcAlias + '.TXT') IF UPPER(JUSTEXT(lcFile)) = 'TMP' *** Details for Cursor (or View) CursorLst( lcAlias, lnH ) ELSE *** Details for Table lcDBCName = TableLst( lcFile, lnH) ENDIF IF ! EMPTY( lcDBCName ) *** We have a DBC IF ! DBUSED( lcDBCName ) *** But it's not open OPEN DATABASE (lcDBCName) ELSE *** Make it current SET DATABASE TO FORCEEXT( lcDBCName, "" ) ENDIF ENDIF *** Index Details IndexLst( lnH ) *** Field Details FieldLst( lnH, NOT EMPTY( lcDbcName ) ) *** Close Output File and Table FCLOSE( lnH ) IF ! lWasOpen USE IN (lcAlias) ENDIF IF tlNoShow *** Suppress display ELSE *** Show the file MODI FILE ( lcAlias + '.TXT') NOWAIT ENDIF *** Clean Up and Exit SELECT ( lnSelect ) RETURN ***************************************** * Get Info for Cursors ***************************************** PROCEDURE CursorLst( tcFile, tnH ) LOCAL ARRAY laFields[1] LOCAL lcFile, lnFCount, lnCnt, llHasMemo, lcMem lcFile = ALLTRIM( tcFile ) *** Check for Memo Fields lnFCount = AFIELDS( laFields ) FOR lnCnt = 1 TO lnFCount IF laFields[lnCnt,2] = "M" llHasMemo = .T. EXIT ENDIF NEXT *** Compose Output lcDBC = "Cursor cannot be associated with a DBC" lcMem = IIF( llHasMemo, lcFile + '.FPT', 'No Memo Fields') lcCDX = CDX(1) lcCDX = IIF( EMPTY( lcCDX), 'No Structural Index', lcCDX ) *** Write out lcStr = 'Structure For: ' + lcFile FPUTS(tnH, lcStr ) FPUTS(tnH, REPLICATE('=', LEN(lcStr))) FPUTS(tnH, 'DBC : ' + lcDBC ) FPUTS(tnH, 'CDX : ' + lcCDX ) FPUTS(tnH, 'Memo : ' + lcMem ) FPUTS(tnH, '') RETURN ***************************************** * Get DBC-Related Info for Tables/DBCs ***************************************** PROCEDURE TableLst( tcFile, tnH ) LOCAL lcFile, lnDBCH, lnNextByte, lcCDX, lcMem, lcDBC, lnFields, lcAlias, llInDBC lnFields = FCOUNT() lcFile = ALLTRIM( tcFile ) lcAlias = JUSTSTEM(tcFile) *** Close the table USE *** Re-Open DBF at low level lnDBCH = FOPEN( lcFile ) *** Associated files FSEEK(lnDBCH,28) lnNextByte = ASC( FREAD(lnDBCH, 1)) IF lnNextByte = 7 *** The specified file is actually a DBC, not a Table llInDBC = .F. lcCDX = lcAlias + '.DCX' lcMem = lcAlias + '.DCT' lcDBC = lcAlias + '.DBC is a Database Container' ELSE *** It's a table after all! lcCDX = IIF( lnNextByte%2 = 0, 'No Structural CDX', lcAlias + '.CDX') lcMem = IIF( lnNextByte%4 < 2, 'No Memo File', lcAlias + '.FPT') *** So now Read DBC Backlink FSEEK(lnDBCH,(33+(lnFields*32))) lcDBC = UPPER(ALLTRIM(STRTRAN(FGETS( lnDBCH, 263), CHR(0)))) IF EMPTY( lcDBC ) llInDBC = .F. lcDBC = "Not Part of a DBC" ELSE llInDBC = .T. ENDIF *** Check to see if table allows nulls IF 'NULLFLAGS' $ lcDBC *** Need to read a different location FSEEK(lnDBCH,(65+(lnFields*32))) lcDBC = UPPER(ALLTRIM(STRTRAN(FREAD( lnDBCH, 263), CHR(0)))) ENDIF ENDIF *** Close File and re-open as a table FCLOSE( lnDBCH ) *** Write the header info for Tables/DBC lcStr = 'Structure For: ' + lcFile FPUTS(tnH, lcStr ) FPUTS(tnH, REPLICATE('=', LEN(lcStr))) FPUTS(tnH, 'DBC : ' + lcDBC ) FPUTS(tnH, 'CDX : ' + lcCDX ) FPUTS(tnH, 'Memo : ' + lcMem ) FPUTS(tnH, '') USE (lcFile) *** Return DBC name RETURN IIF( llInDBC, lcDBC, "" ) ***************************************** * List Index Details ***************************************** PROCEDURE IndexLst( tnFileHandle ) LOCAL lnH, lnCnt, lcStr lnH = tnFileHandle *** Get Index Details FPUTS( lnH, '' ) IF TAGCOUNT() > 0 FPUTS(lnH, 'Associated Indexes' ) FPUTS(lnH, '==================' ) *** We have indexes *** NB Maybe we should save these to an array and sort it too! *** Primary first, then Candidate, then others? FOR lnCnt = 1 TO TAGCOUNT() lcStr = "" *** Check for PK IF PRIMARY( lnCnt ) lcStr = "*** PRIMARY KEY: " ELSE *** Check for Candidate IF Candidate( lnCnt ) lcStr = "(Candidate): " ENDIF ENDIF lcStr = CHR(9) + lcStr + TAG(lnCnt) + ": " + KEY(lnCnt) FPUTS(lnH, lcStr ) NEXT ELSE *** No Associated indexes FPUTS( lnH, 'No Associated Indexes' ) FPUTS( lnH, '=====================' ) ENDIF RETURN ***************************************** * List Field Details ***************************************** PROCEDURE FieldLst( tnFileHandle, tlIsDBC ) LOCAL ARRAY laFields[1] LOCAL lnH, lcStr, lnFields, lnCnt, lnMax, lcFldList, lcT lnH = tnFileHandle *** No DBC so just use AFIELDS() *** Get the structure into an array lnFields = AFIELDS( laFields ) *** Table Details FPUTS(lnH, CHR(13) + "Table Information" ) FPUTS(lnH, "=================" ) IF !EMPTY(laFields[1,12]) *** DBC Table Name lcStr = "Long Name: " + laFields[1,12] FPUTS(lnH, lcStr ) ENDIF IF !EMPTY(laFields[1,16]) *** Table Comment lcStr = "Comment: " + laFields[1,16] FPUTS(lnH, lcStr ) ELSE lcStr = "No Comment Supplied" FPUTS(lnH, lcStr ) ENDIF IF ! EMPTY(laFields[1,10]) lcStr = CHR(9) + 'Table Rule: ' + laFields[1,10] ; + ' (Error Message: ' ; + IIF( EMPTY(laFields[1,11]), 'VFP Default', laFields[1,11] ) ; + ')' FPUTS(lnH, lcStr ) ELSE lcStr = "No Table Rule" FPUTS(lnH, lcStr ) ENDIF IF !EMPTY(laFields[1,13]) *** Insert Trigger lcStr = CHR(9) + "On Insert: " + laFields[1,13] FPUTS(lnH, lcStr ) ELSE lcStr = "No Insert Trigger" FPUTS(lnH, lcStr ) ENDIF IF !EMPTY(laFields[1,14]) *** Update Trigger lcStr = CHR(9) + "On Update: " + laFields[1,14] FPUTS(lnH, lcStr ) ELSE lcStr = "No Update Trigger" FPUTS(lnH, lcStr ) ENDIF IF !EMPTY(laFields[1,15]) *** Delete Trigger lcStr = CHR(9) + "On Delete: " + laFields[1,15] FPUTS(lnH, lcStr ) ELSE lcStr = "No Delete Trigger" FPUTS(lnH, lcStr ) ENDIF FPUTS(lnH, '' ) FPUTS(lnH, 'Field Details' ) FPUTS(lnH, '=============' ) lnMax = 0 *** Find longest Field Name FOR lnCnt = 1 TO lnFields lnMax = MAX( lnMax, LEN(ALLTRIM(laFields[lnCnt,1]))) NEXT *** Output field Data lcFldList = "" FOR lnCnt = 1 TO lnFields *** Field Definition lcStr = CHR(9) + PADR(LOWER(laFields[lnCnt,1]),lnMax) + CHR(9) ; + laFields[lnCnt,2] + " (" ; + PADL( laFields[lnCnt,3], 3 ) + ',' ; + ALLTRIM( STR( laFields[lnCnt,4] )) + ' )' + CHR(9) ; + IIF( laFields[lnCnt,5], 'NULL', 'NOT NULL' ) FPUTS(lnH, lcStr ) *** Add Field Name to Field List lcFldList = lcFldList + IIF( !EMPTY( lcFldList ), ", ", "" ) + LOWER( ALLTRIM( laFields[lnCnt,1])) *** Default Value IF ! EMPTY( laFields[lnCnt,9] ) lcStr = CHR(9) + CHR(9) + 'Default: ' + laFields[lnCnt,9] ; + ' (Error Message: ' ; + IIF( EMPTY(laFields[lnCnt,7]), 'VFP Default', laFields[lnCnt,8] ) ; + ')' ELSE lcStr = "" ENDIF IF ! EMPTY( lcStr ) FPUTS(lnH, lcStr ) ENDIF *** Field Level Validation IF ! EMPTY( laFields[lnCnt,7] ) lcStr = CHR(9) + CHR(9) + 'Valid: ' + laFields[lnCnt,7] ; + ' (Error Message: ' ; + IIF( EMPTY(laFields[lnCnt,8]), 'VFP Default', laFields[lnCnt,8] ) ; + ')' ELSE lcStr = "" ENDIF IF ! EMPTY( lcStr ) FPUTS(lnH, lcStr ) ENDIF *** Comment IF tlIsDBC STORE "" TO lcStr, lcT *** We have a DBC - get some properties lcT = DBGETPROP( ALLTRIM(ALIAS()) + "." + laFields[ lnCnt,1], "FIELD", "COMMENT" ) lcStr = IIF( ! EMPTY( lcT ), CHR(9) + CHR(9) + 'Comment: ' + lcT, "" ) lcT = DBGETPROP( ALLTRIM(ALIAS()) + "." + laFields[ lnCnt,1], "FIELD", "CAPTION" ) lcStr = lcStr + IIF( EMPTY( lcStr ), "", CHR(13) ) lcStr = lcStr + IIF( ! EMPTY( lcT ), CHR(9) + CHR(9) + 'Caption: ' + lcT, "" ) ELSE lcStr = "" ENDIF IF ! EMPTY( lcStr ) FPUTS(lnH, lcStr ) ENDIF NEXT *** Output Field List FPUTS(lnH, CHR(13) + "Field List" ) FPUTS(lnH, "==========" ) FPUTS(lnH, lcFldList ) RETURN