/****************************** Module Header ******************************* * * Module Name: epmshell.e * * Copyright (c) Netlabs EPM Distribution Project 2002 * * $Id$ * * =========================================================================== * * This file is part of the Netlabs EPM Distribution package and is free * software. You can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * Netlabs EPM Distribution. This library is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * ****************************************************************************/ compile if not defined( SMALL) -- If compiled separately EA_comment 'This defines the EPM shell buffer.' define INCLUDING_FILE = 'EPMSHELL.E' include 'stdconst.e' const tryinclude 'mycnf.e' const compile if not defined( NLS_LANGUAGE) NLS_LANGUAGE = 'ENGLISH' compile endif include NLS_LANGUAGE'.e' compile endif ; --------------------------------------------------------------------------- ; Some ShellKram macros added. See STDKEYS.E for key definitions. ; SHELLKRAM.E was available from Joerg Tiemann's homepage some years ago: ; http://home.foni.net/~tiemannj/epm/index.html ; See his pages for documentation. ; =========================================================================== ; Interface for SUE functions of ETKC603.DLL ; =========================================================================== ; --------------------------------------------------------------------------- ; Called from ShellStart defproc Sue_New( var shell_handle, ShellNum) thandle = '????' result = dynalink32( ERES_DLL, 'SUE_new', address( thandle) || GethWndC( EPMINFO_EDITCLIENT) || atol( ShellNum)) shell_handle = thandle return result ; --------------------------------------------------------------------------- ; Called from ShellKill defproc Sue_Free( var shell_handle) thandle = shell_handle result = dynalink32( ERES_DLL, 'SUE_free', address( thandle)) shell_handle = thandle return result ; --------------------------------------------------------------------------- ; Called from ShellOutputLines defproc Sue_ReadLn( shell_handle, var buffe, var BytesMoved) bufstring = buffe -- just to insure the same amount of space is available bm = "??" result = dynalink32( ERES_DLL, 'SUE_readln', shell_handle || address( bufstring) || atol( length( bufstring)) || address( bm)) BytesMoved = itoa( bm, 10) buffe = bufstring return result ; --------------------------------------------------------------------------- ; Called from ShellWrite defproc Sue_Write( shell_handle, buffe, var BytesMoved) bm = "??" result = dynalink32( ERES_DLL, 'SUE_write', shell_handle || address( buffe) || atol( length( buffe)) || address( bm)) BytesMoved = itoa( bm, 10) return result ; --------------------------------------------------------------------------- ; Called from ShellBreak defproc Sue_Break( shell_handle) return dynalink32( ERES_DLL, 'SUE_break', shell_handle) ; =========================================================================== ; Prompt value ; =========================================================================== ; --------------------------------------------------------------------------- defc ShellPromptDlg universal nepmd_hini KeyPath = '\NEPMD\User\Shell\PromptValue' PromptValue = QueryConfigKey( KeyPath) Entry = PromptValue DefaultPromptValue = queryprofile( nepmd_hini, 'RegDefaults', KeyPath) Title = 'Set prompt for shell windows' Text = 'Enter new value. Example: epm: $p $g$s, empty resets to 'DefaultPromptValue Text = Text''copies( ' ', Max( 100 - length( Text), 0)) parse value EntryBox( Title, '', Entry, 0, 240, atoi( 1) || atoi( 0) || atol( 0) || Text) with Button 2 NewPromptValue \0 NewPromptValue = strip( NewPromptValue) if Button = \1 & NewPromptValue <> PromptValue then if NewPromptValue = '' then NewPromptValue = DefaultPromptValue endif WriteConfigKey( KeyPath, NewPromptValue) endif ; --------------------------------------------------------------------------- defproc ShellGetConfiguredPromptValue KeyPath = '\NEPMD\User\Shell\PromptValue' PromptVal = QueryConfigKey( KeyPath) -- Strip leading 'prompt' or '@prompt' UpPromptVal = upcase( PromptVal) if wordpos( word( UpPromptVal, 1), 'PROMPT @PROMPT') then PromptVal = subword( PromptVal, 2) endif return PromptVal ; --------------------------------------------------------------------------- ; This stores both the default and the current prompt values. These different ; prompt values are stored for to handle restored shell files. defproc ShellInitPromptValue i = 0 SetFileAVar( 'shellpromptvalue.0', i) -- Add PromptVal to array if not already added NumPromptVals = 5 do Num = 1 to NumPromptVals if Num = 1 then -- Standard value 1 PromptVal = 'epm: $p $g ' elseif Num = 2 then -- Standard value 2 PromptVal = '[epm: $p ] ' elseif Num = 3 then -- Default value PromptVal = '[$p]' elseif Num = 4 then -- OS/2 shell value PromptVal = Get_Env( 'PROMPT') elseif Num = 5 then -- Currently configured value PromptVal = ShellGetConfiguredPromptValue() endif if PromptVal = '' then iterate endif -- Check if found in previously added values imax = GetFileAVar( 'shellpromptvalue.0') fFound = 0 do i = 1 to imax if GetFileAVar( 'shellpromptvalue.'i) = PromptVal then fFound = 1 leave endif enddo if fFound then iterate endif -- Add PromptVal to array i = imax + 1 --dprintf( 'shellpromptvalue.'i' = 'PromptVal) SetFileAVar( 'shellpromptvalue.'i, PromptVal) SetFileAVar( 'shellpromptvalue.current', i) SetFileAVar( 'shellpromptvalue.0', i) enddo return ; --------------------------------------------------------------------------- ; This handles prompt changes during the shell session by a prompt cmd. ; It stores the specified value. defproc ShellProcessPromptCmd do once = 1 to 1 Cmd = arg( 1) PromptList = 'SET PROMPT=;PROMPT;' CmdSeps = '&|><' EscapeChar = '^' DefaultVal = '[$p]' imax = GetFileAVar( 'shellpromptvalue.0') if imax = '' then call ShellInitPromptValue() endif -- Search for 'PROMPT' in Cmd first. If not found, omit the rest if not pos( 'PROMPT', upcase( Cmd)) then leave endif -- Split Cmd at sep chars, check if sep not escaped FoundPos = 0 CmdRest = Cmd do while CmdRest <> '' pSep = verify( CmdRest, CmdSeps, 'M') -- Not found if pSep = 0 then CmdPart = CmdRest CmdRest = '' -- Found at begin elseif pSep = 1 then CmdPart = '' CmdRest = substr( CmdRest, 2) iterate -- Escaped char elseif substr( CmdRest, pSep - 1, 1) == '^' then CmdPart = '' CmdRest = substr( CmdRest, 2) iterate -- Found at end elseif pSep = length( CmdRest) then CmdPart = CmdRest CmdRest = '' -- Two same sep chars elseif substr( CmdRest, pSep, 1) == substr( CmdRest, pSep + 1, 1) then CmdPart = substr( CmdRest, 1, pSep) CmdRest = substr( CmdRest, pSep + 2) -- Single sep char else CmdPart = substr( CmdRest, 1, pSep) CmdRest = substr( CmdRest, pSep + 1) endif -- Strip leading spaces, strip leading '@' char, uppercase CmdPart = strip( CmdPart, 'L') if leftstr( CmdPart, 1) = '@' then CmdPart = substr( CmdPart, 2) endif UpCmdPart = upcase( CmdPart) -- Find one string of PromptList in UpCmdPart PromptRest = PromptList do while PromptRest <> '' -- Next PromptStr parse value PromptRest with PromptStr ';' PromptRest PromptStr = strip( PromptStr) if PromptStr = '' then iterate endif FoundPos = pos( ' 'PromptStr' ', ' 'UpCmdPart' ') --dprintf( ' ShellProcessPromptCmd: FoundPos = 'FoundPos', PromptStr = |'PromptStr'|, UpCmdPart = |'UpCmdPart'|, Cmd = |'Cmd'|') -- Not found if not FoundPos then iterate endif -- PromptStr found in UpCmdPart, get PromptVal PromptVal = strip( substr( CmdPart, length( PromptStr) + 1), 'L') --dprintf( ' ShellProcessPromptCmd: PromptVal = |'PromptVal'|') if PromptVal = '' then PromptVal = DefaultVal endif -- Store new PromptVal if not already stored imax = GetFileAVar( 'shellpromptvalue.0') icurrent = 0 do i = 1 to imax if GetFileAVar( 'shellpromptvalue.'i) = PromptVal then icurrent = i leave endif enddo if not icurrent then i = imax + 1 SetFileAVar( 'shellpromptvalue.'i, PromptVal) SetFileAVar( 'shellpromptvalue.current', i) SetFileAVar( 'shellpromptvalue.0', i) endif --imax = GetFileAVar( 'shellpromptvalue.0') --do i = 1 to imax --dprintf( ' ShellProcessPromptCmd: PromptVal 'i' stored = |'GetFileAVar( 'shellpromptvalue.'i)'|') --enddo leave enddo if FoundPos then leave endif enddo -- while CmdRest <> '' enddo -- once return ; --------------------------------------------------------------------------- ; Get current prompt value from array defproc ShellGetPromptValue icurrent = GetFileAVar( 'shellpromptvalue.current') if icurrent = '' then call ShellInitPromptValue() endif PromptVal = GetFileAVar( 'shellpromptvalue.'icurrent) return PromptVal ; =========================================================================== ; Find and parse prompt ; =========================================================================== ; --------------------------------------------------------------------------- ; Converts a prompt value to an egrep search string. defproc ShellPromptToSearchStr( PromptVal) SearchStr = '' call ShellPromptToSearchStrCommon( PromptVal, 0, SearchStr, junk) --dprintf( 'PromptVal = 'PromptVal) --dprintf( 'SearchStr = 'SearchStr) return SearchStr ; --------------------------------------------------------------------------- ; Converts a prompt value to 2 egrep search strings. 2 search strings are ; used to place the cursor aroound the path output $p. That allows for to ; determine the current path from a shell output and is used in emergancy ; cases, when the shell is not able to save its environment. defproc ShellPromptToSearchStr2( PromptVal, var SearchStr1, var SearchStr2) SearchStr1 = '' SearchStr2 = '' call ShellPromptToSearchStrCommon( PromptVal, 1, SearchStr1, SearchStr2) --dprintf( 'PromptVal = 'PromptVal) --dprintf( 'SearchStr1 = 'SearchStr1) --dprintf( 'SearchStr2 = 'SearchStr2) return ; --------------------------------------------------------------------------- defproc ShellPromptToSearchStrCommon( PromptVal, fPlaceCursor, var SearchStr1, var SearchStr2) fPlaceCursor = (fPlaceCursor = 1) SearchStr1 = '' SearchStr2 = '' Rest = PromptVal -- Filter out ANSI Esc sequences pStart = 1 do forever pEscStart = pos( '$E[', upcase( Rest), pStart) if pEscStart = 0 then leave else pEscEnd = verify( Rest, 'ABCDHJKnfRhlmpsu', 'M', pEscStart + 3) if pEscEnd = 0 then leave endif Rest = delstr( Rest, pEscStart, pEscEnd - pEscStart + 1) pStart = pEscStart endif enddo -- Escape egrep search chars, allowed for a prompt macro -- '$|&' as normal chars cannot be used in a prompt. They are -- removed here compared to EGREP_METACHARACTERS. AddEscapeChars = '\[]()?*+^.:~#@' pStart = 1 do forever if Rest == '' then leave endif pFound = verify( Rest, AddEscapeChars, 'M', pStart) if not pFound then leave else Rest = leftstr( Rest, pFound - 1)'\'substr( Rest, pFound) pStart = pFound + 2 endif enddo -- Convert prompt macros ('$' + single char) PathConvertStr = ':a\:.#' pStart = 1 do forever pMacroStart = pos( '$', Rest, pStart) if pMacroStart = 0 then leave else MacroChar = substr( Rest, pMacroStart + 1, 1) UpMacroChar = upcase( MacroChar) ConvertStr = '???' if MacroChar = '$' then ConvertStr = '\$' elseif UpMacroChar = 'A' then ConvertStr = '\&' elseif UpMacroChar = 'B' then ConvertStr = '\|' elseif UpMacroChar = 'C' then ConvertStr = '\(' elseif UpMacroChar = 'D' then ConvertStr = '.#' elseif UpMacroChar = 'E' then ConvertStr = \27 elseif UpMacroChar = 'F' then ConvertStr = '\)' elseif UpMacroChar = 'G' then ConvertStr = '\>' elseif UpMacroChar = 'H' then -- Processed below: Backspace -- /Backspace not supported, just print char --/ConvertStr = \8 elseif UpMacroChar = 'I' then ConvertStr = '' elseif UpMacroChar = 'L' then ConvertStr = '\<' elseif UpMacroChar = 'N' then ConvertStr = ':a\:' elseif UpMacroChar = 'P' then if fPlaceCursor then ConvertStr = '\c'PathConvertStr else ConvertStr = PathConvertStr endif elseif UpMacroChar = 'Q' then ConvertStr = '=' elseif UpMacroChar = 'R' then ConvertStr = '(-|):i' elseif UpMacroChar = 'S' then ConvertStr = ' ' elseif UpMacroChar = 'T' then ConvertStr = '.#' elseif UpMacroChar = 'V' then ConvertStr = '.#' elseif MacroChar = '_' then -- Processed below: Linebreak endif if ConvertStr <> '???' then -- Insert ConvertStr pDelta = length( ConvertStr) - 2 -- 2 = length( SearchStr) Rest = leftstr( Rest, pMacroStart - 1)''ConvertStr''substr( Rest, pMacroStart + 2) pStart = pMacroStart + pDelta elseif UpMacroChar = 'H' then -- Backspace pDelta = 1 - 2 -- 2 = length( SearchStr) Rest = leftstr( Rest, pMacroStart - 2)''substr( Rest, pMacroStart + 2) pStart = pMacroStart + pDelta elseif MacroChar = '_' then -- Linebreak: skip everything before Rest = substr( Rest, pMacroStart + 2) pStart = pMacroStart endif endif enddo SearchStr1 = '^'Rest if fPlaceCursor then -- Place the cursor after the path in a second search if '$p' specified pCursor = pos( '\c'PathConvertStr, SearchStr1) if pCursor then SearchStr2 = leftstr( SearchStr1, pCursor - 1) || PathConvertStr'\c' || substr( SearchStr1, pCursor + 2 + length( PathConvertStr)) endif endif return ; --------------------------------------------------------------------------- ; Parse LineStr into Prompt and Cmd. If Prompt was not found, ; both Prompt and Cmd are set to empty. defproc ShellParseStrPromptCmd( LineStr, var Prompt, var Cmd) Prompt = '' Cmd = '' pFound = 0 imax = GetFileAVar( 'shellpromptvalue.0') if imax = '' then call ShellInitPromptValue() imax = GetFileAVar( 'shellpromptvalue.0') endif do i = 1 to imax -- This is required for pos if LineStr = '' then leave endif ThisPrompt = GetFileAVar( 'shellpromptvalue.'i) SearchStr = ShellPromptToSearchStr( ThisPrompt) do n = 1 to 2 -- If not found, repeat search with stripped SearchStr if n = 2 then SearchStr = strip( SearchStr, 'T') endif pFound = pos( SearchStr, LineStr, 1, 'x') if pFound then PromptLen = getpminfo( EPMINFO_LSLENGTH) -- Set output vars Prompt = leftstr( LineStr, PromptLen) Cmd = substr( LineStr, PromptLen + 1) leave endif enddo if pFound then leave endif enddo return ; --------------------------------------------------------------------------- ; Parse line with LineNum into Prompt and Cmd. If Prompt was not found, ; both Prompt and Cmd are set to empty. defproc ShellParseLinePromptCmd( LineNum, var Prompt, var Cmd) getline LineStr, LineNum call ShellParseStrPromptCmd( LineStr, Prompt, Cmd) return ; --------------------------------------------------------------------------- defproc ShellParseLineDirCmd( LineNum, var Dir, var Cmd) call ShellParseLinePromptCmd( LineNum, dummy, Cmd) Dir = ShellGetPrevDir() ; --------------------------------------------------------------------------- defproc ShellDeleteTrailingBlankLines do l = .last to 2 by -1 -- Keep first line getline LineStr, l if verify( LineStr, ' '\t) = 0 then deleteline l else leave endif enddo return ; --------------------------------------------------------------------------- ; Get previous dir from last prompt found in file. defproc ShellGetPrevDir do once = 1 to 1 Dir = '' SavedRc = rc pSave_Pos( SavedPos) getsearch SavedSearch LineNum = arg( 1) if LineNum = '' then LineNum = .line elseif not IsNum( LineNum) then LineNum = .line else .lineg = LineNum endif imax = GetFileAVar( 'shellpromptvalue.0') if imax = '' then call ShellInitPromptValue() imax = GetFileAVar( 'shellpromptvalue.0') endif FoundLine = 0 FoundCol1 = 0 FoundCol2 = 0 SearchStr1 = '' SearchStr2 = '' display -15 -- disables all messsage output and screen updates do i = 1 to imax ThisPrompt = GetFileAVar( 'shellpromptvalue.'i) call ShellPromptToSearchStr2( ThisPrompt, ThisSearchStr1, ThisSearchStr2) --dprintf( 'call ShellPromptToSearchStr2( 'ThisPrompt', 'ThisSearchStr1', 'ThisSearchStr2')') ThisFoundLine = 0 ThisFoundCol1 = 0 do n = 1 to 2 -- If not found, repeat search with stripped SearchStr if n = 2 then if rightstr( ThisSearchStr1, 1) <> ' ' then leave endif ThisSearchStr1 = strip( ThisSearchStr1, 'T') ThisSearchStr2 = strip( ThisSearchStr2, 'T') endif .lineg = LineNum endline --dprintf( 'Start search for col1 at .line = '.line', .col = '.col) 'xcom l '\1''ThisSearchStr1''\1'x-R' if not rc then -- Found --dprintf( 'n = 'n', Found .line = '.line) -- Use the nearest line of both searches if .line > ThisFoundLine then ThisFoundLine = .line ThisFoundCol1 = .col endif endif enddo -- Use the nearest line if ThisFoundLine > FoundLine then FoundLine = ThisFoundLine FoundCol1 = ThisFoundCol1 SearchStr1 = ThisSearchStr1 SearchStr2 = ThisSearchStr2 endif enddo display 15 --dprintf( 'SearchStr1 = 'SearchStr1', rc = 'rc) if FoundLine = 0 then -- Not found leave endif --dprintf( 'FoundLine = 'FoundLine', FoundCol1 = 'FoundCol1) display -15 -- disables all messsage output and screen updates .lineg = FoundLine endline --dprintf( 'Start search for col2 at .line = '.line', .col = '.col) 'xcom l '\1''SearchStr2''\1'x-R' display 15 --dprintf( 'SearchStr2 = 'SearchStr2', rc = 'rc) if rc | .line <> FoundLine then -- Not found leave endif FoundCol2 = .col --dprintf( 'FoundCol2 = 'FoundCol2) getline LineStr Dir = substr( LineStr, FoundCol1, FoundCol2 - FoundCol1) enddo setsearch SavedSearch pRestore_Pos( SavedPos) rc = SavedRc return Dir ; --------------------------------------------------------------------------- ; Moves cursor to the next prompt. ; Optional arg is P (find previous prompt) to search backwards. ; Sets rc from 'xcom l'. defproc ShellGotoNextPrompt --dprintf( 'ShellGotoNextPrompt 'arg( 1)) do once = 1 to 1 pSave_Pos( SavedPos) getsearch SavedSearch if upcase( arg( 1)) = 'P' then Direction = '-r' --dprintf( 'ShellGotoNextPrompt: .col = 1 on .line = '.line) .col = 1 else Direction = '' endif StartLine = .line StartCol = .col imax = GetFileAVar( 'shellpromptvalue.0') if imax = '' then call ShellInitPromptValue() imax = GetFileAVar( 'shellpromptvalue.0') endif FoundLine = 0 FoundCol = 0 SearchStr = '' display -15 -- disables all messsage output and screen updates do i = 1 to imax ThisPrompt = GetFileAVar( 'shellpromptvalue.'i) ThisSearchStr = ShellPromptToSearchStr( ThisPrompt) ThisFoundLine = 0 ThisFoundCol = 0 do n = 1 to 2 -- If not found, repeat search with stripped SearchStr if n = 2 then if rightstr( ThisSearchStr, 1) <> ' ' then leave endif ThisSearchStr = strip( ThisSearchStr, 'T') endif .lineg = StartLine .col = StartCol 'xcom l '\1''ThisSearchStr\1'x'Direction if not rc then -- Found --dprintf( 'n = 'n', Found .line = '.line) -- Use the nearest line of both searches if Direction = '' then -- Foreward if .line & (.line < ThisFoundLine | ThisFoundLine = 0) then ThisFoundLine = .line ThisFoundCol = .col endif else -- Backward if .line > ThisFoundLine then ThisFoundLine = .line ThisFoundCol = .col endif endif endif enddo -- Use the nearest line if Direction = '' then -- Foreward if ThisFoundLine & (ThisFoundLine < FoundLine | FoundLine = 0) then FoundLine = ThisFoundLine FoundCol = ThisFoundCol SearchStr = ThisSearchStr endif else -- Backward if ThisFoundLine > FoundLine then FoundLine = ThisFoundLine FoundCol = ThisFoundCol SearchStr = ThisSearchStr endif endif enddo display 15 if not FoundCol then pRestore_Pos( SavedPos) leave endif -- Go to found pos. .line = FoundLine .col = FoundCol -- Go to CurCmd call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) .col = length( CurPrompt) + 1 enddo setsearch SavedSearch return ; --------------------------------------------------------------------------- defc ShellGotoPrevPrompt SavedRc = rc call ShellGotoNextPrompt( 'P') if not rc then 'HighlightCursor' endif rc = SavedRc defc ShellGotoNextPrompt SavedRc = rc call ShellGotoNextPrompt() if not rc then 'HighlightCursor' endif rc = SavedRc ; --------------------------------------------------------------------------- ; Called by IsADirList. defproc ShellGetLastCmd --dprintf( 'ShellGetLastCmd') call pSave_Pos( SavedPos) SavedRc = rc CurCmd = '' -- Check previous prompt line call ShellGotoNextPrompt( 'P') if not rc then call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) endif rc = SavedRc call pRestore_Pos( SavedPos) return CurCmd ; =========================================================================== ; Determine shell ; =========================================================================== ; --------------------------------------------------------------------------- ; Used to query if current file is a shell. Returns 0 (false) or 1 (true). defproc IsAShell fShell = 0 fid = arg( 1) if fid = '' then getfileid fid Filename = fid.filename elseif IsNum( fid) then Filename = fid.filename else Filename = fid getfileid fid, Filename endif ShellNum = GetAVar( fid'.shellnum') if ShellNum <> '' then ShellHandle = GetAVar( 'shell_h'ShellNum) if ShellHandle <> '' then fShell = 1 endif endif return fShell ; --------------------------------------------------------------------------- ; Returns default shell filename. A number can be specified. If no number is ; passed, then the next free number is used to build the name. ; Syntax: Name = GetDefaultShellFilename( []) defproc GetDefaultShellFilename universal shell_index ShellNum = arg( 1) if not IsNum( ShellNum) then ShellNum = shell_index + 1 endif Name = '.command_shell_'ShellNum return Name ; --------------------------------------------------------------------------- ; Used by defproc GetMode only to determine the default mode for it. ; Returns 0 (false) or 1 (true). defproc IsAShellFilename fShell = 0 fid = arg( 1) if fid = '' then getfileid fid Filename = fid.filename elseif IsNum( fid) then Filename = fid.filename else Filename = fid getfileid fid, Filename endif Name = GetFileSpec( 'N', Filename) if leftstr( Name, 15) = '.command_shell_' then fShell = 1 endif return fShell ; =========================================================================== ; Shell command ; =========================================================================== ; --------------------------------------------------------------------------- ; Executes a passed command in next (current, next or new) shell. ; Syntax: Shell [] defc Shell do once = 1 to 1 parse arg Cmd if IsAShell() then -- Execute passed Cmd in current shell if Cmd then 'ShellWriteCmd' Cmd endif else -- Pass Cmd to next or new Shell 'ShellSwitchStart 'Cmd endif end ; --------------------------------------------------------------------------- ; Starts a new shell object or switches between shell buffers and a ; (starting) non-shell buffer. If args were specified, then the last shell ; is reused and the args are executed in that shell. ; Activates shell1 -> shell2 -> startfile -> shell 1 -> ... ; ; Syntax: ShellSwitchStart [] ; ; shell_index is the number of the last created shell, . ; The array var 'Shell_f' holds the fileid, ; 'Shell_h' the handle. defc ShellSwitchStart -- fSwitch activates shell1 -> shell2 -> startfile -> shell 1 -> ... universal shell_index universal ring_enabled if not ring_enabled then 'Ring_Toggle' endif --dprintf( 'Shell: Cmd = >'arg( 1)'<') Cmd = strip( arg( 1)) fShellInRing = 0 do n = 1 to shell_index ShellFid = GetAVar( 'shell_f'n) if validatefileid( ShellFid) then fShellInRing = 1 leave endif enddo if fShellInRing then --dprintf( 'ShellSwitchStart: ShellSwitch 'Cmd) 'ShellSwitch 'Cmd else -- Execute Cmd only when shell is newly started 'ShellStart 'Cmd endif ; --------------------------------------------------------------------------- ; Similar to ShellSwitchStart, but doesn't execute passed Cmd on ShellSwitch. ; This is used for the Shell toolbar button. defc ShellSwitchStartTB -- fSwitch activates shell1 -> shell2 -> startfile -> shell 1 -> ... universal shell_index universal ring_enabled if not ring_enabled then 'Ring_Toggle' endif --dprintf( 'Shell: Cmd = >'arg( 1)'<') Cmd = strip( arg( 1)) fShellInRing = 0 do n = 1 to shell_index ShellFid = GetAVar( 'shell_f'n) if validatefileid( ShellFid) then fShellInRing = 1 leave endif enddo if fShellInRing then --dprintf( 'ShellSwitchStart: ShellSwitch') 'ShellSwitch' else -- Execute Cmd only when shell is newly started 'ShellStart 'Cmd endif ; --------------------------------------------------------------------------- defc ShellSwitch universal shell_index universal shellstartfid do once = 1 to 1 parse arg Cmd getfileid CurFid ShellFid = '' ShellNum = '' ShellStartNum = 0 if not IsAShell() then -- Save fid of non-shell file as universal shellstartfid = CurFid else ShellStartNum = GetAVar( CurFid'.shellnum') endif -- Find next shell file fMoreShellsInRing = 0 do n = ShellStartNum + 1 to shell_index ShellFid = GetAVar( 'shell_f'n) -- Need to check if not empty if ShellFid then if validatefileid( ShellFid) then fMoreShellsInRing = 1 leave endif endif enddo -- Check if saved non-shell start file exists in ring fSavedOtherInRing = 0 -- Need to check if not empty if shellstartfid then if validatefileid( shellstartfid) then fSavedOtherInRing = 1 endif endif if fMoreShellsInRing then -- Next shell found pActivateFile( ShellFid) elseif fSavedOtherInRing then -- shellstartfid validated pActivateFile( shellstartfid) else -- Find next non-shell file in ring OtherFid = '' getfileid StartFid do f = 1 to filesinring( 1) -- Provide an upper limit; prevent looping forever nextfile getfileid fid if fid = StartFid then leave endif ShellNum = GetAVar( fid'.shellnum') if ShellNum then iterate else OtherFid = fid pActivateFile( OtherFid) leave endif enddo if OtherFid = '' then -- Find first shell in ring do n = 1 to shell_index ShellFid = GetAVar( 'shell_f'n) -- Need to check if not empty if ShellFid then if validatefileid( ShellFid) then pActivateFile( ShellFid) leave endif endif enddo endif endif -- Check file and last line if not IsAShell() then leave endif getfileid ShellFid ShellNum = GetAVar( ShellFid'.shellnum') call ShellParseLinePromptCmd( .last, LastPrompt, LastCmd) if not LastPrompt & .last > 1 then 'SayError Command canceled. Shell is waiting for user input.' leave endif -- Use real case of current dir, even when XWP has uppercased it. CurDir = directory() DirRealCase = GetRealCase( CurDir) if CurDir <> DirRealCase then 'WorkDir 'EnquoteFilespec( DirRealCase) endif -- Execute passed Cmd if Cmd then --dprintf( 'ShellSwitch: ShellWriteCmd 'ShellNum' ShellFilterCmd( 'Cmd')') 'ShellWriteCmd' ShellNum ShellFilterCmd( Cmd) endif enddo ; --------------------------------------------------------------------------- ; This is called by ShellStart and ShellRestore. defproc ShellStart universal shell_index do once = 1 to 1 RestoredFile = arg( 1) Cmd = arg( 2) fRestored = (RestoredFile <> '') --dprintf( 'ShellStart( 'arg( 1)', 'arg( 2)')') shell_index = shell_index + 1 ShellNum = shell_index ShellHandle = '????' retval = Sue_New( ShellHandle, ShellNum) if retval then 'SayError 'ERROR__MSG retval SHELL_ERROR1__MSG leave endif if fRestored then getfileid fid, RestoredFile if fid then pActivateFile( fid) endif else Filename = '.command_shell_'ShellNum 'xcom e /c 'Filename if rc <> sayerror( 'New file') then 'SayError 'ERROR__MSG rc SHELL_ERROR2__MSG leave endif endif getfileid ShellFid if not IsABackupFile() then .autosave = 0 endif call SetAVar( 'shell_f'ShellNum, ShellFid) call SetAVar( 'shell_h'ShellNum, ShellHandle) call SetAVar( ShellFid'.shellnum', ShellNum) 'PostMe Monofont' -- Set shell start state -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended SetFileAVar( 'shellstartstate', 1) -- Set shell restore state -- Current restore state -- 1 restore started -- 2 restore ended if fRestored then SetFileAVar( 'shellrestorestate', 1) else SetFileAVar( 'shellrestorestate', 2) endif -- Current ignore level -- 0 cmd not ignored (normal cmd) or not found in array -- 1 cmd quiet (only the prompt is changed) -- 2 prompt after a quiet cmd (not set by a ShellWrite* cmd) -- 3 cmd hidden (no output at all) -- 4 prompt hidden and cmd quiet (used at ShellStart only) -- 5 next output line is the first after a quiet ShellStart -- Init current ignore level SetFileAVar( 'shellcurrentignorelevel', 4) -- Alias file KeyPath = '\NEPMD\User\Shell\Alias' on = (QueryConfigKey( KeyPath) <> 0) if on then call ShellAliasFileRead() endif -- Init prompt array with value of system prompt call ShellInitPromptValue() -- Change prompt PromptValue = ShellGetConfiguredPromptValue() PromptCmd = '@prompt 'PromptValue --dprintf( 'ShellStart: ShellWriteCmd' ShellNum PromptCmd) 'ShellWriteCmd' ShellNum PromptCmd if fRestored then -- A restored file restores its env. Therefore no InitCmd is -- executed. --dprintf( 'ShellStart: Restored shell, no InitCmd') else -- Execute InitCmd KeyPath = '\NEPMD\User\Shell\InitCmd' InitCmd = QueryConfigKey( KeyPath) if InitCmd <> '' then CurCmd = InitCmd --dprintf( 'ShellStart: ShellWriteCmd 'ShellNum' ShellFilterCmd( 'CurCmd')') 'ShellWriteCmd' ShellNum ShellFilterCmd( CurCmd) endif endif -- Use correct case for current dir -- Get current dir from cd output ShellDir = ShellGetCurDir() --dprintf( 'ShellStart: Output from ShellGetCurDir() = 'ShellDir) ShellDirRealCase = GetRealCase( ShellDir) if ShellDir <> ShellDirRealCase then CdCmd = 'cdd 'EnquoteFilespec( ShellDirRealCase) 'ShellWriteCmd' ShellNum CdCmd endif -- Fix also WorkDir if it has the same value, caseless CurDir = directory() if upcase( CurDir) = upcase( ShellDirRealCase) & CurDir <> ShellDirRealCase then --dprintf( 'ShellStart: WorkDir 'EnquoteFilespec( ShellDirRealCase)) 'WorkDir 'EnquoteFilespec( ShellDirRealCase) endif -- Store StartDir SetFileAVar( 'shellstartdir', ShellDirRealCase) if fRestored then -- A restored file restores its env. Therefore no passed Cmd is -- executed. --dprintf( 'ShellStart: Restored shell, no passed cmd') else -- Execute passed Cmd if Cmd then --dprintf( 'ShellStart: ShellWriteCmd 'ShellNum' ShellFilterCmd( 'Cmd')') 'ShellWriteCmd' ShellNum ShellFilterCmd( Cmd) endif endif enddo return ; --------------------------------------------------------------------------- defc ShellStart Cmd = arg( 1) call ShellStart( '', Cmd) ; --------------------------------------------------------------------------- ; Starts a new shell for the current file, if not already done and if Mode = ; SHELL. Can be used to "reactivate" a shell, whose .command_shell_ output ; was saved before and then gets reloaded. ; This is executed by defc Mode. ; This was executed by the fileswitched hook. -> Overhead. defc ShellRestore universal shell_index do once = 1 to 1 getfileid fid RestoredFile = .filename ShellNum = GetAVar( fid'.shellnum') if ShellNum then leave endif -- Mode is set after file loading Mode = GetMode() if Mode <> 'SHELL' then leave endif if IsAShell() then leave endif --dprintf( 'ShellRestore: .filename = '.filename) -- Restore shell file call ShellStart( RestoredFile) ShellNum = shell_index -- Determine previous dir from prompt line display -3 ShellDeleteTrailingBlankLines() .lineg = .last endline PrevDir = ShellGetPrevDir() display 3 if PrevDir <> '' then if directory() <> PrevDir then CdCmd = 'cdd' EnquoteFilespec( PrevDir) --dprintf( 'ShellRestore: ShellWriteCmdHidden' ShellNum CdCmd) 'ShellWriteCmdHidden' ShellNum CdCmd endif endif -- Restore env from .env file -- 'restartfilename' is saved in RestoreFilename. RestartFilename = GetFileAVar( 'restartfilename') if RestartFilename <> '' then EnvFile = RestartFilename'.env' else EnvFile = RestoredFile'.env' endif 'ShellLoadEnv 'EnquoteFileSpec( EnvFile) call pSave_Pos( SavedPos) -- Set bookmarks for prompts. 'Last' is already added. Start at 'Last'. .lineg = .last .col = 1 do l = .last to 1 by -1 -- Just an upper limit, instead of forever call ShellGotoNextPrompt( 'P') if rc then leave endif -- Add bookmark for found prompt -- Recognize Cmd call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) if CurPrompt then -- Set new bookmark bmline = .line bmcol = 1 bmname = CurCmd if CurCmd = '' then bmname = '(no cmd)' endif SetBookmark( 3, bmline, bmcol, bmname) endif enddo -- Search for 'Directory of' lines and add bookmarks for that do l = 1 to .last LineStr = textline( l) -- For a 'Directory of' line, add bookmark -- for later restore after editing if IsADirectoryOfLine( LineStr) then -- Set new bookmark bmline = .line bmcol = 1 bmname = LineStr SetBookmark( 3, bmline, bmcol, bmname) endif enddo call pRestore_Pos( SavedPos) enddo ; =========================================================================== ; Process ShellRestore at fileswitched ; =========================================================================== ; --------------------------------------------------------------------------- ; Reactivates a shell buffer, if no ShellHandle exists. ; Processes that at defselect instead of at defload to save file loading ; time. ; Tips for hook debugging: ; o Use HookShow to display all added cmds. ; o After changing or deactivating a hook, an EPM restart may be required. ; Otherwise the previous hook contents might not be flushed. definit -- After making changes for definit, restart EPM 'HookAdd fileswitched ShellRestore' ; =========================================================================== ; ShellInitCmd ; =========================================================================== ; --------------------------------------------------------------------------- defc ShellSetInitCmd KeyPath = '\NEPMD\User\Shell\InitCmd' WriteConfigKey( KeyPath, strip( arg( 1))) ; --------------------------------------------------------------------------- defc ShellInitCmdDlg KeyPath = '\NEPMD\User\Shell\InitCmd' InitCmd = QueryConfigKey( KeyPath) Title = 'Init command for shell windows' Text = 'Enter new value:' Text = Text''copies( ' ', Max( 100 - length( Text), 0)) Entry = InitCmd parse value EntryBox( Title, '', Entry, 0, 240, atoi( 1) || atoi( 0) || atol( 0) || Text) with Button 2 NewInitCmd \0 NewInitCmd = strip( NewInitCmd) if Button = \1 & NewInitCmd <> InitCmd then WriteConfigKey( KeyPath, NewInitCmd) endif ; =========================================================================== ; ShellKill ; =========================================================================== ; --------------------------------------------------------------------------- ; Destroys a shell object. ; Syntax: ShellClose [] [NOCLOSE] defc ShellClose, ShellKill, Shell_Kill ValidFlags = 'NOCLOSE' call ParseFlags( arg( 1), ValidFlags, Args, Flags) ShellNum = Args fClose = (upcase( Flags) <> 'NOCLOSE') if ShellNum = '' & IsAShell() then ShellNum = GetFileAVar( 'shellnum') endif if ShellNum = '' then 'SayError 'NOT_IN_SHELL__MSG return endif ShellFid = GetAVar( 'shell_f'ShellNum) ShellHandle = GetAVar( 'shell_h'ShellNum) -- Execute 'xcom quit' before Sue_Free to be able to cancel this proc. -- 'xcom quit' opens the 'Quitting file' dialog and offers to save, -- discard or cancel. if ShellFid <> '' then if fClose then getfileid curfid activatefile ShellFid -- This opens the 'Quitting file' dialog if modified and allows to -- cancel it 'xcom quit' endif call SetAVar( 'shell_f'ShellNum, '') call SetAVar( 'shell_h'ShellNum, '') call SetAVar( ShellFid'.shellnum', '') if fClose then if curfid <> ShellFid then activatefile curfid endif endif endif if ShellHandle <> '' then -- Must come after 'xcom quit' for to enable cancel retval = Sue_Free( ShellHandle) if retval then 'SayError 'ERROR__MSG retval SHELL_ERROR3__MSG endif call SetAVar( 'shell_h'ShellNum, '') endif ; =========================================================================== ; ShellWrite ; =========================================================================== ; Cmd output arrives asynchronously. A Cmd is determined from the output. ; IgnoreLevel is stored with a Cmd to control the output for the command ; shell window. ; Current ignore level ; 0 cmd not ignored (normal cmd) or not found in array ; 1 cmd quiet (only the prompt is changed) ; 2 prompt after a quiet cmd (not set by a ShellWrite* cmd) ; 3 cmd hidden (no output at all) ; 4 prompt hidden and cmd quiet (used at ShellStart only) ; 5 next output line is the first after a quiet ShellStart ; --------------------------------------------------------------------------- ; Syntax: ShellWrite [] [] ; If first word is not a number, then last opened shell is used as ; . defc ShellWrite, Shell_Write call ShellWriteParseCommon( arg( 1), ShellNum, Input, fCallAgain, 'ShellWrite') if fCallAgain then return endif if arg( 1) = '' then 'ShellHistory' return endif -- Write to shell fCmd = 0 fAppendLineEnd = 1 --dprintf( 'ShellWrite: -------- Input = 'Input' --------') call ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) ; --------------------------------------------------------------------------- ; Syntax: ShellWriteCmd [] [] ; If first word is not a number, then last opened shell is used as ; . defc ShellWriteCmd call ShellWriteParseCommon( arg( 1), ShellNum, Input, fCallAgain, 'ShellWriteCmd') if fCallAgain then return endif -- Store IgnoreLevel and Cmd IgnoreLevel = 0 imax = GetFileAVar( 'shellcmd.0') if imax = '' then imax = 0 endif i = imax + 1 SetFileAVar( 'shellcmd.'i, IgnoreLevel Input) SetFileAVar( 'shellcmd.0', i) -- Write to shell fCmd = 1 fAppendLineEnd = 1 --dprintf( 'ShellWriteCmd: -------- Input = |'Input'| --------') call ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) ; --------------------------------------------------------------------------- ; Syntax: ShellWriteCmdQuiet [] [] ; If first word is not a number, then last opened shell is used as ; . ; The output of is not displayed. The previous last line (the empty ; prompt) is replaced with a new one. defc ShellWriteCmdQuiet call ShellWriteParseCommon( arg( 1), ShellNum, Input, fCallAgain, 'ShellWriteCmdQuiet') if fCallAgain then return endif -- Store IgnoreLevel and Cmd IgnoreLevel = 1 imax = GetFileAVar( 'shellcmd.0') if imax = '' then imax = 0 endif i = imax + 1 SetFileAVar( 'shellcmd.'i, IgnoreLevel Input) SetFileAVar( 'shellcmd.0', i) fCmd = 1 fAppendLineEnd = 1 --dprintf( 'ShellWriteCmdQuiet: -------- Input = |'Input'| --------') call ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) ; --------------------------------------------------------------------------- ; Syntax: ShellWriteCmdHidden [] [] ; If first word is not a number, then last opened shell is used as ; . ; Both the output of and the last line are not displayed. The ; previous output is not changed. defc ShellWriteCmdHidden call ShellWriteParseCommon( arg( 1), ShellNum, Input, fCallAgain, 'ShellWriteCmdHidden') if fCallAgain then return endif SavedModify = .modify -- Store IgnoreLevel and Cmd IgnoreLevel = 3 imax = GetFileAVar( 'shellcmd.0') if imax = '' then imax = 0 endif i = imax + 1 SetFileAVar( 'shellcmd.'i, IgnoreLevel Input) SetFileAVar( 'shellcmd.0', i) fCmd = 1 fAppendLineEnd = 1 --dprintf( 'ShellWriteCmdHidden: -------- Input = |'Input'| --------') call ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) .modify = SavedModify ; --------------------------------------------------------------------------- ; Syntax: ShellWriteChars [] [] ; If first word is not a number, then last opened shell is used as ; . defc ShellWriteChars call ShellWriteParseCommon( arg( 1), ShellNum, Input, fCallAgain, 'ShellWriteChars') if fCallAgain then return endif -- Split at line end and strip it pLF = pos( \10, Input) if pLF then Input = leftstr( Input, pLF) endif if rightstr( Input, 1) = \13 then Input = leftstr( Input, length( Input) - 1) endif --dprintf( 'ShellWriteChars: Input = 'Input', pLF = 'pLF) -- Type Input fCmd = 0 fAppendLineEnd = 0 --dprintf( 'ShellWriteChars: -------- Input = 'Input', pLF = 'pLF' --------') call ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) if pLF then -- Write cmd or line 'ShellEnterLine' endif ; --------------------------------------------------------------------------- ; This returns fCallAgain and ensures that ShellNum is set. ; - If ShellNum is specified, it is verified. If it's of current file, ; fCallAgain is set to 0. ; - If ShellNum is not in ring or not specified, a new shell is opened and ; fCallAgain is set to 1. ; - If ShellNum is not specified, the last opened shell is used and ; fCallAgain is set to 1. defproc ShellVerifyShellNum( var fCallAgain, var ShellNum) universal shell_index do once = 1 to 1 fCallAgain = 0 fShellFound = 0 -- Keep topmost shell if IsAShell() then ShellNum = GetFileAVar( 'shellnum') getfileid ShellFid fShellFound = 1 --dprintf( ' ShellVerifyShellNum 1: ShellNum = 'ShellNum) leave endif -- Switch to specified ShellNum if in ring if IsNum( ShellNum) then ShellFid = GetAVar( 'shell_f'ShellNum) if validatefileid( ShellFid) then pActivateFile( ShellFid) fShellFound = 1 --dprintf( ' ShellVerifyShellNum 2: ShellNum = 'ShellNum) fCallAgain = 1 leave endif endif -- Switch to last created shell if in ring do s = shell_index to 1 by -1 ShellFid = GetAVar( 'shell_f'shell_index) if validatefileid( ShellFid) then ShellNum = GetAVar( ShellFid'.shellnum') pActivateFile( ShellFid) fShellFound = 1 --dprintf( ' ShellVerifyShellNum 3: ShellNum = 'ShellNum) fCallAgain = 1 leave endif enddo -- Open a new shell if none in ring if not fShellFound then 'Shell' --dprintf( ' ShellVerifyShellNum 4: ShellNum = 'ShellNum) fCallAgain = 1 leave endif enddo -- once return ; --------------------------------------------------------------------------- defproc ShellWriteParseCommon( Args, var ShellNum, var Input, var fCallAgain, CallingCmd) parse value Args with ShellNum Input ShellNum = strip( ShellNum) Input = strip( Input, 'L') if ShellNum = '' then -- nop elseif not IsNum( ShellNum) then ShellNum = '' parse arg Input Input = strip( Input, 'L') endif --dprintf( ' ShellWriteCmd after parse : ShellNum = 'ShellNum', Input = |'Input'|') -- Ensure that ShellNum is set call ShellVerifyShellNum( fCallAgain, ShellNum) --dprintf( 'ShellWriteCmd after verify: ShellNum = 'ShellNum', Input = 'Input) if fCallAgain then -- After switching to the shell, Input must be posted. -- Otherwise it won't be written to the shell. 'PostMe 'CallingCmd ShellNum Input endif return ; --------------------------------------------------------------------------- defproc ShellWriteCommon( ShellNum, fCmd, fAppendLineEnd, Input) ShellHandle = GetAVar( 'Shell_h'ShellNum) -- Determine if cmd output should be ignored fIgnored = 0 do once = 1 to 1 imax = GetFileAVar( 'shellcmd.0') if not imax then leave endif do i = 1 to imax NextEntry = GetFileAVar( 'shellcmd.'i) if not NextEntry then iterate endif parse value NextEntry with NextIgnoreLevel NextCmd if NextIgnoreLevel = 0 then leave elseif Input = NextCmd then if NextIgnoreLevel = 1 then fIgnored = 1 elseif NextIgnoreLevel = 3 then fIgnored = 1 endif leave endif enddo enddo -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended -- Determine last and current prompt values fNewFile = 0 do once = 1 to 1 if .last <> 1 then leave elseif GetFileAVar( 'shellstartstate') >= 3 then leave elseif textline( .line) <> '' then leave endif fNewFile = 1 enddo if fNewFile then -- System prompt value LastPrompt = ShellGetPromptValue() CurPrompt = LastPrompt LastCmd = '' CurCmd = '' else call ShellParseLinePromptCmd( .last, LastPrompt, LastCmd) call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) endif if not fIgnored then -- Prepare prompt for echoed cmd to append if fCmd then --dprintf( 'ShellWriteCommon: fAppendLineEnd = 'fAppendLineEnd', no app is waiting') if .line = .last then --dprintf( 'ShellWriteCommon: Delete CurCmd = 'CurCmd', .line = '.line) .col = length( CurPrompt) + 1 eraseendline elseif CurPrompt then -- Restore the previous Cmd from bookmark getfileid ShellFid bmidx = GetBookmarkIdx( ShellFid, .line, 1) bmname = GetBmName( ShellFid, bmidx) --dprintf( '# ShellWriteCommon: Restore bmname = 'bmname', .line = '.line) .col = length( CurPrompt) + 1 -- Eraseendline preserves the bookmark in col 1 eraseendline keyin bmname -- Ensure that text after prompt in last line is deleted .line = .last .col = length( LastPrompt) + 1 eraseendline endif -- Restore line elseif IsADirectoryOfLine() then -- Restore the previous 'Directory of' line from bookmark getfileid ShellFid bmidx = GetBookmarkIdx( ShellFid, .line, 1) bmname = GetBmName( ShellFid, bmidx) bmline = .line bmcol = 1 -- Replaceline deletes the bookmark --dprintf( '# ShellWriteCommon: Restore bmname = 'bmname', .line = '.line) replaceline bmname -- Set the bookmark back to col 1 SetBookmark( 3, bmline, bmcol, bmname) --dprintf( '# ShellWriteCommon: Bookmark set on line 'bmline' to >'bmname'<') else -- Not a cmd -- Ensure that Input is written at the end, not just at cursor .line = .last endline endif -- fCmd endif -- not fIgnored WriteBuff = Input if fAppendLineEnd then WriteBuff = WriteBuff\13\10 endif -- Write to shell --dprintf( 'ShellWriteCommon: Write Input = 'Input', fIgnored = 'fIgnored) rc = Sue_Write( ShellHandle, WriteBuff, BytesMoved) -- Check rc if rc | (BytesMoved <> length( WriteBuff)) then 'SayError ShellWriteCommon: rc =' rc', byteswritten =' BytesMoved 'of' length( WriteBuff) 'ShellBreak' endif return -- The above code is not really complete. It should also deal with -- situations where only part of the data to be written is written. It -- needs to keep the unwritten data around and put it out again during -- the "NowCanWriteShell" comand processing (todo). ; --------------------------------------------------------------------------- ; Shell object sends this command to inform the editor that there is ; room for additional data to be written. defc NowCanWriteShell -- Use ShellWrite with argumentstring 'SayError 'SHELL_OBJECT__MSG arg( 1) SHELL_READY__MSG ; =========================================================================== ; Current dir and E cmd ; =========================================================================== ; --------------------------------------------------------------------------- ; Instead of writing to a tmp file, write Cmd to shell, redirect output to ; LogFile, read line LineNum and disable Cmd output to shell file. ; This is executed at shell start to get the initial prompt line. ; If LineNum > last line of LogFile, avoid an error message and return ; empty. defproc ShellGetCmdOutput Cmd = arg( 1) LineNum = arg( 2) -- Which line of LogFile should be read for returned value if LineNum = '' then LineNum = 1 endif OutputLine = '' display -1 do once = 1 to 1 if Cmd = '' then leave endif if not IsAShell() then leave endif LogFile = GetTmpPath()'nepmd\shellcmd.log' if Exist( LogFile) then DeleteFile( LogFile) endif -- ShellStart calls ShellGetCurDir() -> ShellGetCmdOutput. Need to use -- ShellWriteCmdQuiet, not ShellWriteCmdHidden to put the initial -- prompt line out. 'ShellWriteCmdQuiet 'Cmd'>'LogFile getfileid ShellFid -- Delay loop until file exists fLogFileFound = 0 TimeOutMs = 1000 NumIterations = 1000 -- 1000 means: a check every 1 ms do i = 1 to NumIterations if Exist( LogFile) then --dprintf( 'LogFile found after '(i - 1) * TimeOutMs / NumIterations' ms') fLogFileFound = 1 leave else Sleep( TimeOutMs / NumIterations) endif enddo if not fLogFileFound then rc = 2 leave endif -- xcom e /d always loads a file. If it doesn't exist, it's created. 'xcom e /d /q 'LogFile if rc then --dprintf( sayerrortext( rc)) activatefile ShellFid leave endif getfileid LogFid if LineNum > .last then OutputLine = '' else getline OutputLine, LineNum, LogFid endif activatefile LogFid .modify = 0 'xcom q' DeleteFile( LogFile) activatefile ShellFid enddo display 1 return OutputLine ; --------------------------------------------------------------------------- ; Instead of writing to a tmp file, write Cmd to shell, redirect output to ; LogFile, read line LineNum and disable Cmd output to shell file. defproc ShellGetCurDir LogFileLine = 1 CurDir = ShellGetCmdOutput( 'cd', LogFileLine) return CurDir defc ShellGetCurDir CurDir = ShellGetCurDir() dprintf( 'CurDir = 'CurDir', rc = 'rc) 'SayError CurDir = 'CurDir', rc = 'rc ; =========================================================================== ; ShellOutput ; =========================================================================== ; --------------------------------------------------------------------------- ; Shell object sends this command to inform the editor that there is ; additional data to be read. defc NowCanReadShell parse arg ShellNum . if not IsNum( ShellNum) then 'SayError NowCanReadShell: 'INVALID_ARG__MSG '"'arg( 1)'"' return endif call ShellOutputLines( ShellNum) ; --------------------------------------------------------------------------- ; - Fixed to handle LF-terminated lines correctly. ; - Recognize if an app is waiting for user input (then last line is not the ; EPM prompt). ; - Stores line and col after output to shelloutputpos. ; - Right margin setting of current shell is not respected. ; - The prompt after executing a start command (maybe "start epm config.sys") ; changed to "epm:F:\>" instead of "epm: F:\ >". This should be fixed now: ; Slow output and missing spaces in the prompt with "do while RestLineStr <> ''". ; Replaced with "do forever" and "leave". ; Example: "svn ci *" and discarding log changes returns to the initial ; shell and prompts for "(a)bort, (c)ontinue, (e)dit:" ; After pressing "a", the prompt appears but without spaces. ; - But the main problem still remains (EPM bug): ; After a "start epm" command, SUE_readln sends all data very slowly, ; sometimes byte per byte. This can be checked by undoing the output of a ; "dir" command. A new undo state is created for every line of the dir ; output then. defproc ShellOutputLines( ShellNum) LastLineStr = '' ShellFid = GetAVar( 'shell_f'ShellNum) ShellHandle = GetAVar( 'shell_h'ShellNum) pActivateFile( ShellFid) --dprintf( 'ShellOutputLines: .last = '.last) -- Determine StartNumLines to distiguish Restore (> 1) from new file (1). -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended if GetFileAVar( 'shellstartstate') < 3 then StartNumLines = .last else StartNumLines = 0 endif TotalBytesMoved = 0 do forever -- until BytesMoved = 0 -- Read next line -- SUE_readln doesn't handle LF as line end, received from the app. -- Therefore it must be parsed here again. -- BTW: MORE.COM has the same bug. ReadBuff = copies( ' ', MAXCOL) retval = Sue_ReadLn( ShellHandle, ReadBuff, BytesMoved) ReadBuff = leftstr( ReadBuff, BytesMoved) --dprintf( '* ShellOutputLines: proc start: LastLineStr = |'textline( .last)'|') --dprintf( 'ShellOutputLines: ReadBuff = >>'translate( ReadBuff, \1\2'~', \10\13\20)'<<') if not BytesMoved then leave endif TotalBytesMoved = TotalBytesMoved + BytesMoved -- Both CR and LF end a line (read by Sue_ReadLn) and start a new one. if ReadBuff = \13 then iterate -- ignore CR endif RestLineStr = ReadBuff do forever -- until RestLineStr == '' ---- Parse RestLineStr ---- -- Split RestLineStr at further LF chars into NextLineStr and -- RestLineStr. A read line usually starts with LF. (CR is already -- filtered out at this point.) pLF = pos( \10, RestLineStr, 2) if pLF > 0 then --dprintf( '* LF found in RestLineStr = 'leftstr( translate( RestLineStr, \1\2'~', \10\13\20), Min( 40, length( RestLineStr)))) NextLineStr = leftstr( RestLineStr, pLF - 1) RestLineStr = substr( RestLineStr, pLF) else NextLineStr = RestLineStr RestLineStr = '' endif --dprintf( 'ShellOutputLines : NextLineStr = >'translate( NextLineStr, \1\2'~', \10\13\20)'<') --dprintf( 'ShellOutputLines : RestLineStr = >'translate( RestLineStr, \1\2'~', \10\13\20)'<') -- Determine and strip off leading LF if leftstr( NextLineStr, 1) = \10 then -- LF is lineend TextStr = substr( NextLineStr, 2) fLeadingLF = 1 else TextStr = NextLineStr fLeadingLF = 0 endif --dprintf( 'ShellOutputLines : TextStr = >'TextStr'< fLeadingLF = 'fLeadingLF) --dprintf( 'ShellOutputLines : shellstartstate = 'GetFileAVar( 'shellstartstate')'--------------') -- Ignore ANSI Esc sequences. Determine if current line should be -- ignored because of quiet output set by ShellWriteCmdQuiet. -- If a prompt, followed by a cmd, store cmd as bookmark. -- If a cmd that changes prompt, store its value. call ShellFilterReadLine( TextStr, fLeadingLF) -- Write line to current file fProcessTextStr = 1 -- This is required even after ignore at start was added to -- ShellIsCmdIgnored. It ensures that empty lines before the -- replaced prompt are correct for all restored shell files. -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended if GetFileAVar( 'shellrestorestate') < 2 then -- Current restore state -- 1 restore started -- 2 restore ended -- Omit too early calls of ShellProcessReadLine during start fProcessTextStr = 0 endif if fProcessTextStr then --dprintf( 'ShellOutputLines : fProcessTextStr = 1, call ShellProcessReadLine( 'TextStr', 'fLeadingLF')') call ShellProcessReadLine( TextStr, fLeadingLF) endif if RestLineStr == '' then -- Get next ReadBuf from output stream leave endif enddo -- forever until RestLineStr == '' enddo -- forever until BytesMoved = 0 if TotalBytesMoved then -- Read the last output line. Place the cursor after RestBuffStr. -- This may be after the last prompt. getline LastLineStr, .last .line = .last .col = Min( MAXCOL, length( LastLineStr) + 1) call ShellParseLinePromptCmd( .last, LastPrompt, LastCmd) do once = 1 to 1 if not LastPrompt then leave endif -- Prepare to set bookmark 'Last' bmline = .last bmcol = 1 bmval = 'Last' -- In case of slow output, each char of the prompt arrives separately. -- If the prompt has a trailing space, LastPrompt parsing is in both -- cases true and the same bookmark is added two times. SavedRc = rc Checkidx = GetBookmarkIdx( ShellFid, bmline, bmcol) if not rc then CheckName = GetBmName( ShellFid, Checkidx) if CheckName = bmval then -- Already set leave endif endif rc = SavedRc -- Set bookmark SetBookmark( 3, bmline, bmcol, bmval) --dprintf( '#ShellOutputLines: Bookmark set on line 'bmline' to >'bmval'<') enddo -- Go to end .line = .last endline -- Store pos. after output PrevOutputPos = GetFileAVar( 'shelloutputpos') call SetFileAVar( 'shelloutputpos', .line .col) --dprintf( 'ShellOutputLines: -------- End of output at '.line .col' -------- PrevOutputPos = 'PrevOutputPos) -- Start ended -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended if GetFileAVar( 'shellstartstate') < 3 then -- Current restore state -- 1 restore started -- 2 restore ended if GetFileAVar( 'shellrestorestate') < 2 then SetFileAVar( 'shellrestorestate', 2) endif 'DiscardChanges' if GetFileAVar( 'shellrestoremodify') then .modify = 1 DropFileAVar( 'shellrestoremodify') endif SetFileAVar( 'shellstartstate', 3) --dprintf( 'ShellOutputLines: shellstartstate just set to 3 (start ended)') endif endif -- TotalBytesMoved --dprintf( '* ShellOutputLines: proc end : LastLineStr = |'textline( .last)'| TotalBytesMoved = 'TotalBytesMoved) -- ShellOutputLines is multiple times called on shell start, therefrom -- only 1x with non-empty ReadBuff. return ; --------------------------------------------------------------------------- ; Syntax: ShellGetCmdIgnoreLevel( [, fDelete]) ; Default option is fDelete = 0. That means, is not deleted from the ; array when found. That is used for to query the IgnoreLevel without ; modification. ; Determines if CurCmd should be ignored. Returns IgnoreLevel: ; 0 cmd not ignored (normal cmd) or not found in array ; 1 cmd quiet (only the prompt is changed) ; 2 prompt after a quiet cmd (not set by a ShellWrite* cmd) ; 3 cmd hidden (no output at all) ; 4 prompt hidden and cmd quiet (used at ShellStart only) ; 5 next output line is the first after a quiet ShellStart defproc ShellGetCmdIgnoreLevel( CurCmd) do once = 1 to 1 fDelete = (arg( 2) = 1) PrevIgnoreLevel = GetFileAVar( 'shellcurrentignorelevel') --dprintf( 'ShellGetCmdIgnoreLevel for 'CurCmd', fDelete = 'fDelete', arg( 2) = 'arg( 2)) if PrevIgnoreLevel = 4 then IgnoreLevel = 4 leave endif IgnoreLevel = 0 imax = GetFileAVar( 'shellcmd.0') --dprintf( ' ShellGetCmdIgnoreLevel: imax = 'imax', CurCmd = |'CurCmd'|') if imax = '' then imax = 0 endif do i = 1 to imax NextEntry = GetFileAVar( 'shellcmd.'i) --dprintf( ' ShellGetCmdIgnoreLevel: shellcmd = 'NextEntry', i = 'i) if not NextEntry then iterate endif parse value NextEntry with NextIgnoreLevel NextCmd if CurCmd = NextCmd & NextCmd then IgnoreLevel = NextIgnoreLevel --dprintf( '** ShellGetCmdIgnoreLevel: ### Set IgnoreLevel = 'IgnoreLevel' for CurCmd = |'CurCmd'|') --dprintf( ' ShellGetCmdIgnoreLevel: ### Set IgnoreLevel = 'IgnoreLevel' for CurCmd = |'CurCmd'|') -- Remove CurCmd from cmd list, after it was found if fDelete then call SetFileAVar( 'shellcmd.'i, '') --dprintf( ' ShellGetCmdIgnoreLevel: ### Remove shellcmd after found = 'NextEntry) endif leave -- i endif enddo -- i = 1 to imax if IgnoreLevel < 3 then -- Handle prompt changes by a prompt cmd call ShellProcessPromptCmd( CurCmd) --dprintf( ' ShellGetCmdIgnoreLevel: call ShellProcessPromptCmd( 'CurCmd')') endif enddo return IgnoreLevel ; --------------------------------------------------------------------------- ; Ignores ANSI Esc sequences. Determines if current line should be ; ignored because of quiet output set by ShellWriteCmdQuiet. Sets array var ; 'shellcurrentignorelevel' to IgnoreLevel stored by ShellWriteCommon with Cmd. defproc ShellFilterReadLine( var TextStr, var fLeadingLF) -- Filter out ANSI Esc sequences do forever pEscStart = pos( \27'[', TextStr) if pEscStart = 0 then leave endif pEscEnd = verify( TextStr, 'ABCDHJKnfRhlmpsu', 'M', pEscStart + 2) if pEscEnd = 0 then leave endif TextStr = delstr( TextStr, pEscStart, pEscEnd - pEscStart + 1) enddo -- Determine and save output line state -- Current line state -- 0 initial -- 1 prompt -- 2 echoed cmd -- 3 other PrevLineState = GetFileAVar( 'shelllinestate') call ShellParseStrPromptCmd( TextStr, CurPrompt, CurCmd) LastLineStr = textline( .last) call ShellParseLinePromptCmd( .last, LastPrompt, LastCmd) fNewCmd = 0 if not CurPrompt then --nop elseif CurCmd = '' then fNewCmd = 1 elseif ShellGetCmdIgnoreLevel( CurCmd) >= 0 then -- if found in shellcmd. array fNewCmd = 1 endif if fNewCmd then LineState = 1 -- prompt elseif PrevLineState = 1 & LastCmd = '' then LineState = 2 -- echoed cmd else LineState = 3 -- other endif call SetFileAVar( 'shelllinestate', LineState) -- Determine current cmd -- For the first output after previous empty prompt line, prepend the -- read prompt to the output string for the CheckLineStr parsing CheckLineStr = TextStr if LastPrompt & LineState = 2 then -- Append cmd to empty 'Last' prompt CheckLineStr = LastPrompt''TextStr endif -- Parse output stream into prompt and cmd call ShellParseStrPromptCmd( CheckLineStr, CheckPrompt, CheckCmd) --dprintf( ' ShellFilterReadLine: *** ShellParseStrPromptCmd( 'CheckLineStr', 'CheckPrompt', 'CheckCmd')') -- Query previous (at this point: current) IgnoreLevel PrevIgnoreLevel = GetFileAVar( 'shellcurrentignorelevel') -- Current ignore level -- 0 cmd not ignored (normal cmd) or not found in array -- 1 cmd quiet (only the prompt is changed) -- 2 prompt after a quiet cmd (not set by a ShellWrite* cmd) -- 3 cmd hidden (no output at all) -- 4 prompt hidden and cmd quiet (used at ShellStart only) -- 5 next output line is the first after a quiet ShellStart -- if prompt or echoed cmd if LineState then -- Redetermine and store current IgnoreLevel IgnoreLevel = PrevIgnoreLevel -- Cmd specified: look up for quiet cmds to set IgnoreLevel amd delete -- entry if found if CheckCmd then fDelete = 1 IgnoreLevel = ShellGetCmdIgnoreLevel( CheckCmd, fDelete) --dprintf( '** ShellFilerReadLine: IgnoreLevel = 'IgnoreLevel' = ShellGetCmdIgnoreLevel( 'CheckCmd', 'fDelete')') endif -- Prompt after an ignored cmd --dprintf( '** IgnoreLevel = 'IgnoreLevel', PrevIgnoreLevel = 'PrevIgnoreLevel', LineState = 'LineState', TextStr = 'TextStr) if LineState then if PrevIgnoreLevel = 1 then if not CheckCmd | IgnoreLevel = 0 then IgnoreLevel = 2 --dprintf( '** Set IgnoreLevel = 'IgnoreLevel) --dprintf( ' ShellFilerReadLine: LineState = 1 & PrevIgnoreLevel = 1 & (not CheckCmd | IgnoreLevel = 0): Set IgnorwLevel = 'IgnoreLevel) endif elseif IgnoreLevel = 1 & PrevIgnoreLevel = 5 then IgnoreLevel = 5 --dprintf( '** Set IgnoreLevel = 'IgnoreLevel) elseif PrevIgnoreLevel = 4 then IgnoreLevel = 5 --dprintf( '** Set IgnoreLevel = 'IgnoreLevel) elseif PrevIgnoreLevel = 5 then IgnoreLevel = 2 --dprintf( '** Set IgnoreLevel = 'IgnoreLevel) endif endif -- Store eventually changed current IgnoreLevel if IgnoreLevel <> PrevIgnoreLevel then call SetFileAVar( 'shellcurrentignorelevel', IgnoreLevel) endif endif --dprintf( ' ShellFilterReadLine: *** LineState = 'LineState', IgnoreLevel = 'IgnoreLevel' PrevIgnoreLevel = 'PrevIgnoreLevel) return ; --------------------------------------------------------------------------- ; Writes RestLineStr line-wise to current file. ; RestLineStr may contain additional LF chars. That is handled here. defproc ShellProcessReadLine( var TextStr, var fLeadingLF) getfileid ShellFid -- Determine if TextStr is the first line, the echoed cmd LineState = GetFileAVar( 'shelllinestate') -- Recognize starting shell ShellStartState = GetFileAVar( 'shellstartstate') -- Current ignore level -- 0 cmd not ignored (normal cmd) or not found in array -- 1 cmd quiet (only the prompt is changed) -- 2 prompt after a quiet cmd (not set by a ShellWrite* cmd) -- 3 cmd hidden (no output at all) -- 4 prompt hidden and cmd quiet (used at ShellStart only) -- 5 next output line is the first after a quiet ShellStart IgnoreLevel = GetFileAVar( 'shellcurrentignorelevel') --dprintf( ' ShellProcessReadLine: Current IgnoreLevel = 'IgnoreLevel', TextStr = |'TextStr'|') if wordpos( IgnoreLevel, '0 2') then -- Current start state -- 1 just started -- 2 first line being put out -- 3 start ended if ShellStartState < 3 then ---- Shell start: Put out TextStr ---- if ShellStartState < 2 then -- First line during start SetFileAVar( 'shellstartstate', 2) endif -- Insert first new line fReplaceEmptyTopLine = 0 if .last = 1 then if textline( .last) = '' then fReplaceEmptyTopLine = 1 endif endif if fReplaceEmptyTopLine then --dprintf( 'ShellProcessReadLine: 1 replaceline 'TextStr', '.last) replaceline TextStr, .last else insertline TextStr, .last + 1 endif .line = .last else -- ShellStartState < 3 ---- No shell start: Put out TextStr ---- --dprintf( 'ShellProcessReadLine: TextStr = >'TextStr'< fLeadingLF = 'fLeadingLF', IgnoreLevel = 'IgnoreLevel', LineState = 'LineState) -- Determine fAppend, fReplace or fInsert if IgnoreLevel = 2 then -- 2 = prompt after an ignored cmd -- Replace last fAppend = 0 fReplace = 1 fInsert = 0 elseif fLeadingLF then if LineState = 2 then -- 2 = echoed cmd -- Append last fAppend = 1 fReplace = 0 fInsert = 0 else -- Insert next fAppend = 0 fReplace = 0 fInsert = 1 endif else -- Append last fAppend = 1 fReplace = 0 fInsert = 0 endif --dprintf( ' ShellProcessReadLine: A-R-I = 'fAppend'-'fReplace'-'fInsert' 'TextStr) if fAppend then getline LastLineStr, .last --dprintf( ' ShellProcessReadLine: LastLineStr = |'LastLineStr'| .last = '.last) LastLen = length( LastLineStr) NextLen = length( TextStr) if LastLen + NextLen <= MAXCOL then -- Append TextStr to LastLineStr -- Instead of replaceline, keyin preserves bookmarks -- in LastLineStr. .line = .last .col = LastLen + 1 --dprintf( ' ShellProcessReadLine: keyin TextStr, LastLineStr = |'LastLineStr'|, TextStr = |'TextStr'|') keyin TextStr TextStr = LastLineStr''TextStr else -- Doesn't fit, must insert fInsert = 1 endif endif if fReplace then -- Replace last prompt = detete + insert --dprintf( 'ShellProcessReadLine: 2 deleteline '.last) deleteline .last -- Insert TextStr fInsert = 1 endif if fInsert then -- Insert TextStr --dprintf( 'ShellProcessReadLine: 3 insertline 'TextStr', '.last + 1) insertline TextStr, .last + 1 .line = .last --dprintf( ' ShellProcessReadLine: inserted TextStr = |'TextStr'| on .last = '.last) endif endif -- ShellStartState < 3 else ---- Bookmarks ---- -- Recognize Cmd call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) --dprintf( ' ShellProcessReadLineBuff: ShellParseLinePromptCmd( '.line', 'CurPrompt', 'CurCmd')') if CurPrompt & CurCmd then --dprintf( 'Change bookmark name for bmidx 'bmidxmax' to "'CurCmd'".') -- Get bmidx for last bookmark bmidxmax = GetFileAVar( 'bmname.0') -- Get name for it bmname = GetBmName( ShellFid, bmidxmax) if .line = .last & bmname = 'Last' then -- Change bookmark name from 'Last' to CurCmd --dprintf( "# ShellProcessReadLineBuff: Bookmark name with index "bmidxmax" changed from 'Last' to >"CurCmd"<") call SetFileAVar( 'bmname.'bmidxmax, CurCmd) else -- Set new bookmark bmline = .line bmcol = 1 bmname = CurCmd SetBookmark( 3, bmline, bmcol, bmname) --dprintf( '# ShellProcessReadLineBuff: Bookmark set on line 'bmline' to >'bmname'<') endif endif -- CheckPrompt & CheckCmd -- For a 'Directory of' line, add bookmark -- for later restore after editing if IsADirectoryOfLine( TextStr) then -- Set new bookmark bmline = .last bmcol = 1 bmname = TextStr SetBookmark( 3, bmline, bmcol, bmname) --dprintf( '# ShellProcessReadLineBuff: Bookmark set on line 'bmline' to >'bmname'<') endif --dprintf( ' ShellProcessReadLine, end: IgnoreLevel = 'IgnoreLevel', TextStr = |'TextStr'|') endif return ; =========================================================================== ; ShellBreak ; =========================================================================== ; --------------------------------------------------------------------------- ; Sends a Break to a shell object defc ShellBreak, Shell_Break parse arg shellnum . if ShellNum = '' & IsAShell() then ShellNum = GetFileAVar( 'shellnum') endif if ShellNum = '' then 'SayError 'NOT_IN_SHELL__MSG return endif ShellHandle = GetAVar( 'shell_h'ShellNum) if ShellHandle = '' then return endif -- Confirm on a prompt line call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) if CurPrompt then refresh if MBID_OK <> WinMessageBox( 'Sending a Break signal not required', -- title 'Apparently you are on a prompt line and there is'\n || 'no action to send a Break to.'\n\n || 'Do you really want to send a Break signal?', MB_OKCANCEL + MB_QUERY + MB_DEFBUTTON1 + MB_MOVEABLE) then return endif endif -- Send a break signal to the shell object retval = Sue_Break( ShellHandle) if retval then if retval = 162 then 'SayError A signal is already pending, rc = 'retval' from SUE_break' else -- rc = 184 here means either: No process to break or Break not possible. 'SayError 'ERROR__MSG retval 'sending break to 'SHELL_OBJECT__MSG endif -- Pop up a MsgBox and ask the user before killing the shell -- Sideeffect: the command is paused. refresh if MBID_OK <> WinMessageBox( 'Error sending Break signal', -- title 'The Break signal, sent to the shell object, was'\n || 'not successful, at least not immediately.'\n\n || 'Do you want to restart the shell object?'\n || '(The path will be restored, but any special'\n || 'environment will be lost.)', MB_OKCANCEL + MB_QUERY + MB_DEFBUTTON1 + MB_MOVEABLE) then return endif -- Kill the shell -- Sending the kill signal will force a kill immediately retval = Sue_Free( ShellHandle) if retval then if retval = 162 then 'SayError A signal is already pending, rc = 'retval' from SUE_free' elseif retval = 184 then 'SayError Shell object is already killed, rc = 'retval' from SUE_free' else 'SayError 'ERROR__MSG retval SHELL_ERROR3__MSG endif -- Ignore errors here, most likely the shell object will be killed delayed else 'SayError Shell object was killed successfully' endif -- Create a new shell object, keeping ShellNum ShellHandle = '????' retval = Sue_New( ShellHandle, ShellNum) if retval then 'SayError 'ERROR__MSG retval SHELL_ERROR1__MSG else call SetAVar( 'shell_h'ShellNum, ShellHandle) InitCmd = '' ShellValue = ShellGetPromptValue() InitCmd = '@prompt 'ShellValue 'ShellWriteCmdQuiet' ShellNum InitCmd -- Determine previous work dir display -3 .lineg = .last endline Dir = ShellGetPrevDir() display 3 if Dir <> '' then CdCmd = 'cdd' Dir 'ShellWriteCmdQuiet' ShellNum CdCmd endif endif endif ; =========================================================================== ; Key definitions ; =========================================================================== ; --------------------------------------------------------------------------- ; Enhanced for filename completion. Prepends 'cd ' to input if a directory. ; Removes trailing \ from directories for 'cd'. ; Works with spaces in filenames and surrounding "...". ; ECHO must be ON. That is the default setting in CMD.EXE, but not in ; 4OS2.EXE. Otherwise no prompt is inserted after the command execution and ; further commands won't work (CMD.EXE) or the command is deleted (4OS2.EXE). ; Therefore ECHO ON must be executed _after_ every call of 4OS2.EXE. defproc ShellFilterCmd( Input) -- Process alias in Input KeyPath = '\NEPMD\User\Shell\Alias' on = (QueryConfigKey( KeyPath) <> 0) if on then Input = ShellAliasResolve( Input) endif -- Parse Input into CmdWord, CmdArgs and CmdName fCmdWordEnquoted = 0 if leftstr( Input, 1) = '"' then fCmdWordEnquoted = 1 parse value Input with '"'CmdWord'"' CmdArgs else parse value Input with CmdWord CmdArgs endif CmdName = upcase( GetFileSpec( 'B', CmdWord)) --dprintf( ' ShellFilterCmd( 'Input'): CmdWord = 'CmdWord', CmdArgs = 'CmdArgs', CmdName = 'CmdName ) -- Handle the silly M$ syntax extension for CD like "cd\", "cd.." etc. -- Not required for CMD.EXE, just for to determine CmdName. if wordpos( leftstr( upcase( CmdWord), 3), 'CD\ CD.') then CmdArgs = substr( CmdWord, 3) CmdWord = 'cd' CmdName = 'CD' endif -- Prepend "cd" if no CmdName given (true for a trailing '\') if CmdName = '' & CmdWord <> '' & CmdArgs = '' then CmdArgs = CmdWord CmdWord = 'cd' CmdName = 'CD' fCmdWordEnquoted = 0 -- Strip trailing \ from CmdArgs if rightstr( CmdArgs, 1) = '\' & CmdArgs <> '\' & rightstr( CmdArgs, 2) <> ':\' then CmdArgs = strip( CmdArgs, 'T', '\') endif CmdArgs = EnquoteFileSpec( CmdArgs) endif if CmdName = '4OS2' then -- Insert "echo on" when 4os2 is called if CmdArgs = '' then CmdArgs = 'echo on' -- Don't append "echo on" if already appended as CmdArgs elseif upcase( strip( CmdArgs)) = 'ECHO ON' then -- nop else CmdArgs = 'echo on&'CmdArgs endif endif --dprintf( ' ShellFilterCmd: Input = 'Input', CmdName = 'CmdName', CmdArgs = 'CmdArgs) -- Reconcatenate Input args if fCmdWordEnquoted = 0 then Input = CmdWord else Input = '"'CmdWord'"' endif if CmdArgs <> '' then Input = Input CmdArgs endif --dprintf( ' ShellFilterCmd: New Input = 'Input) return Input ; --------------------------------------------------------------------------- defc ShellEnterLine NormalCmd = strip( arg( 1)) fExecNormalCmd = 1 SavedRc = rc do once = 1 to 1 --dprintf( 'ShellEnterLine: IsADirList() = 'IsADirList()) ---- Dir list ---- if IsADirList() then call DirProcessDirectoryOfLine() if not rc then -- Processed fExecNormalCmd = 0 leave endif endif if not IsAShell() then leave endif ---- Shell ---- -- Determine prompt and cmd at last line and current line CurPrompt = '' CurCmd = '' PromptVal = ShellGetPromptValue() SearchStr = ShellPromptToSearchStr( PromptVal) call ShellParseLinePromptCmd( .last, LastPrompt, LastCmd) call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) -- Determine fWriteToApp if CurPrompt = '' then fWriteToApp = 1 else fWriteToApp = 0 endif -- Determine Input from current line if fWriteToApp then --dprintf( 'ShellEnterLine: App is waiting for user input') parse value GetFileAVar( 'shelloutputpos') with lastl lastc if lastc = '' then leave endif Input = '' l = lastl do while l <= .line getline line, l if l = lastl then startc = lastc else startc = 1 endif Input = Input''substr( line, startc) if l = .last then insertline '', .last + 1 leave else l = l + 1 endif enddo else --dprintf( 'ShellEnterLine: CurCmd before ShellFilterCmd = 'CurCmd) CurCmd = ShellFilterCmd( CurCmd) --dprintf( 'ShellEnterLine: CurCmd after ShellFilterCmd = 'CurCmd) Input = CurCmd endif if Input == '' then -- Ask for Input if fWriteToApp then 'ShellInputChars' else 'ShellHistory' endif else -- Write Input to shell if fWriteToApp then 'ShellWrite' Input else -- Reset shellcmd before cmd is executed by user imax = GetFileAVar( 'shellcmd.0') if imax > 0 then do i = 1 to imax DropFileAVar( 'shellcmd.'i) enddo endif SetFileAVar( 'shellcmd.0', 0) --dprintf( 'ShellEnterLine: Reset shellcmd') 'ShellWriteCmd 'Input endif endif -- Processed fExecNormalCmd = 0 enddo rc = SavedRc if fExecNormalCmd then ---- Normal NewLine (specified as arg) ---- NormalCmd endif ; --------------------------------------------------------------------------- defc ShellDeleteCmd NormalCmd = strip( arg( 1)) fExecNormalCmd = 1 do once = 1 to 1 if not IsAShell() then leave endif call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) if not CurPrompt then leave endif -- Process only if cursor is at the end or after if .col < length( CurPrompt''CurCmd) + 1 then leave endif -- Delete CurCmd .col = length( CurPrompt) + 1 eraseendline fExecNormalCmd = 0 enddo if fExecNormalCmd then NormalCmd endif ; --------------------------------------------------------------------------- ; Executed by Save aand SaveNextLastRing. defc ShellSaveEnv ShellFile = StripQuotesFileSpec( strip( arg( 1))) if ShellFile = '' then ShellFile = .filename endif if substr( ShellFile, 2, 2) <> ':\' & substr( ShellFile, 1, 2) <> '\\' then ShellDir = ShellGetCurDir() ShellFile = GetFullName( ShellFile, ShellDir) endif EnvFile = ShellFile'.env' CompareFile = GetTmpDir()'\nepmd\compare.env' -- Create CompareFile --dprintf( 'ShellSaveEnv: quietshell set >'CompareFile) quietshell 'set >'EnquoteFileSpec( CompareFile) -- Create EnvFile Cmd = 'epmshell saveenv 'EnquoteFileSpec( EnvFile) EnquoteFileSpec( CompareFile) --dprintf( 'Cmd = 'Cmd) --dprintf( 'ShellSaveEnv: ShellWriteCmdHidden 'Cmd) 'ShellWriteCmdHidden 'Cmd -- Deleting CompareFile here or in epmshell.cmd would come too early. -- Therefore CompareFile is created in %TMP%. ; --------------------------------------------------------------------------- ; Executed by ShellRestore. defc ShellLoadEnv do once = 1 to 1 EnvFile = StripQuotesFileSpec( strip( arg( 1))) savedrc = rc getfileid startfid if not Exist( EnvFile) then leave endif -- Load EnvFile 'xcom e /d 'EnquoteFileSpec( EnvFile) if rc = -282 then -- -282 = sayerror( "New file") .modify = 0 'xcom quit' leave endif -- Find COMSPEC in EnvFile and call it ComSpec = '' do l = 1 to .last -- Read line and skip comments LineStr = textline( l) if leftstr( LineStr, 1) = ':' then iterate elseif strip( LineStr) = '' then iterate elseif not pos( '=', LineStr) then iterate endif parse value LineStr with EnvVar'='EnvValue if upcase( EnvVar) <> 'COMSPEC' then iterate else -- Found ComSpec = EnvValue leave endif enddo -- Close EnvFile .modify = 0 'xcom quit' -- Restore file and rc -- Quit may switch to another file after execution, so always restore -- startfid. 'ActivateFile 'startfid rc = savedrc -- Execute ComSpec if found. It was added to the .env file if different -- from compare.env. Cmd = '' if ComSpec then Cmd = ComSpec 'ShellWriteCmdHidden 'Cmd -- 4os2 needs echo on ComSpecBasename = upcase( GetFileSpec( 'B', ComSpec)) if ComSpecBasename = '4OS2' then 'ShellWriteCmdHidden echo on' endif endif -- Load env from EnvFile Cmd = 'epmshell loadenv 'EnquoteFileSpec( EnvFile) --dprintf( 'ShellLoadEnv: ShellWriteCmdHidden 'Cmd) 'ShellWriteCmdHidden 'Cmd -- 'ShellWriteCmdHidden' won't change the prompt line. To achieve that, -- uncomment the following: /* 'ShellWriteCmd cd' 'DiscardChanges' */ enddo ; =========================================================================== ; Enter chars ; =========================================================================== defc ShellInputChars parse arg ShellNum . if ShellNum = '' & IsAShell() then ShellNum = GetFileAVar( 'shellnum') endif if ShellNum = '' then 'SayError 'NOT_IN_SHELL__MSG return endif -- Open EntryBox BoxTitle = 'Write characters to shell' EntryText = '' Text = 'Enter characters to be written to shell' ShellNum Text = Text''copies( ' ', Max( 0, 100 - length( Text))) EntryBoxButtons = '/'OK__MSG'/'Cancel__MSG'/' DefaultButton = 1 HelpId = 0 parse value EntryBox( BoxTitle, EntryBoxButtons, EntryText, 0, 254, -- cols, maxchars atoi( DefaultButton) || atoi( HelpId) || GethWndC( APP_HANDLE) || Text) with EbButton 2 Input \0 if EbButton = \1 then -- OK 'ShellWrite' ShellNum Input endif ; =========================================================================== ; History ; =========================================================================== ; --------------------------------------------------------------------------- ; Opens a list box with last commands. The selected one can be submitted and ; optionally be edited in an entry box before. This was previously a part of ; ShellWrite, except that the order of both boxes was changed. It starts ; with the list box, not with the entry box. Main idea by Joerg Tiemann. defc ShellHistory, Shell_History do once = 1 to 1 parse arg ShellNum . if ShellNum = '' & IsAShell() then ShellNum = GetFileAVar( 'shellnum') endif if ShellNum = '' then 'SayError 'NOT_IN_SHELL__MSG return endif NewCmd = '' ShellHandle = GetAVar( 'shell_h'ShellNum) if ShellHandle = '' then leave endif BoxTitle = 'Write command to shell' -- Get NewCmd do forever -- Open ListBox getfileid ShellFid call pSave_Pos( SavedPos) 'xcom e /c cmdslist' if rc <> -282 then -- -282 = sayerror( "New file") 'SayError 'ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext( rc) return endif .autosave = 0 getfileid lb_fid activatefile ShellFid 0 do forever PrevPos = .line .col call ShellGoToNextPrompt() if rc then leave elseif .line .col = PrevPos then leave endif call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) if CurCmd = '' then iterate endif insertline strip( CurCmd, 'L'), lb_fid.last + 1, lb_fid enddo call pRestore_Pos( SavedPos) activatefile lb_fid if not .modify then -- Nothing added? -- ListBox needs at least one empty entry insertline '', lb_fid.last + 1, lb_fid -- empty line endif if ListBox_Buffer_From_File( ShellFid, bufhndl, noflines, usedsize) then return endif Text = '' ListBoxButtons = '/'OK__MSG'/'EDIT__MSG'.../'Cancel__MSG DefaultItem = noflines - 1 -- Last item, first line of lb_fid is empty DefaultButton = 1 HelpId = 0 parse value ListBox( BoxTitle, \0 || atol( usedsize) || atoi( 32) || atoi( bufhndl), ListBoxButtons, 0, 0, -- top, left 12, 40, -- height, width, GethWndC( APP_HANDLE) || atoi( DefaultItem) || atoi( DefaultButton) || atoi( HelpId) || Text\0) with LbButton 2 NewCmd \0 call buffer( FREEBUF, bufhndl) if LbButton = \1 then -- OK leave elseif LbButton = \2 then -- Edit... 'ShellEnterCmd 'ShellNum NewCmd return else -- Cancel return endif enddo -- forever -- Get NewCmd -- Write NewCmd 'ShellWriteCmd 'NewCmd enddo ; --------------------------------------------------------------------------- defc ShellEnterCmd -- Open EntryBox -- Use same BoxTitle as ListBox -- Use selected entry from ListBox BoxTitle = 'Write command to shell' parse arg ShellNum NewCmd EntryText = NewCmd Text = 'Enter command to be written to shell' ShellNum Text = Text''copies( ' ', Max( 0, 100 - length( Text))) EntryBoxButtons = '/'OK__MSG'/'LIST__MSG'/'Cancel__MSG'/' DefaultButton = 1 HelpId = 0 parse value EntryBox( BoxTitle, EntryBoxButtons, EntryText, 0, 254, -- cols, maxchars atoi( DefaultButton) || atoi( HelpId) || GethWndC( APP_HANDLE) || Text) with EbButton 2 NewCmd \0 if EbButton = \1 then -- OK 'ShellWriteCmd 'NewCmd elseif EbButton = \2 then -- List... 'ShellHistory' endif ; =========================================================================== ; Filename completion ; =========================================================================== ; --------------------------------------------------------------------------- defc ShellFnc universal prevkey NormalCmd = strip( arg( 1)) fExecNormalCmd = 1 parse value prevkey with PrevKeyName \1 . KeyPath = '\NEPMD\User\Shell\FilenameCompletion' on = (QueryConfigKey( KeyPath) <> 0) if on then if wordpos( PrevKeyName, 'tab s_backtab') = 0 then 'ShellFncInit' endif 'ShellFncProcess' fExecNormalCmd = 0 endif if fExecNormalCmd then NormalCmd endif ; --------------------------------------------------------------------------- defc ShellFncBack universal prevkey NormalCmd = strip( arg( 1)) fExecNormalCmd = 1 parse value prevkey with PrevKeyName \1 . KeyPath = '\NEPMD\User\Shell\FilenameCompletion' on = (QueryConfigKey( KeyPath) <> 0) if on then if wordpos( PrevKeyName, 'tab s_backtab') = 0 then 'ShellFncInit' endif 'ShellFncProcess -' fExecNormalCmd = 0 endif if fExecNormalCmd then NormalCmd endif ; --------------------------------------------------------------------------- ; Filename completion like in 4os2. Rewrite of Joerg Tiemann's filename ; completion of SHELLKRAM.E. ; Difference in sorting order compared with 4os2: dirs come first and ; executables are sorted according to their appearance in FNC_EXE_MASK_LIST. const compile if not defined( FNC_EXE_MASK_LIST) FNC_EXE_MASK_LIST = '*.cmd *.exe *.com *.bat' compile endif compile if not defined( FNC_DIR_ONLY_CMD_LIST) FNC_DIR_ONLY_CMD_LIST = 'CD' compile endif compile if not defined( FNC_FILE_ONLY_CMD_LIST) FNC_FILE_ONLY_CMD_LIST = '' compile endif defc ShellFncInit if IsAShell() then ShellNum = GetFileAVar( 'shellnum') else return endif call ShellParseLinePromptCmd( .line, CurPrompt, CurCmd) Input = CurCmd ShellDir = ShellGetPrevDir() -- Todo: -- o Find expression starting with ':\' or '\\' (FilePart may be part of a parameter, -- e.g.: Input = -dd:\os2\apps or Input = -d:d:\os2\apps) -- o Make options with filenames, not followed by a space, work, -- e.g.: Input = app.exe -d* -> CmdPart = 'app.exe -d', FilePart = '*' -- Parse Input into CmdPart and FilePart CmdPart = '' CmdWord = '' FilePart = '' if rightstr( Input, 1) == ' ' then -- No FilePart if words( Input) > 0 then CmdWord = word( Input, 1) CmdPart = Input endif elseif rightstr( Input, 1) = '"' then -- FilePart is last word in "..." next = leftstr( Input, length( Input) - 1) -- strip last " lp = lastpos( '"', next) --dprintf( 'TabComplete', 'Input = ['Input'], lp = 'lp) FilePart = substr( Input, lp + 1, length( Input) - lp - 1) if lp > 1 then CmdPart = leftstr( Input, lp - 1) if pos( ' ', CmdPart) then CmdWord = word( Input, 1) endif endif else -- FilePart is last word if words( Input) = 1 then -- No CmdWord FilePart = Input elseif words( Input) > 1 then CmdWord = word( Input, 1) FilePart = lastword( Input) lp = wordindex( Input, words( Input)) CmdPart = leftstr( Input, lp - 1) endif endif --dprintf( 'FNC: CmdWord = ['CmdWord'], CmdPart = ['CmdPart'], FilePart = ['FilePart']') -- Construct fully qualified dirname to avoid change of directories, that -- doesn't work for UNC names. do once = 1 to 1 FileMask = FilePart PrepMask = '' if substr( FilePart, 2, 2) = ':\' then leave elseif leftstr( FilePart, 2) = '\\' then leave else if leftstr( FilePart, 1) = '\' then -- Prepend drive if substr( ShellDir, 2, 2) = ':\' then PrepMask = leftstr( ShellDir, 2) FileMask = PrepMask''FilePart -- -- Prepend host -- elseif leftstr( ShellDir, 2) = '\\' then -- not possible -- parse value ShellDir with '\\'Server'\'Resource -- if pos( '\', Resource) then -- parse value Resource with Resource'\'rest -- endif -- PrepMask = '\\'Server'\'Resource -- FileMask = PrepMask''FilePart endif else -- Prepend ShellDir PrepMask = strip( ShellDir, 't', '\')'\' FileMask = PrepMask''FilePart endif endif enddo -- Resolve FileMask to valid names for DosFind* FileMask = NepmdQueryFullName( FileMask) -- The here fully qualified filemask must be changed to a relative path later, -- if FilePart was relative before. -- Append * to FileMask only, if no * or ? is present in last dir segment. -- Determine if ExeMasks should be appended to FileMask. fAppendExeMask = 0 fAppendAllMask = 0 UnAppendedFileMask = FileMask lp = lastpos( '\', FileMask) LastSegment = substr( FileMask, lp + 1) --dprintf( 'TabComplete', 'LastSegment = ['LastSegment']') if verify( LastSegment, '?*', 'M') then --dprintf( 'TabComplete', '1 (wildcards): FileMask = ['FileMask']') elseif CmdWord = '' then --dprintf( 'TabComplete', '2 (no CmdWord): AppendExeMask') fAppendExeMask = 1 else fAppendAllMask = 1 endif if fAppendExeMask | fAppendAllMask then FileMask = FileMask'*' --dprintf( 'TabComplete', '3 (no wildcard): FileMask = ['FileMask']') endif -- Delete old array cTotal = GetAVar( 'fncfound.0') if cTotal <> '' then do i = 1 to cTotal call SetAVar( 'fncfound.'i, '') enddo endif call SetAVar( 'fncfound.0', '') call SetAVar( 'fncfound.last', '') c = 0 -- number of found names -- Should dirs be found? fFindDirs = (wordpos( upcase( CmdWord), FNC_FILE_ONLY_CMD_LIST) = 0) -- Should files be found? fFindFiles = (wordpos( upcase( CmdWord), FNC_DIR_ONLY_CMD_LIST) = 0) --dprintf( 'FNC: fAppendExeMask = 'fAppendExeMask', fAppendAllMask = 'fAppendAllMask', fFindDirs = 'fFindDirs', fFindFiles = 'fFindFiles) -- Find dirs Handle = '' Name = '' --dprintf( 'FNC: find dirs: FileMask = 'FileMask) do while fFindDirs & NepmdGetNextDir( FileMask, Handle, Name) -- Append \ for dirs Name = Name'\' -- Remove maybe previously added PrepMask if FilePart was relative l = length( PrepMask) if l > 0 then if leftstr( upcase( Name), l) == upcase( PrepMask) then Name = substr( Name, l + 1) endif endif -- Add it c = c + 1 call SetAVar( 'fncfound.'c, Name) enddo -- Find files if fAppendExeMask then mMax = words( FNC_EXE_MASK_LIST) else mMax = 1 endif do m = 1 to mMax -- Append next ExeMask to FileMask if fAppendExeMask then NextExeMask = word( FNC_EXE_MASK_LIST, m) -- Reset FileMask FileMask = UnAppendedFileMask FileMask = FileMask''NextExeMask endif Handle = '' Name = '' --dprintf( 'FNC: find files: FileMask = 'FileMask) do while fFindFiles & NepmdGetNextFile( FileMask, Handle, Name) -- Remove maybe previously added PrepMask if FilePart was relative l = length( PrepMask) if l > 0 then if leftstr( upcase( Name), l) == upcase( PrepMask) then Name = substr( Name, l + 1) endif endif -- Add it c = c + 1 call SetAVar( 'fncfound.'c, Name) enddo enddo if c > 0 then call SetAVar( 'fncfound.0', c) -- number of found names call SetAVar( 'fncfound.last', '0') -- use 0 as initial number 'SayError 'c 'dirs/files found.' else 'SayError No match for "'FilePart'".' endif call SetAVar( 'fncshellnum', ShellNum) call SetAVar( 'fncprompt', CurPrompt) call SetAVar( 'fnccmdpart', CmdPart) ; --------------------------------------------------------------------------- ; Tab must not be defined as accelerator key, because otherwise ; lastkey( 2) and lastkey( 3) would return wrong values for Tab. ; lastkey() = lastkey( 0) and lastkey( 1) for Tab doesn't work in EPM! ; When Sh is pressed, lastkey() is set to Sh. While Sh is down and ; Tab is pressed additionally, lastkey is set to Sh+Tab and lastkey( 2) ; is set to Sh. Therefore querying lastkey( 2) to determine if Tab was ; pressed before doesn't work for any key combination! ;defc TabComplete defc ShellFncProcess fForeward = ( arg( 1) <> '-') -- Check shell ShellNum = '' if IsAShell() then ShellNum = GetFileAVar( 'shellnum') endif next = GetAVar( 'fncshellnum') if ShellNum = '' | ShellNum <> next then return endif -- Query array Prompt = GetAVar( 'fncprompt') CmdPart = GetAVar( 'fnccmdpart') Name = '' cLast = GetAVar( 'fncfound.last') if cLast <> '' then cTotal = GetAVar( 'fncfound.0') --'SayError 'cTotal 'files in array.' if fForeward then if cLast < cTotal then cLast = cLast + 1 else cLast = 1 endif else if cLast > 1 then cLast = cLast - 1 else cLast = cTotal endif endif Name = GetAVar( 'fncfound.'cLast) call SetAVar( 'fncfound.last', cLast) -- save last used name number endif if Name <> '' then if pos( ' ', Name) then Name = '"'Name'"' endif ; Todo: ; Make -dName possible if CmdPart <> '' then NewLine = Prompt''strip( CmdPart) Name else NewLine = Prompt''Name endif replaceline NewLine .col = length( NewLine) + 1 -- go to end endif ; =========================================================================== ; Alias ; =========================================================================== ; --------------------------------------------------------------------------- const compile if not defined( ALIAS_SEP_CHARS) ALIAS_SEP_CHARS = ' |<>&' compile endif ; Resolves alias values for shell commands. Returns '' if no alias def found. defproc ShellAliasResolve String = arg( 1) ResolvedString = '' if not IsNum( GetKeysAVar( 'alias.key.'0)) then call ShellAliasFileRead() endif if not IsNum( GetKeysAVar( 'alias.key.'0)) then return String elseif GetKeysAVar( 'alias.key.'0) = 0 then return String endif amax = GetKeysAVar( 'alias.key.0') if amax = '' then rc = ShellAliasFileRead() if rc <> 0 then return String endif endif Rest = String UpString = upcase( String) KeyPath = '\NEPMD\User\Shell\Alias\EscapeChar' AliasEscapeChar = GetAVar( KeyPath) do while Rest <> '' -- Find string at p in alias keys -- Start at the end of the array to match the longest string UpRest = upcase( Rest) Val = '' do a = amax to 1 by -1 --dprintf( 'Alias: a = 'a', Key = 'GetKeysAVar( 'alias.key.'a)) Key = GetKeysAVar( 'alias.key.'a) UpKey = upcase( Key) if abbrev( UpRest, UpKey) = 1 then -- Get surrounding chars to check for separators PrevChar = rightstr( ResolvedString, 1) NextChar = substr( Rest, length( Key) + 1, 1) -- Handle EscapeChar if PrevChar == AliasEscapeChar then -- Remove it ResolvedString = leftstr( ResolvedString, length( ResolvedString) - 1) -- Keep found key Val = Key leave elseif pos( PrevChar, ALIAS_SEP_CHARS) > 0 & pos( NextChar, ALIAS_SEP_CHARS) > 0 then fExclusive = GetKeysAVar( 'alias.ex.'a) if fExclusive then if UpString <> UpKey then -- Keep string return String endif endif -- Replace key with value Val = GetKeysAVar( 'alias.value.'a) leave else iterate endif endif enddo if Val <> '' then -- Found, advance search pos by length of key pDelta = length( Key) else -- Not found, advance search pos by 1 pDelta = 1 Val = leftstr( Rest, 1) endif ResolvedString = ResolvedString''Val Rest = substr( Rest, pDelta + 1) enddo --dprintf( 'ResolvedString = 'ResolvedString) rc = 0 return ResolvedString ; --------------------------------------------------------------------------- defproc ShellAliasFileRead ValidApplications = 'SHELL' IniFile = arg( 1) if IniFile = '' then IniFile = Get_Env( 'NEPMD_USERDIR')'\bin\alias.cfg' endif if not Exist( IniFile) then IniFile = Get_Env( 'NEPMD_ROOTDIR')'\netlabs\bin\alias.cfg' endif if not Exist( IniFile) then rc = 2 return endif getfileid CurFid 'DisableLoad' 'DisableSelect' -- Load ini 'xcom e /d' IniFile a = 0 Application = '' if rc = 0 then getfileid IniFid .visible = 0 .autosave = 0 -- Delete array preva = GetKeysAVar( 'alias.key.'0) if IsNum( preva) then do x = 1 to preva call SetKeysAVar( 'alias.key.'x, '') call SetKeysAVar( 'alias.value.'x, '') enddo call SetKeysAVar( 'alias.key.'0, 0) endif -- Load temp file for sorting 'xcom e /c /q tempfile' if rc <> -282 then -- sayerror( 'New file') 'SayError 'ERROR__MSG rc BAD_TMP_FILE__MSG sayerrortext( rc) return endif getfileid TempFid .visible = 0 .autosave = 0 browse_mode = browse() -- query current state if browse_mode then call browse( 0) endif -- Read ini activatefile IniFid do l = 1 to .last IniLine = textline( l) StrippedIniLine = strip( IniLine) -- Ignore comments, lines starting with ';' at column 1 are comments if substr( IniLine, 1, 1) = ';' then iterate -- Ignore empty lines elseif StrippedIniLine = '' then iterate endif -- '[' at column 1 followed by a ']' on the same line marks the start -- of an application col1 = pos( '[', IniLine, 1) col2 = pos( ']', IniLine, 2) if col1 = 1 & col2 > 1 then Application = substr( IniLine, col1 + 1, col2 - col1 - 1) iterate endif -- The first '=' in the line marks keyword and expression. -- Spaces around '=' are allowed parse value StrippedIniLine with KeyWord '=' KeyValue -- KeyWord (without '=') is allowed KeyWord = strip( KeyWord) KeyValue = strip( KeyValue) if upcase( leftstr( KeyValue, 2)) == 'X,' then KeyValue = strip( substr( KeyValue, 3)) KeyEx = 1 else KeyEx = 0 endif if wordpos( Application, ValidApplications) = 0 then iterate else -- Append to temp file -- Use \1 as seperator because of its low ASCII value, -- '=' wouldn't be a good choice for sorting. insertline KeyWord''\1''KeyEx''\1''KeyValue, TempFid.last + 1, TempFid --dprintf( 'Read alias file: Key = 'KeyWord', Value = 'KeyValue) endif enddo -- Quit ini activatefile IniFid .modify = 0 'xcom quit' -- Sort temp file to allow for finding the longest matched key activatefile TempFid if .last > 2 then call Sort( 2, .last, 1, 40, TempFid, 'I') endif -- Add lines to array do l = 2 to .last parse value textline( l) with KeyWord''\1''KeyEx''\1''KeyValue a = a + 1 call SetKeysAVar( 'alias.key.'a, KeyWord) call SetKeysAVar( 'alias.value.'a, KeyValue) call SetKeysAVar( 'alias.ex.'a, KeyEx) --dprintf( 'Sort alias keys: a = 'a', Key = 'KeyWord', Value = 'KeyValue) enddo call SetKeysAVar( 'alias.key.'0, a) -- Quit temp file activatefile TempFid .modify = 0 'xcom quit' else if rc = -282 then -- sayerror( 'New file') 'xcom quit' endif 'SayError Error reading ini file 'inifile rc = 30 endif 'EnableLoad' 'EnableSelect' activatefile CurFid return ; --------------------------------------------------------------------------- defc ShellAliasFileRead call ShellAliasFileRead()