File "Unix.inc"

Path: /linkmaker/inc/Unix.inc
File size: 9.44 KB
MIME-type:
Charset: utf-8

'---------------------------------------------------------------------------------------------------------------------
' This is +Unix.inc v1.5+ for PBWin9
' This library offers tool functions to run Unix commands from within Wine
'---------------------------------------------------------------------------------------------------------------------
MACRO UNIX_HOME = "z:\home\" + ENVIRON$("USERNAME") + "\"  ' Returns the Wine path of Unix '~' (User home)
DECLARE FUNCTION GetHostVer() AS STRING                    ' Return Host OS Core name+version e.g. Linux-6.14.0-37-gen
DECLARE FUNCTION GetWineVer() AS STRING                    ' Return Wine version e.g. 11.0
DECLARE FUNCTION IsOnUnix() AS LONG                        ' Return %True if we are on a Unix system, %False otherwise
DECLARE FUNCTION WinePath(BYVAL file AS STRING) AS STRING  ' Return the Wine path of a Unix file
DECLARE FUNCTION UnixPath(BYVAL file AS STRING) AS STRING  ' Return the Unix path of a Wine file
DECLARE FUNCTION UnixExists(BYVAL file AS STRING) AS LONG  ' Return existence of a Unix file e.g. /usr/bin/vlc
DECLARE FUNCTION GetUnixRet(BYVAL cmd AS STRING) AS STRING ' Return the full output of a Unix command
DECLARE SUB UnixShell(BYVAL cmd AS STRING)                 ' Execute a Unix command asynchronously
DECLARE SUB GetWineDrivesMapping()                         ' Get all Wine drives and their mapping
'---------------------------------------------------------------------------------------------------------------------
GLOBAL dosdevices() AS STRING ' list of Wine drives followed by their mapping, in the form "d:/media/user/myhdd"
GLOBAL wine_ver     AS STRING ' Wine version (needed since Wine 11.0 as it breaks 'start.exe' retro-compatibility)
'---------------------------------------------------------------------------------------------------------------------
#INCLUDE ONCE "Win32Api.inc"
'---------------------------------------------------------------------------------------------------------------------

'---------------------------------------------------------------------------------------------------------------------
FUNCTION GetHostVer() AS STRING
' Return Host OS Core name+version e.g. Linux-6.14.0-37-generic
    LOCAL hNt AS DWORD
    LOCAL pwh AS DWORD
    LOCAL sys AS ASCIZ POINTER
    LOCAL ver AS ASCIZ POINTER

    hNt = GetModuleHandle("ntdll.dll")
    IF hNt = 0 THEN EXIT FUNCTION
    pwh = GetProcAddress(hNt, "wine_get_host_version")
    IF pwh = 0 THEN EXIT FUNCTION

    CALL DWORD pwh CDECL(sys, ver)
    FUNCTION = @sys + "-" + @ver
END FUNCTION
'---------------------------------------------------------------------------------------------------------------------

'---------------------------------------------------------------------------------------------------------------------
DECLARE FUNCTION WineGetV() AS DWORD
'---------------------------------------------------------------------------------------------------------------------
FUNCTION GetWineVer() AS STRING
' Return Wine version e.g. 11.0
    LOCAL hNt AS DWORD
    LOCAL pwv AS DWORD
    LOCAL ver AS ASCIZ POINTER

    hNt = GetModuleHandle("ntdll.dll")
    IF hNt = 0 THEN EXIT FUNCTION
    pwv = GetProcAddress(hNt, "wine_get_version")
    IF pwv = 0 THEN EXIT FUNCTION

    CALL DWORD pwv USING WineGetV() TO ver
    FUNCTION = @ver
END FUNCTION
'---------------------------------------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
FUNCTION IsOnUnix() AS LONG
' Return %True if we are on a Unix system, %False otherwise
    LOCAL hNt AS DWORD
    LOCAL pwv AS DWORD
    hNt = GetModuleHandle("ntdll.dll")
    IF hNt = 0 THEN
        FUNCTION = %FALSE
    ELSE
        pwv = GetProcAddress(hNt, "wine_get_version")
        FUNCTION = (pwv <> 0)
    END IF
END FUNCTION
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
SUB GetWineDrivesMapping()
' Get all Wine drives and their mapping
    LOCAL r, e AS STRING
    LOCAL i, j AS LONG

    REDIM dosdevices(-1)

    r = GetUnixRet("ls -al ~/.wine/dosdevices")
    IF r = "" THEN EXIT SUB

    FOR i = 1 TO PARSECOUNT(r, $LF)
        e = PARSE$(r, $LF, i)
        j = INSTR(e, " -> ")
        IF j = 0 THEN ITERATE FOR
        j = INSTR(j - LEN(e) - 2, e, " ")
        e = MID$(e, j + 1)
        IF INSTR(e, "/dev/") > 0 OR _
           INSTR(e, "c:") > 0 OR _
           INSTR(e, "z:") > 0 THEN
           ITERATE FOR
        END IF
        e = REMOVE$(e, " -> ")
        REPLACE "::" WITH ":" IN e
        REDIM PRESERVE dosdevices(UBOUND(dosdevices) + 1)
        dosdevices(UBOUND(dosdevices)) = e
    NEXT

