Get browser information (path and version)

I created a new code for path and version detection for browsers. It works on Windows, Linux and Mac, but on Mac requires a little bit more work to be complete.

I am using this code in my new version of FireTcl: https://sourceforge.net/projects/firetcl/

package provide browser_info 1.0

namespace eval ::browser_info {

    array set WINDOWS_LOCALE {
        0436 af_ZA 
        041c sq_AL 
        0484 gsw_FR
        045e am_ET 
        0401 ar_SA 
        0801 ar_IQ 
        0c01 ar_EG 
        1001 ar_LY 
        1401 ar_DZ 
        1801 ar_MA 
        1c01 ar_TN 
        2001 ar_OM 
        2401 ar_YE 
        2801 ar_SY 
        2c01 ar_JO 
        3001 ar_LB 
        3401 ar_KW 
        3801 ar_AE 
        3c01 ar_BH 
        4001 ar_QA 
        042b hy_AM 
        044d as_IN 
        042c az_AZ 
        082c az_AZ 
        046d ba_RU 
        042d eu_ES 
        0423 be_BY 
        0445 bn_IN 
        201a bs_BA 
        141a bs_BA 
        047e br_FR 
        0402 bg_BG 
        0403 ca_ES 
        0004 zh_CHS
        0404 zh_TW 
        0804 zh_CN 
        0c04 zh_HK 
        1004 zh_SG 
        1404 zh_MO 
        7c04 zh_CHT
        0483 co_FR 
        041a hr_HR 
        101a hr_BA 
        0405 cs_CZ 
        0406 da_DK 
        048c gbz_AF
        0465 div_MV
        0413 nl_NL 
        0813 nl_BE 
        0409 en_US 
        0809 en_GB 
        0c09 en_AU 
        1009 en_CA 
        1409 en_NZ 
        1809 en_IE 
        1c09 en_ZA 
        2009 en_JA 
        2409 en_CB 
        2809 en_BZ 
        2c09 en_TT 
        3009 en_ZW 
        3409 en_PH 
        4009 en_IN 
        4409 en_MY 
        4809 en_IN 
        0425 et_EE 
        0438 fo_FO 
        0464 fil_PH
        040b fi_FI 
        040c fr_FR 
        080c fr_BE 
        0c0c fr_CA 
        100c fr_CH 
        140c fr_LU 
        180c fr_MC 
        0462 fy_NL 
        0456 gl_ES 
        0437 ka_GE 
        0407 de_DE 
        0807 de_CH 
        0c07 de_AT 
        1007 de_LU 
        1407 de_LI 
        0408 el_GR 
        046f kl_GL 
        0447 gu_IN 
        0468 ha_NG 
        040d he_IL 
        0439 hi_IN 
        040e hu_HU 
        040f is_IS 
        0421 id_ID 
        045d iu_CA 
        085d iu_CA 
        083c ga_IE 
        0410 it_IT 
        0810 it_CH 
        0411 ja_JP 
        044b kn_IN 
        043f kk_KZ 
        0453 kh_KH 
        0486 qut_GT
        0487 rw_RW 
        0457 kok_IN
        0412 ko_KR 
        0440 ky_KG 
        0454 lo_LA 
        0426 lv_LV 
        0427 lt_LT 
        082e dsb_DE
        046e lb_LU 
        042f mk_MK 
        043e ms_MY 
        083e ms_BN 
        044c ml_IN 
        043a mt_MT 
        0481 mi_NZ 
        047a arn_CL
        044e mr_IN 
        047c moh_CA
        0450 mn_MN 
        0850 mn_CN 
        0461 ne_NP 
        0414 nb_NO 
        0814 nn_NO 
        0482 oc_FR 
        0448 or_IN 
        0463 ps_AF 
        0429 fa_IR 
        0415 pl_PL 
        0416 pt_BR 
        0816 pt_PT 
        0446 pa_IN 
        046b quz_BO
        086b quz_EC
        0c6b quz_PE
        0418 ro_RO 
        0417 rm_CH 
        0419 ru_RU 
        243b smn_FI
        103b smj_NO
        143b smj_SE
        043b se_NO 
        083b se_SE 
        0c3b se_FI 
        203b sms_FI
        183b sma_NO
        1c3b sma_SE
        044f sa_IN 
        0c1a sr_SP 
        1c1a sr_BA 
        081a sr_SP 
        181a sr_BA 
        045b si_LK 
        046c ns_ZA 
        0432 tn_ZA 
        041b sk_SK 
        0424 sl_SI 
        040a es_ES 
        080a es_MX 
        0c0a es_ES 
        100a es_GT 
        140a es_CR 
        180a es_PA 
        1c0a es_DO 
        200a es_VE 
        240a es_CO 
        280a es_PE 
        2c0a es_AR 
        300a es_EC 
        340a es_CL 
        380a es_UR 
        3c0a es_PY 
        400a es_BO 
        440a es_SV 
        480a es_HN 
        4c0a es_NI 
        500a es_PR 
        540a es_US 
        0441 sw_KE 
        041d sv_SE 
        081d sv_FI 
        045a syr_SY
        0428 tg_TJ 
        085f tmz_DZ
        0449 ta_IN 
        0444 tt_RU 
        044a te_IN 
        041e th_TH 
        0851 bo_BT 
        0451 bo_CN 
        041f tr_TR 
        0442 tk_TM 
        0480 ug_CN 
        0422 uk_UA 
        042e wen_DE
        0420 ur_PK 
        0820 ur_IN 
        0443 uz_UZ 
        0843 uz_UZ 
        042a vi_VN 
        0452 cy_GB 
        0488 wo_SN 
        0434 xh_ZA 
        0485 sah_RU
        0478 ii_CN 
        046a yo_NG 
        0435 zu_ZA 
    }
    
    
    variable LANGUAGE_NAME   
    array set LANGUAGE_NAME {
        en English
        es Spanish
        de German
        fr French
        it Italian
        pt Portuguese
        sv Swedish
        da Danish
        nb Norwegian
        nn Norwegian
        ro Romanian
    }
                
