Generically Remove or Rename a specific TPS Table in a Superfile

When you do a REMOVE(TpsSuperFileTable), the RTL/Driver doesn’t need to know the the table structure.

This is an example commandline (not console, but could be) app for removing a table from a tps file. TPS Table Remover - Doesn't need to know file structure · GitHub

You can enumerate the \!File-Names inside a TPS Super file with SEND(file, 'PNM=[starting point]')

Possible enhancement to your utility pass the 2nd Parameter as “?” to use PNM:

TableRemove YourTpsFile ?

And it pops up a Message() with all the \!Names one per line and a Copy button, or just have MsgMode Copy.

Next would be a LIST with a Remove and Copy button for the selected line. Rename may be possible (nice way to hide instead of remove).


TName" = SEND(file, ‘PNM=[starting point]’ )

Returns the next table name in the file’s TopSpeed super file, after the specified starting point. If there are no table names after the specified starting point, SEND returns an empty string. If starting point is omitted or contains an empty string, SEND returns the first table name in the file. PNM= is only valid with the SEND command. There are no spaces surrounding the equal sign (=). The target file is the label of any of the tables within the TopSpeed super file.

This error Message showing ERROR() twice does not make sense. You probably meant the second to be ErrorCode()

  IF RemoveTPSTable(BuiltFilePath,COMMAND('3'))
    MESSAGE(CLIP(ERROR()) & '||' & ERROR(),'Error',ICON:EXCLAMATION)
  ELSE

