====== 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
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