    array set PROGRAM_FILES {
        English {{C:\Program Files} {C:\Program Files (x86)}}
        German {{C:\Programme} {C:\Programme (x86)}}
        Spanish {{C:\Archivos de programa} {C:\Archivos de programa (x86)}}
        French {{C:\Programmes} {C:\Programmes (x86)}}
        Italian {{C:\Programmi} {C:\Programmi (x86)}}
        Portuguese {{C:\Arquivos de Programas} {C:\Arquivos de Programas (x86)}}
        Swedish {{C:\Program} {C:\Program (x86)}}
        Danish {{C:\Programmer} {C:\Programmer (x86)}}
        Norwegian {{C:\Programfiler} {C:\Programfiler (x86)}}
        Romanian {{C:\Fisiere Program} {C:\Fisiere Program (x86)}}
    }
    
    variable DEFAULT_BROWSER_PATH_ON_MAC
    
    array set DEFAULT_BROWSER_PATH_ON_MAC {
        firefox {/Applications/Firefox.app/Contents/MacOS/firefox-bin}
        chrome {/Applications/Google Chrome.app/Contents/MacOS/Google Chrome}
        safari {/Applications/Safari.app/Contents/MacOS/Safari}
        opera {/Applications/Opera.app/Contents/MacOS/Opera}   
    }
    
    variable BINARY_DIRECTORY_ON_WINDOWS
    
    array set BINARY_DIRECTORY_ON_WINDOWS {
        ie {Internet Explorer}
        firefox {Mozilla Firefox}
        chrome {Google\Chrome\Application}        
        opera {Opera}
    }
    
    variable BINARY_NAME
    
    array set BINARY_NAME {
        windows {
            ie iexplore.exe
            firefox firefox.exe
            chrome chrome.exe
            opera opera.exe
        }
        linux {
            firefox firefox 
            chrome google-chrome 
            opera opera
        }
    }
    
    # https://helgeklein.com/blog/2010/08/how-the-app-paths-registry-key-makes-windows-both-faster-and-safer/
    # https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121%28v=vs.85%29.aspx
    
