====== Utilities ====== A collection of scripts and the like which I have come to rely on. ===== IE2FF ===== #!/usr/bin/env python """ URL-click forwarder (Windows -> Linux). This script catches URL clicks (actually iexplore.exe invokations) and forwards them over TCP/IP to a Linux machine which launches them in the default browser (e.g. Firefox). This was written so that clicking a URL in, say, MS Outlook in a WinXP VM would open the page in the host Linux's browser. (Yes, VMware Fusion can do that via VMwareOpenHost.exe, but VMware Player doesn't seem to want to do that, so this was written.) WINDOWS-SIDE INSTALLATION ========================= 1. Change ``giHOST`` to match your server-side hostname 2. Call this script with --install LINUX-SIDE INSTALLATION ======================= 1. Run the script without arguments TODO: Use Python-Win32 modules to implement a Win32 DDE server to catch special invocations, such as "start http://example.org" TCP/IP PROTOCOL =============== The protocol uses endpoint readiness to signal when processing has been completed. This relies on the behaviour of ``recv()``, which blocks until data is available (returns a non-empty string) or the local READ endpoint has been closed by the remote end (returns an empty string). 1. Client: a. Connects to server b. Transmits URL data as ASCII-encoded text c. Closes WRITE end-point d. Starts spinning on recv() 2. Server: a. Waits for a connection b. Spins on recv() until it returns an empty string c. Closes WRITE end-point d. Spins on recv() e. Closes connection 3. Client: a. Finishes spinning on recv() b. Closes connection. """ __docformat__ = "restructuredtext en" giPORT = 43233 # That's IE2FF in T9 (predictive text, as on a phone) giHOST = "e102928-lin" # ----------------------------------------------------------------------------- def regedit(rRegistryFragment): import os import tempfile import subprocess as sp assert os.name == "nt", "You're not running Windows!" rRegistryFragment = os.linesep.join( rRegistryFragment.splitlines() ) rTF = tempfile.mktemp() try: with file(rTF, "wb") as sTF: sTF.write( rRegistryFragment.encode("utf-16") ) sTF.flush() # Seems we must close the file or regedit cannot read it. Go figure. sPH = sp.Popen([ "regedit", "/S", # Silent sTF.name]) iRetVal = sPH.wait() finally: if os.path.exists(rTF): os.unlink(rTF) return iRetVal == 0 # ----------------------------------------------------------------------------- def install(): import os import sys from textwrap import dedent # Note that backslashes have to be escaped, and double-escaped between a # drive letter and a path. rRegistryFragment = dedent(R""" Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\iexplore.exe] "Debugger"="%s %s" """).lstrip() % ( (os.path.abspath( sys.executable ) .replace(":\\", ":\\\\") .replace("\\", "\\\\")), (os.path.abspath( install.func_code.co_filename ) .replace(":\\", ":\\\\") .replace("\\", "\\\\")), ) if regedit(rRegistryFragment): print "Installation was successful" else: print "Installation failed, `regedit.exe` returned %d" % iRetVal # ----------------------------------------------------------------------------- def uninstall(): from textwrap import dedent # Note that backslashes have to be escaped, and double-escaped between a # drive letter and a path. rRegistryFragment = dedent(R""" Windows Registry Editor Version 5.00 [-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\iexplore.exe] """).lstrip() if regedit(rRegistryFragment): print "Uninstallation was successful" else: print "Uninstallation failed, `regedit.exe` returned %d" % iRetVal # ----------------------------------------------------------------------------- def server(): """ Launch URLs sent via TCP/IP using ``gnome-open``. """ import socket import subprocess as sp sSock = socket.socket() sSock.bind( ("", giPORT) ) sSock.listen(1) print "Listening on", giPORT while True: sConn, tAddr = sSock.accept() rMessage = "" while True: rData = sConn.recv(1024) if rData == "": break rMessage += rData sConn.shutdown( socket.SHUT_WR ) # Indicate finished # Spin until remote side is also finished while sConn.recv(1024) != "": pass sConn.close() print rMessage sPH = sp.Popen([ "gnome-open", rMessage]) sPH.wait() # ----------------------------------------------------------------------------- def client(): R""" ``sys.argv`` should look like so: 0. ``H:\IE2FF.py`` 1. ``C:\Program Files\Internet Explorer\IEXPLORE.EXE`` 2. May be absent. If present, should be one of: a. ``-nofile``, indicates DDE usage (TODO) b. ``http://...`` (or other URL) """ import sys if len(sys.argv) < 3 or sys.argv[2] == "-nofile": # TODO: Implement fallback behaviour (start local browser?) return rMessage = sys.argv[2] import socket import subprocess as sp sConn = socket.socket() sConn.connect( (giHOST, giPORT) ) sConn.sendall( rMessage ) sConn.shutdown( socket.SHUT_WR ) # Indicate finished # Spin until remote side is also finished while sConn.recv(1024) != "": pass sConn.close() # ----------------------------------------------------------------------------- def main(): import platform from optparse import OptionParser sParser = OptionParser( description=" ".join( x.strip() for x in """\ This script catches URL clicks (actually iexplore.exe invocations) on a Windows machine and forwards them over TCP/IP to a Linux machine (also running this script) which launches them in the default browser (e.g. Firefox). """.splitlines() ) ) sParser.add_option( "--install", action="store_true", dest="yInstall", help="Intercept all invocations of iexplore.exe. Internet Explorer "\ "will not be available until --uninstall is used.", default=False) sParser.add_option( "--uninstall", action="store_true", dest="yUninstall", help="Disable interception of iexplore.exe. Internet Explorer will "\ "be available again.", default=False) # Parse command line sOptions, lArgs = sParser.parse_args() if sOptions.yInstall: install() if sOptions.yUninstall: uninstall() if platform.system() == "Linux": server() else: client() # ----------------------------------------------------------------------------- if __name__ == "__main__": main() ===== BASH shell ===== ==== Red STDERR ==== Runs the command given, but colours STDERR red, making it easier to spot problems when there is a lot of output. Particularly useful with "make" commands. # Red STDERR # rse function rse() { # We need to wrap each phrase of the command in quotes to preserve arguments that contain whitespace # Execute the command, swap STDOUT and STDERR, colour STDOUT, swap back ((eval $(for phrase in "$@"; do echo -n "'$phrase' "; done)) 3>&1 1>&2 2>&3 | sed -e "s/^\(.*\)$/\1/") 3>&1 1>&2 2>&3 } export -f rse # Export function, so it is available to subshells Examples: rse find /etc -iname \*.conf
...
/etc/debconf.conf
/etc/ca-certificates.conf
/etc/gssapi_mech.conf
find: /etc/ssl/private: Permission denied
/etc/pam.conf
/etc/nsswitch.conf
/etc/sestatus.conf
...
==== Multi-Move ==== Allows renaming of multiple files using pattern matching. # Multi-Move # mm [file list/glob] [regexp match] [regexp replace] function mm { [ "$#" -eq 0 ] && echo "mm [file list/glob] [regexp match] [regexp replace]" >&2 && return for file in $1; do mv "$file" "$(echo $file | sed -e "s/$2/$3/")"; done } export -f mm Example: # Files before: $ ls 1.dat 1.txt 10.txt 2.dat 2.txt 3.dat 3.txt 4.dat 4.txt 5.txt 6.txt 7.txt 8.txt 9.txt # Rename *.txt -> *.txt.bak $ mm '*.txt' '.txt' '.txt.bak' # Files after: $ ls 1.dat 1.txt.bak 10.txt.bak 2.dat 2.txt.bak 3.dat 3.txt.bak 4.dat 4.txt.bak 5.txt.bak 6.txt.bak 7.txt.bak 8.txt.bak 9.txt.bak # Files before: $ ls a_1.html a_2.html a_3.html b_1.html b_2.html b_3.html c_1.html c_2.html c_3.html a_1.txt a_2.txt a_3.txt b_1.txt b_2.txt b_3.txt c_1.txt c_2.txt c_3.txt # Pad numbers with zeros # Use escaped parenthesis to capture one or more characters in the range 0-9, and replace them with "0" + "previous contents" $ mm '*' '\([0-9]\+\)' '0\1' # Files after: $ ls a_01.html a_02.html a_03.html b_01.html b_02.html b_03.html c_01.html c_02.html c_03.html a_01.txt a_02.txt a_03.txt b_01.txt b_02.txt b_03.txt c_01.txt c_02.txt c_03.txt ==== Generate Cscope database ==== Creates a cscope database for the current directory, suitable for use with a vim which has been compiled with +cscope. #!/bin/bash echo Creating file list... find `pwd` "$@" -name \*.c -o -name \*.h -o -name \*.cpp -o -iname Makefile -o -name \*.mak | sed -e 's/^/"/g' -e 's/$/"/g'> cscope.files echo Building database... cscope -b -q echo Done ==== Propagate RC files ==== Simple script to push my config files from the current host to all others in my list #!/bin/bash # Copies local .vimrc, .screenrc and other settings to all my other accounts # TODO: Possibly account for my tablet PC, which isn't always on cd ~ FILELIST=" .vimrc .screenrc .bashrc .bash_profile editor.sh less.sh propagate_rc_files.sh .vim " if [ $# -gt 0 ]; then HOSTLIST="$@" else HOSTLIST=" meermanr@ikari.robmeerman.co.uk: meermanr@ryoga.robmeerman.co.uk: meermanr@ryo-ohki.robmeerman.co.uk: " fi function IPof() { # POSIX if [ -x "$(which traceroute 2>/dev/null)" ]; then traceroute -m 1 -w 2 $1 2>&1 \ | head -n1 \ | cut -d " " -f 4 \ | tr -d "()," # Windows (Cygwin) elif [ -x "$(which tracert 2>/dev/null)" ]; then tracert -h 1 -w 1 $1 \ | head -n2 \ | tail -n1 \ | cut -d " " -f 5 \ | tr -d "[]\015" # Snip ^M off the end, stupid Windows<->Cygwin interworking! fi } # Find local IP if [ -x "/sbin/ifconfig" ]; then LOCAL_IP=$(/sbin/ifconfig | head -n2 | tail -n1 | sed -e 's/^[^0-9]\+//' | cut -d ' ' -f1) elif [ -x "/cygdrive/c/WINDOWS/system32/ipconfig" ]; then LOCAL_IP=$(ipconfig | grep -C3 "Local Area Connection" | grep "IP Address" | tail -n1 | cut -d : -f 2 | sed -e 's/\s\+\(\S\+\)\s\+/\1/') else LOCAL_IP="" fi echo LOCAL_IP = \"$LOCAL_IP\" for HOST in $HOSTLIST; do TARGET_HOST=$(echo $HOST | cut -d '@' -f2 | cut -d ':' -f1) TARGET_IP=$( IPof $TARGET_HOST ) # Function call echo $TARGET_HOST = \"$TARGET_IP\" [ "$LOCAL_IP" = "$TARGET_IP" ] && echo "Skipping..." && continue [ "127.0.1.1" = "$TARGET_IP" ] && echo "Skipping..." && continue echo Uploading to $HOST scp -r ${FILELIST} $HOST echo ___ echo done ==== pb ==== Runs a command in a Microsoft Platform Builder environment. #!/usr/bin/bash # vim: set ft=sh: # Runs a command in a Microsoft Windows CE Platform Builder environment # If you don't have a buildenv.bat you can create one by running # # pbxmlutils /getbuildenv > ../../buildenv.bat # # from inside a build window (Visual Studio -> Build -> Open Release Directory # in Build Window). # Note the parenthesis below, all commands inside them are run in a # sub-shell, which prevents the export polluting the current shell ( PB_BUILDENV_BAT=/cygdrive/c/WINCE600/OSDesigns/mwin/WINCE600/OSDesigns/mali-win-ebmpcore/buildenv.bat # Convert all "set" commands in $PB_BUILDENV_BAT to "export" commands, # and evaluate them (updating this sub-shell's environment) eval \ $( grep '^set' $PB_BUILDENV_BAT \ | sed \ -e 's/^set /export /' \ -e 's/=/="/' \ -e 's/$/"/' \ -e 's/\\/\\\\/g' \ ) # Convert paths in $PATH from Win32 style ("c:\foo;c:\bar") to Cygwin # style ("/cygdrive/c/foo:/cygrive/c/bar) using the `cygpath` utility # NB: Must use absolute paths here, because the effective $PATH is trashed! eval \ $( echo export PATH=\"$PATH\" \ | /usr/bin/sed -e 's/;/\n/g' \ | /usr/bin/xargs --delimiter="\n" /usr/bin/cygpath -u \ | /usr/bin/tr "\n" ":" ) # Run arguments in this altered sub-shell "$@" ) Example: # What happens without the "pb" command $ build -bash: build: command not found # And with the "pb" command $ pb build /home/robmee01/usr/bin/pb: line 32: export: `WECVERSIONFORROSEBUD.1488=2': not a valid identifier BUILD: [Thrd:Sequence:Type ] Message BUILD: [00:0000000000:PROGC ] Build started with parameters: BUILD: [00:0000000001:PROGC ] Build started in directory: c:\work\WinCE\estimate_current_directory ... ==== mymake ==== Convenience script which picks the appropriate build method. Suited for use as a one-fire make command from vim. '':set makeprg=~/usr/bin/mymake'' Written primarily to allow me compile GNU-Make and Microsoft-Platform-Builder((i.e. WindowsCE builds)) driven sources using a single command which works out which to call, and mangles the output from Platform Builder so it looks more like "traditional" compiler output which I vim can parse. #!/usr/bin/bash # Wrapper script to find the most appropriate build method depending on where # it is called from # Configure pre/post scripts # Note that ":" is a BASH built-in which returns true (0) if [ -r ./mymake-pre ]; then PREEXEC=./mymake-pre else PREEXEC=: fi if [ -r ./mymake-post ]; then POSTEXEC=./mymake-post else POSTEXEC=: fi if [ -e sources -o -e dirs ]; then # WindowsCE # Mangle the output so it resembles every other compiler on the planet, # and thus can be intepreted by decent text editors $PREEXEC && \ pb build "$@" 2>&1 \ | tr -d "\\015" \ | grep -e "WARNN\\|ERRORE" \ | sed -e "s@^.*] @@" -e "s@ : @:@" \ && $POSTEXEC exit fi for filename in ./mymake ../mymake; do if [ -x $filename ]; then $PREEXEC && exec $filename "$@" && $POSTEXEC elif [ -r $filename ]; then $PREEXEC && source $filename "$@" && $POSTEXEC exit fi done if [ -e Makefile -o -e GNUMakefile ]; then $PREEXEC && make "$@" && $POSTEXEC exit fi echo "Don't know how to make this directory, sorry." ==== python-win ==== Runs a python script using the Win32 version of Python from within Cygwin. Only useful if you have both Cygwin-python and Win32-python installed on the same machine. #!/usr/bin/bash # Wrapper to allow shell scripts to be run using the Windows (not Cygwin) version of Python # Specifically, translates the first argument into a Windows-friendly path, and hands-off to Python SCRIPT=$1; shift exec /cygdrive/c/Python25/python.exe $(cygpath -w "$SCRIPT") "$@" ==== dexplore-jump.py ==== Jump to a topic in the Microsoft Visual Studio 2005 Documentation explorer (''dexplore.exe'') via the command line. I wrote this so I could use vim's manpage-lookup feature when editing WindowsCE source code. Hard-coded to only search within the documentation for "Windows CE 6.0". **Note:** This script requires Python's win32com module, which is part of [[http://sourceforge.net/projects/pywin32/|Python for Windows extensions]]. **Note:** ''dexplore.exe'' must already be running for this script to work. Code to start ''dexplore.exe'' if needed may be added later. **Note:** This uses the python-win script (above) as the interpreter to ensure it is run using the Win32 installation of Python #!/home/robmee01/usr/bin/python-win # Jump to keyword in DExplore # Huge thanks to Rob Chandler and his page http://www.helpware.net/mshelp2/dexplore/dexplorer.htm import win32com.client, sys h = win32com.client.Dispatch("DExplore.AppObj") h = h.Help # Probably not necessary h.SetCollection("ms-help://MS.VSCC.v80/MS.VSIPCC.v80/MS.WindowsCE.v60.en", "Windows Embedded CE 6.0") if sys.argv[1][0:10] == "ms-help://": h.DisplayTopicFromURL(sys.argv[1]) else: h.DisplayTopicFromKeyword(sys.argv[1]) Example: # Jump to the topic "GetModuleFileName" in an existing instance of dexplore.exe $ dexplore-jump.py GetModuleFileName