END SUB
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
FUNCTION WinePath(BYVAL file AS STRING) AS STRING
' Return the Wine path of a Unix file e.g. /usr/bin/vlc -> z:\usr\bin\vlc
    file = "z:" + file
    REPLACE "/" WITH "\" IN file
    FUNCTION = file
END FUNCTION
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
FUNCTION UnixPath(BYVAL file AS STRING) AS STRING
' Return the Unix path of a Wine file e.g. z:\usr\bin\vlc -> /usr/bin/vlc
    LOCAL i AS LONG

    ' Get the mapping for all Wine drives
    IF UBOUND(dosdevices) < 0 THEN GetWineDrivesMapping()
    REPLACE "\" WITH "/" IN file

    ' Special cases: files on Z:\ and C:\ are easy to translate
    IF LEFT$(LCASE$(file), 3) = "z:/" THEN
        FUNCTION = MID$(file, 3)
        EXIT FUNCTION
    END IF
    IF LEFT$(LCASE$(file), 3) = "c:/" THEN
        FUNCTION = "/home/" + ENVIRON$("USERNAME") + "/.wine/drive_c/" + MID$(file, 4)
        EXIT FUNCTION
    END IF

    ' Else use dosdevices() mapping
    FOR i = 0 TO UBOUND(dosdevices)
        IF LEFT$(LCASE$(file), 3) = LEFT$(dosdevices(i), 2) + "/" THEN
            FUNCTION = MID$(dosdevices(i), 3) + MID$(file, 3)
            EXIT FUNCTION
        END IF
    NEXT

    ' Final case, in case drive is not mapped (should not happen?)
    file = "/home/" + ENVIRON$("USERNAME") + "/.wine/dosdevices/" + file
    i = INSTR(file, ":") ' the following is needed to transform the drive letter to smaller case
    IF i > 1 THEN file = LEFT$(file, i-2) + LCASE$(MID$(file, i-1, 1)) + MID$(file, i)
    FUNCTION = file

END FUNCTION
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
FUNCTION UnixExists(BYVAL file AS STRING) AS LONG
' Return existence of a Unix file e.g. /usr/bin/vlc
    LOCAL lRes AS LONG
    lRes = GETATTR(WinePath(file))
    FUNCTION = (ERRCLEAR = 0)
END FUNCTION
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
SUB CreateExecSh(BYVAL cmd AS STRING)
' Create the 'Dynamic Unix Execution Script' (dues) to be called from Wine
    LOCAL dues  AS STRING
    LOCAL ff    AS LONG

    ' Check wine version
    IF wine_ver = "" THEN wine_ver = GetWineVer()

    ' Reset script
    dues = UNIX_HOME + "dues.sh"
    KILL dues

    ' Write script
    ff = FREEFILE
    OPEN dues FOR BINARY ACCESS WRITE AS #ff
    PUT$ #ff, "#!/bin/bash" + $LF + cmd + $LF
    CLOSE #ff

    ' Make script executable
    SHELL "start /" + IIF$(wine_ver>="11","unix","exec") + _
          " /bin/chmod +x /home/" + ENVIRON$("USERNAME") + "/dues.sh", 0

END SUB
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
FUNCTION GetUnixRet(BYVAL cmd AS STRING) AS STRING
' Return the full output of a Unix command
    LOCAL timout AS SINGLE
    LOCAL logfl AS STRING
    LOCAL ret AS STRING
    LOCAL ff AS LONG

    ' Define temporary files
    logfl = UNIX_HOME + "dues.log"
    KILL logfl

    ' Now, synchronously call the command
    UnixShell cmd + " > ~/dues.log"

    ' Poll the temporary file every 1/4s until filled
    ' or we reach a timeout of 3s (arbitrary)
    DO
        SLEEP 250 : timout += 0.25
        ff = FREEFILE
        OPEN logfl FOR BINARY ACCESS READ LOCK SHARED AS #ff
        GET$ #ff, LOF(#ff), ret
        CLOSE #ff
    LOOP UNTIL ret <> "" OR timout >= 3

    ' Clean-up temporary files
'    KILL LEFT$(logfl,-3) + "sh"
'    KILL logfl

    FUNCTION = ret
END FUNCTION
'-----------------------------------------------------------------------------------------

'-----------------------------------------------------------------------------------------
SUB UnixShell(BYVAL cmd AS STRING)
' Execute a Unix command asynchronously
    LOCAL pid AS DWORD

    ' Check wine version
    IF wine_ver = "" THEN wine_ver = GetWineVer()

    ' Call command
    CreateExecSh(cmd)
    pid = SHELL("start /" + IIF$(wine_ver>="11","unix","exec") + _
          " /bin/sh /home/" + ENVIRON$("USERNAME") + "/dues.sh", 0)
    SLEEP 250
END SUB
'-----------------------------------------------------------------------------------------