    variable BROWSER_PATH_IN_WINDOWS_REGISTRY
    
    # The arrays for the windows registry has this format: 
    #   browser_name: 
    #       subpath_of_keys 
    #       pattern_of_last_key 
    #       key_value
    
    array set BROWSER_PATH_IN_WINDOWS_REGISTRY {
        ie {{HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\IEXPLORE.EXE} {} {}}
        firefox {
            {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe} {} {}
            {HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe} {} {}}
        chrome {
            {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe} {} {}
            {HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe} {} {}}
        opera {
            {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\Opera.exe} {} {}
            {HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Opera.exe} {} {}}
    }
    
    # Another posibles keys to find the path of firefox:
    #  HKEY_LOCAL_MACHINE\SOFTWARE\Classes\FirefoxHTML\shell\open\command
    #  HKEY_CURRENT_USER\SOFTWARE\Classes\FirefoxHTML\shell\open\command
    #  HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Applications\firefox.exe\shell\open\command
    #  HKEY_CURRENT_USER\SOFTWARE\Classes\Applications\firefox.exe\shell\open\command
    
    # http://stackoverflow.com/questions/4212002/how-to-find-out-from-the-windows-registry-where-ie-is-installed
    # I can find out where Firefox is installed as follows; query this key to get the current version:
    #   HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\Mozilla Firefox
    #      CurrentVersion
    # ... then query this key to get the installed path (where "nnn" is the version number obtained above):
    #   HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\Mozilla Firefox\nnn\Main
    #      Install Directory

    
    variable BROWSER_VERSION_IN_WINDOWS_REGISTRY
    
    # http://automationoverflow.blogspot.com.es/2013/08/find-installed-browser-version-from.html
    