Suggest include possible driver error:

    MESSAGE('Remove() failed on ' & BuiltFilePath & |
            '<13,10><13,10>Error Code: ' & ERRORCODE()&' '&ERROR() & |
             CHOOSE(~FILEERRORCODE(),'','<13,10>Driver Error: ' & FILEERRORCODE()&' '&FILEERROR() ) & | 
             CHOOSE(~ERRORFILE(),'','<13,10>File Name: ' & ERRORFILE() , |
             'Error',ICON:EXCLAMATION)
1 Like

Thanks Carl - Did you actually try the PNM? I thought it required opening a table that exists, or something. Perhaps I was doing it wrong when I tried to add it 20 years ago.

The code I can find was ! commented out, but it does show the file being Opened (Share). It also has the comment !this did not work.

OpenFileRtn  ROUTINE   !Read an older TPS file
    DATA
PNMString   string(255)
    CODE
!this did not work
    DB('OpenFileRtn','fVer=' & fVer )
    !Maybe getting all of the files within the SuperFile will show if we have Management
    share(VerHistory)
    DB('OpenFileRtn','share(VerHistory) Error=' & errorcode() )

    LOOP
        PNMString = SEND(VerHistory, 'PNM=' & clip(PNMString) )
        DB('OpenFileRtn','PNM=' & PNMString )
        IF ~PNMString THEN BREAK.
    END
    close(VerHistory)

Seems easy enough to try. Add my simple debug:

MAP 
DB          PROCEDURE(STRING DebugMessage)  !OutputDebugString
   MODULE('WinAPI')
      OutputDebugString(*cstring Msg),PASCAL,RAW,DLL(1),NAME('OutputDebugStringA')
   END 


DB   PROCEDURE(STRING xMsg)
Prfx EQUATE('TpsTblRmv: ')
sz   CSTRING(SIZE(Prfx)+SIZE(xMsg)+3),AUTO
  CODE 
  sz=Prfx & CLIP(xMsg) & '<13,10>'
  OutputDebugString(sz)

The Message should also show the Password since a command line file name with a space could parse as a 3rd parameter. The error with a bad password is usually odd so the message showing a password where there was none will help spot a problem.

    MESSAGE('Remove() failed on "' & BuiltFilePath &'"'& |
            CHOOSE(~COMMAND('3'),'','<13,10>Using Password: '& COMMAND('3') ) & |
            '<13,10><13,10>Error Code: ' & ERRORCODE()&' '&ERROR() & |
            CHOOSE(~FILEERRORCODE(),'','<13,10>Driver Error: ' & FILEERRORCODE()&' '&FILEERROR() ) & | 
            CHOOSE(~ERRORFILE(),'','<13,10>File Name: ' & ERRORFILE() ) , |
            'RemoveTPSTable Error',ICON:EXCLAMATION)

This code is from my Err4Msg() function you can find here on Hub with a search. I use it often to easily add complete error info to a Message or log.

Same for RENAME() does not need the FILE structure defined. I like the idea of Renaming the file to hide it instead of Remove and its gone.

Syntax is TpsTableRename TpsFile OldTableName NewTableName [password]
for example TpsTableRename ProjectFile TaskTable Old_Tasks
will rename ProjectFile.tps\!TaskTable
inside the file to ProjectFile.tps\!Old_Tasks

One option is having Rename() extract the Table out of the Super file into a separate TPS file. I decided that if the New Name was ~\! that flags to “extract” (i.e. not a super file). Maybe someone has other ideas for syntax?

For example TpsTableRename ProjectFile TaskTable ~\!
—> creates TaskTable.TPS with only the TaskTable table inside as “UNNAMED”
Or TpsTableRename ProjectFile TaskTable ~\!OldTasks\!\TaskTable
—> creates OldTasks.TPS with the TaskTable table inside as “TaskTable”

  PROGRAM  !TpsTableRename.exe Utility by Carl Barnes based on Table Remove by Jeff Slarve
           !Usage: TpsTableRename YourTpsFile TableName NewName [Password] 
           !FYI:   New Name as "~\!" the Table is "Extracted" to its own TPS
  MAP
RenameTPSTable  PROCEDURE(STRING pTableName, STRING pNewName,<STRING pOwnerID>),LONG,PROC  
Err4Msg         PROCEDURE(Byte NoCRLF=0),STRING  !Fromat ErrorCode() & Error() & FileError... for Message() Stop() Halt() or Log file (NoCRLF)
  END
BuiltFilePath   CSTRING(401)
BuiltRenFile    CSTRING(401)
NewTableName    CSTRING(256)
Password        CSTRING(101) 
ExractFlag      BOOL
  CODE 
  SYSTEM{PROP:FontName}='Segoe UI' ; SYSTEM{PROP:FontSize}=10
  IF NOT COMMAND('1') OR NOT COMMAND('2') OR NOT COMMAND('3')
    MESSAGE('TpsTableRename.exe: ' & |
           '||<9>A commandline program designed for the quick' & |
           '|<9>rename of a \!Table within a TOPSPEED "super" file.' & |
           '||Usage:' & |
           '|<9>TpsTableRename YourTpsFile TableName NewName [Password]'& | 
           '||Specify NewName as "~\!" to Extract the Table as a separate TPS file.' & |
           '|FYI: The table "UNNAMED" is when no "\!" was specified.' & |
           '','TpsTableRename Usage',ICON:Help,,,MSGMODE:CANCOPY)
    RETURN
  END

  BuiltFilePath = LONGPATH(COMMAND('1')) & '\!'& COMMAND('2')
  NewTableName  = CLIP(COMMAND('3'))
  Password      = CLIP(COMMAND('4'))
  
  IF SUB(NewTableName,1,3) = '~\!' THEN         !Pass New Name as ~\! to extract
     IF SUB(NewTableName,4,999) <> '' THEN      !There is text after '~\!'
        BuiltRenFile = SUB(NewTableName,4,999)  !Extracted TPS file name = after '~\!'
     ELSE
        BuiltRenFile  = COMMAND('2')            !Extracted TPS file name = just \!Table Name
     END
     ExractFlag = True 
  ELSE 
     BuiltRenFile  = LONGPATH(COMMAND('1')) & '\!'& NewTableName    !Rename inside current TPS
  END

  IF RenameTPSTable(BuiltFilePath,BuiltRenFile,Password) THEN
    MESSAGE('Rename() failed ' & |
                '<13,10><13,10>Table Name: "' & BuiltFilePath &'"'& |
                '<13,10>Rename as: "' & BuiltRenFile &'"'& |
                CHOOSE(NOT Password,'','<13,10>Using Password: "'& Password & '"' ) & | 
                Err4Msg(),'TPSTableRename Error',ICON:EXCLAMATION,,,MSGMODE:CANCOPY)

  ELSIF ExractFlag THEN
    MESSAGE('From File: ' & BuiltFilePath &'||Extracted To: '& BuiltRenFile & |
            '||The table was extracted as a separate TPS file','TPSTableRename Extract',Icon:Tick,,,MSGMODE:CANCOPY)
  ELSE
    MESSAGE('Renamed: ' & BuiltFilePath &'||To Table: '& BuiltRenFile,'TPSTableRename Success',Icon:Tick,,,MSGMODE:CANCOPY)
  END
  RETURN
!-----------------------------------------------------------------------------
RenameTPSTable  PROCEDURE(STRING pTableName, STRING pNewName,<STRING pOwnerID>)
FileName          STRING(400),STATIC
OwnerID           CSTRING(101),STATIC
F                 FILE,DRIVER('TOPSPEED'),NAME(FileName),PRE(F),OWNER(OwnerID),ENCRYPT
Record              RECORD
                    END
                  END
  CODE
  IF NOT pTableName THEN RETURN 0.
  IF ~OMITTED(pOwnerID) THEN OwnerID = CLIP(pOwnerID).
  FileName = pTableName
  !    STOP('RenameTPSTable Debug <13,10><13,10>Name(F): '& CLIP(Name(F)) &'<13,10><13,10>Rename To: '& pNewName)
  RENAME(F,pNewName)    !was  REMOVE(F)
  RETURN ERRORCODE()
!-----------------------------------------------------------------------------
Err4Msg  PROCEDURE(Byte NoCRLF=0)!,STRING 
  !Example: IF ERRORCODE() THEN STOP('Failed ADD(xxx)' & Err4Msg()).
  !Note: Return starts '<13,10><13,10>Error Code:' so no need to put in the Message()
  CODE
  IF ~ERRORCODE() THEN RETURN ''.   
  IF ~NoCRLF THEN 
     RETURN '<13,10><13,10>Error Code: ' & ERRORCODE()&' '&ERROR() & |
             CHOOSE(~FILEERRORCODE(),'','<13,10>Driver Error: ' & FILEERRORCODE()&' '&FILEERROR() ) & | 
             CHOOSE(~ERRORFILE(),'','<13,10>File Name: ' & ERRORFILE() )
  END 
  !NoCRLF<>0 is 1 line format for use by logging
  RETURN ERRORCODE()&' '&ERROR() & |     ! {148}
         CHOOSE(~FILEERRORCODE(),'',' [Driver ' & FILEERRORCODE()&' '&FILEERROR() &']' ) & | 
         CHOOSE(~ERRORFILE(),'',' {{' & ERRORFILE() & '}' ) 

Project_TpsTableRename.zip (5.1 KB)


A reminder that every TPS file is a Super File. If you don’t specify a \!TableName then Table Name defaults to UNNAMED.

Cool. Thank you Carl.

A way to list all the Tables inside a Super TPS file e.g. TpsTableList Zuper

This is done using a feature of the TPS Driver SEND(File,'PNM=...). This requires a Table be Open … so you have to know the Structure :unamused_face:. To work around that I Create() my own Table __List-Tps-Temp-File__ that I can open. To not alter the original TPS file I make a Copy() and Create() in that.

This is what I came up … alternative ways or ideas ???

  PROGRAM  !TpsTableList.exe Utility by Carl Barnes based on Table Remove by Jeff Slarve
           !Usage: TpsTableList YourTpsFile [Password] 

MsgCaption  EQUATE('TPS Table List ')
  MAP
ListTablesMain  PROCEDURE()  
ListTPSTables   PROCEDURE(STRING pTpsSuperFileName,<STRING pOwnerID>, *CSTRING OutTableList),LONG,PROC 
Err4Msg         PROCEDURE(Byte NoCRLF=0),STRING  !Fromat ErrorCode() & Error() & FileError... for Message() Stop() Halt() or Log file (NoCRLF)
  END
    CODE 
    ListTablesMain()
    RETURN

ListTablesMain PROCEDURE()

TpsFile2List  CSTRING(261)      !File name on Command line
TpsFilePwd    CSTRING(101)      !Password from Command line
TmpCopyTPS    CSTRING(261)      !Copy of cmdline TPS file so leave orignal untouched 
TableList     CSTRING(4000)
  CODE 
  SYSTEM{PROP:FontName}='Segoe UI' ; SYSTEM{PROP:FontSize}=10 
  DO HouseKeepingRtn
  IF NOT COMMAND('1')
    MESSAGE('TpsTableList.exe: ' & |
           '||<9>A commandline program designed for the quick' & |
           '|<9>list of \!Table''s within a TOPSPEED "super" file.' & |
           '||Usage:|<9>TpsTableList YourTpsFile [Password]|',MsgCaption,ICON:Help)
    RETURN
  END

  TpsFile2List = CLIP(LONGPATH(COMMAND('1')))
  TpsFilePwd   = CLIP(COMMAND('2'))
  
  IF ~EXISTS(TpsFile2List) THEN                 !Does the command line file exist?
     IF EXISTS(TpsFile2List & '.TPS') THEN      !No ... what about with .TPS Extension
        TpsFile2List = TpsFile2List & '.TPS'    !   Yes so add .TPS
     ELSE                                       !   No  they get error
        MESSAGE('The File Name does not exist:||'& TpsFile2List , |
               MsgCaption,ICON:Help)
        RETURN 
     END
  END
  
  TmpCopyTPS = '.\~TpsTblListTemp~' & Clock() &'.TpsTmp'
  COPY(TpsFile2List,TmpCopyTPS)
  IF ERRORCODE() THEN
     Message('Copy to create Temp Tps file failed' & |
            '||   Copy From: '& TpsFile2List & |
            '|   Copy To: '& TmpCopyTPS & |
            '|' & Err4Msg(), MsgCaption, ICON:Exclamation)
     RETURN
  END
  !Edge case - Could file be Read Only, fix with change attributes?
  !  STOP('TpsFile2List<9>=' & TpsFile2List &'<13,10>TmpCopyTPS<9>='& TmpCopyTPS)
  IF ListTPSTables(TmpCopyTPS,TpsFilePwd, TableList) THEN
     MESSAGE('TPS List failed on "' & TpsFile2List &'"'& |
             CHOOSE(NOT TpsFilePwd,'','<13,10>Using Password: "'& TpsFilePwd & '"' ) & |
             Err4Msg(), MsgCaption &' Error',ICON:EXCLAMATION,,,MSGMODE:CANCOPY)  
  ELSE
     REMOVE(TmpCopyTPS)
     SETCLIPBOARD(TableList)
     CASE MESSAGE(CLIP(TpsFile2List) &'||'& CLIP(TableList) , |
                   MsgCaption,Icon:Tick,'Close|Copy List',,MSGMODE:CANCOPY)
     OF 2 ; SETCLIPBOARD(TableList)
     END 
  END  
  REMOVE(TmpCopyTPS)
  DO HouseKeepingRtn
  RETURN 
HouseKeepingRtn    ROUTINE  !Remove any Tmp Copy incase Orphaned
    DATA
QNdx    LONG,AUTO
DirTmpQ QUEUE(FILE:Queue),PRE(DirTmpQ)
        END ! DirTmpQ:Name  DirTmpQ:ShortName(8.3?)  DirTmpQ:Date  DirTmpQ:Time  DirTmpQ:Size  DirTmpQ:SizeU  DirTmpQ:Attrib
    CODE
    DIRECTORY(DirTmpQ,'.\~TpsTblListTemp*.TpsTmp',ff_:NORMAL)    
    LOOP QNdx = 1 TO RECORDS(DirTmpQ)
         GET(DirTmpQ,QNdx)
         REMOVE(DirTmpQ:Name)
    END
    EXIT
!-----------------------------------------------------------------------------
ListTPSTables  PROCEDURE(STRING pTpsSuperFileName,<STRING pOwnerID>, *CSTRING OutTableList)!,LONG,PROC
TmpTableName      PSTRING(64)           !Name of Table to Create then Open
TmpFullName       STRING(400),STATIC    !Name of TPS super file, expect it is Temp Copy
OwnerID           CSTRING(101),STATIC
ListTmpFile       FILE,DRIVER('TOPSPEED'),NAME(TmpFullName),PRE(LstTmpFile),OWNER(OwnerID),ENCRYPT,CREATE
Record              RECORD
TmpField                STRING(1)
                    END
                  END
NextPNM STRING(128)
PnmList ANY                  
  CODE
  IF NOT pTpsSuperFileName
     OutTableList = 'No File Name so no Tables'
     RETURN 0
  END
  IF ~OMITTED(pOwnerID) THEN OwnerID = CLIP(pOwnerID). 
  TmpTableName = '__List-Tps-Temp-File__' & Clock()
  TmpFullName  = CLIP(pTpsSuperFileName) &'\!' & TmpTableName
  CREATE(ListTmpFile) ; IF ERRORCODE() THEN RETURN ERRORCODE().
  SHARE(ListTmpFile)  ; IF ERRORCODE() THEN RETURN ERRORCODE().
  LOOP 200 TIMES 
    NextPNM = SEND(ListTmpFile,'PNM=' & NextPNM ) 
    IF ~NextPNM THEN BREAK.
    IF UPPER(NextPNM) = UPPER(TmpTableName) THEN CYCLE.
    PnmList = CHOOSE(~PnmList,'',PnmList &'<13,10>') & CLIP(NextPNM)
  END 
  CLOSE(ListTmpFile)
  REMOVE(ListTmpFile) 
  OutTableList = PnmList
  RETURN 0  !ERRORCODE()
!-----------------------------------------------------------------------------
Err4Msg  PROCEDURE(Byte NoCRLF=0)!,STRING 
  !Example: IF ERRORCODE() THEN STOP('Failed ADD(xxx)' & Err4Msg()).
  !Note: Return starts '<13,10><13,10>Error Code:' so no need to put in the Message()
  CODE
  IF ~ERRORCODE() THEN RETURN ''.   
  IF ~NoCRLF THEN 
     RETURN '<13,10><13,10>Error Code: ' & ERRORCODE()&' '&ERROR() & |
             CHOOSE(~FILEERRORCODE(),'','<13,10>Driver Error: ' & FILEERRORCODE()&' '&FILEERROR() ) & | 
             CHOOSE(~ERRORFILE(),'','<13,10>File Name: ' & ERRORFILE() )
  END 
  !NoCRLF<>0 is 1 line format for use by logging
  RETURN ERRORCODE()&' '&ERROR() & |     ! {148}
         CHOOSE(~FILEERRORCODE(),'',' [Driver ' & FILEERRORCODE()&' '&FILEERROR() &']' ) & | 
         CHOOSE(~ERRORFILE(),'',' {{' & ERRORFILE() & '}' ) 

Project_TpsTableList.zip (14.3 KB)

Project contains example Zuper.TPS with 8 tables. Also ZuperBad.TPS that’s a text file so causes driver errors.

It should work to change RENAME() to COPY() so a Table can be duplicated with a new name. Just need to work out the command line syntax…

Instead of relying of the order of the command line (i.e. 1,2,3) use Name=Value so

TpsTableRename.exe
   F=File Name of TPS 
   T=Table Name Original 
   N=New Table Name (renamed table)
   E=Extract to TPS File Name
   P=Password
   /Q=Quiet Mode, No Messages to Run

These same parameters would work for Copy.

It might make a neat console example, too