    array set BROWSER_VERSION_IN_WINDOWS_REGISTRY {
        ie {
            {HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer} {} svcVersion
            {HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer} {} Version
        }
        opera {
            {HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall} {[Oo]pera *} DisplayVersion
            {HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall} {[Oo]pera *} DisplayVersion
        }
        chrome {
            {HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome} {} DisplayVersion
            {HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome} {} DisplayVersion
            HKEY_CLASSES_ROOT\\Wow6432Node\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}\\LocalServer32 {} {}
        }
        firefox {                    
            {HKEY_CURRENT_USER\Software\Mozilla\Mozilla Firefox} {} CurrentVersion
            {HKEY_LOCAL_MACHINE\Software\Mozilla\Mozilla Firefox} {} CurrentVersion
            {HKEY_LOCAL_MACHINE\Software\Wow6432Node\Mozilla\Mozilla Firefox} {} CurrentVersion
        }
    }
        
    variable COMMAND_TO_GET_BROWSER_VERSION
    array set COMMAND_TO_GET_BROWSER_VERSION {
        firefox {firefox -v}
        chrome {google-chrome --version}
        opera {opera -version}
    }
    
    
    namespace export firefox ie chrome opera
    
    
    oo::class create browser_info {
        variable browser_name WINDOWS_LOCALE LANGUAGE_NAME PROGRAM_FILES COMMAND_TO_GET_BROWSER_VERSION DEFAULT_BROWSER_PATH_ON_MAC BROWSER_PATH_IN_WINDOWS_REGISTRY BROWSER_VERSION_IN_WINDOWS_REGISTRY BINARY_NAME BINARY_DIRECTORY_ON_WINDOWS
        
        constructor {browserName} {
            set browser_name $browserName
            
            foreach varname [info class variables [self class]] {

                if [string is upper [string index $varname 0]] {
                    namespace upvar ::browser_info $varname [self namespace]::$varname
                }
            }
            
        }
                
        method default_program_files_on_windows {} {
   
            set locale_code [my get_windows_locale_ID]
    
            if {$locale_code eq ""} {
                set language_code [lindex [split $locale_code "_"] 0]
            } else {
                set language_code "en"
            }
            
            set language $LANGUAGE_NAME($language_code)
            return $PROGRAM_FILES($language)
        }
        
        
        method default_windows_location_of_browser {} {
            
            set directory_of_program_files [list]
        
            set default_program_files [my default_program_files_on_windows]
        
        
            if {[info exists ::env(PROGRAMFILES)]} {
                lappend directory_of_program_files $::env(PROGRAMFILES)
            } else {
                lappend directory_of_program_files [lindex $default_program_files 0]
            }
    
            if {[info exists ::env(PROGRAMFILES(X86)]} {
                lappend directory_of_program_files $::env(PROGRAMFILES(X86)
            } else {
                lappend directory_of_program_files [lindex $default_program_files 1]
            }
    
            set browser_subpath [file join $BINARY_DIRECTORY_ON_WINDOWS($browser_name) [dict get $BINARY_NAME(windows) $browser_name]]
            foreach directory $directory_of_program_files {
                set binary_path [file join $directory $browser_subpath]
            
                if {[file isfile $binary_path] && [file executable $binary_path]} {
                    return $binary_path
                }
    
            }
            
            return {}
        }      
        
            
        method get_windows_locale_ID {} {
            package require registry
            
            set locale_subkey [string tolower [registry get {HKEY_LOCAL_MACHINE\system\controlset001\control\nls\language} Installlanguage]]
            
            if {[info exists WINDOWS_LOCALE($locale_subkey)]} {
                return $WINDOWS_LOCALE($locale_subkey)
            } else {
                return ""
            }
        }
        
        method which {executable} {
            foreach path [split $::env(PATH) $::tcl_platform(pathSeparator)] {
                set exe [file join $path $executable]
                if {[file isfile $exe] && [file executable $exe]} {
                    return $exe
                }
            }
    
            return {}
        }
        
        method first_value_found_in_windows_registry {data_of_paths} {
            package require registry
            
            set value_found ""
            foreach {subpath_of_keys pattern_of_last_key key_value} $data_of_paths {
                if {$pattern_of_last_key ne ""} {
                    set list_of_subkeys [registry keys $subpath_of_keys $pattern_of_last_key]
                    if {[llength $list_of_subkeys] != 0} {
                        set key_name "$subpath_of_keys\\[lindex $list_of_subkeys 0]"
                    } else {
                        continue
                    }
                } else {
                    set key_name $subpath_of_keys
                }
                
                try {
                    set value_found [registry get $key_name $key_value]
                    break
                } on error {} {
    
                }
            }
            
            return $value_found
        }
                
        method browser_path_in_windows_registry {} {
            set browser_command [my first_value_found_in_windows_registry $BROWSER_PATH_IN_WINDOWS_REGISTRY($browser_name)]
            return $browser_command
        }
        
        method browser_version_in_windows_registry {} {
            set browser_version [my get_version_from_text [my first_value_found_in_windows_registry $BROWSER_VERSION_IN_WINDOWS_REGISTRY($browser_name)]]
            return $browser_version
        }
                
        method get_path {} {
            # Returns the path to the browser binary.
            
            set path_to_browser {}
            if {$::tcl_platform(os) in [list MacOS Darwin]} {
                set path_to_browser $DEFAULT_BROWSER_PATH_ON_MAC($browser_name)
            } elseif {$::tcl_platform(platform) eq "windows"} {
                set path_to_browser [string trim [my browser_path_in_windows_registry] \"]
                if {$path_to_browser eq ""} {
                    set path_to_browser [my default_windows_location_of_browser]
                }
            } else {                
                set path_to_browser [my which [dict get $BINARY_NAME(linux) $browser_name]]
            }
    
            return $path_to_browser
        }
        
        method get_version {} {
            # Returns the version of the browser.
            
            set browser_version {}
            if {$::tcl_platform(os) in [list MacOS Darwin]} {
    
            } elseif {$::tcl_platform(platform) eq "windows"} {                
                return [my browser_version_in_windows_registry]
            } else {

                catch "exec $COMMAND_TO_GET_BROWSER_VERSION($browser_name)" output
    
                foreach line [split $output "\n"] {
                    if {[set browser_version [my get_version_from_text $line]] ne ""} break
                }
            }
    
            return $browser_version
        }
        
        method get_version_from_text {text} {
            if {[regexp {(\d+\.\d+(?:\.\d+)*)} $text -> browser_version]} {
                return $browser_version
            } 
            return ""
        }
    
    }
    
    oo::class create Firefox {       
        superclass browser_info
        
        constructor {} {
            next firefox
        }   
    }
    
    oo::class create IE {
        superclass browser_info 
        
        constructor {} {
            next ie
        }
    }
    
    oo::class create Chrome {
        superclass browser_info
        
        constructor {} {
            next chrome
        }
    }
    
    oo::class create Opera {
        superclass browser_info
        
        constructor {} {
            next opera
        }
    }

    oo::class create Safari {
        superclass browser_info
        
        constructor {} {
            next safari
        }
    }
    
    Firefox create firefox
    IE create ie
    Chrome create chrome
    Opera create opera

    if {$::tcl_platform(os) == "Darwin"} {
        Safari create safari
    }
}

Example of usage:

puts [::browser_info::firefox get_path]
puts [::browser_info::firefox get_version]

puts [::browser_info::chrome get_path]
puts [::browser_info::chrome get_version]

puts [::browser_info::opera get_path]
puts [::browser_info::opera get_version]

puts [::browser_info::ie get_path]
puts [::browser_info::ie get_version]

if {$::tcl_platform(os) == "Darwin"} {
  puts [::browser_info::safari get_path]
  puts [::browser_info::safari get_version]
}

JOB 16-10-12, support for OSX added and briefly tested: works!

An additional procedure to get the browser(info) currently installed on the OS.

proc ::browser_info::getbrowserinfo {} {
        #
        # get available browser name and path,
        # returns an empty string if not successful
        #
        if {$::tcl_platform(platform) == "windows"} {

                # note: safari is not supported any more under windows
                set browser_list {"firefox" "chrome" "opera" "ie" "safari"}

        } else {

                # unix / maybe OSX - this order might be successfull
                set browser_list {"safari" "firefox" "chrome" "opera"}
        }
        
        foreach browser $browser_list {

        switch -- $browser {
                "firefox" {
                        if {[set p [::browser_info::firefox get_path]] != ""} {
                                return [list $browser $p]
                        }
                }
                "chrome" {
                        if {[set p [::browser_info::chrome get_path]] != ""} {
                                return [list $browser $p]
                        }
                }
                "opera" {
                        if {[set p [::browser_info::opera get_path]] != ""} {
                                return [list $browser $p]
                        }
                }
                "ie" {
                        if {[set p [::browser_info::ie get_path]] != ""} {
                                return [list $browser $p]
                        }
                }
                "safari" {
                        if {[set p [::browser_info::safari get_path]] != ""} {
                                return [list $browser $p]
                        }
                }
        }}
        
        return {}
}


# get browser command:

if {[set browser_info [::browser_info::getbrowserinfo]] == ""} {
        tk_messageBox \
                -title "Attempt to find browser failed." \
                -icon "warning" \
                -type ok \
                -message "Cannot detect any web browser in your system!"
} else {

        puts "------------------------"
        puts "Your Current Browser is:"
        puts [lindex $browser_info 0]
        puts [lindex $browser_info 1]
        puts "------------------------"
}

Might be used like this:

proc OpenInBrowser {html_file} {

        if {[llength [set browser_info [::browser_info::getbrowserinfo]]] == 0 } {

                tk_messageBox \
                        -title "Attempt to find browser failed." \
                        -icon "warning" \
                        -type ok \
                        -message "Cannot detect any web browser in your system!"

                return
        }

        switch -- $::tcl_platform(platform) {
                "windows" {
                
                        # only the name is required to start the command
                        # path under windows is not relevant ...
                        set browser [lindex $browser_info 0]
                        
                        set command [list {*}[auto_execok start] {}]
                        exec {*}$command $browser $html_file &
                }
                "unix" {
                        # call up safari ...
                        set browser_path [lindex $browser_info 1]
                        exec $browser_path $html_file &
                }
                default {
                        tk_messageBox \
                        -title "Operating system currently not supported" \
                        -icon "info" \
                        -type ok \
                        -message "You are invited, to edit the code yourself."
                }
        }
}