Technical Tidbits

Over the years I have run into hundreds of technical "oh that is cool" or "that is good to know" items. Of course months later I forget them but I remember that some little slight of hand is possible. Of course Google has helped a lot with this. Yet, many times the actual item I am looking for is gone, or worse, lost in a sea of info-crud(tm).

The pages are organized in no particular order (yet) and the title of the pages hopefully are detailed enough to make locating the magic trick needed easy to find.

Applications and Tools

This section contains chapters dedicated to specific devices, applications, utilities, and services.

BASH magic

== Table of Contents ==


== My .bashrc.user file right now ==

export SVN_EDITOR=/usr/bin/vim

# make bash autocomplete with up arrow
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'

# a simple function that will check for headers of HTTP server.
# invoke: headers  []
#                 port number defaults to 80 if not provided.
headers() {
    server=$1; port=${2:-80}
    exec 5<> /dev/tcp/$server/$port
    echo -e "HEAD / HTTP/1.0\nHost: ${server}\n\n" >&5
    cat <5
    exec 5<&-

# a quick way to check if a port is open or not. You can always use netcat or telnet,
# but I find this easier
# invoke: testPort   []
#                  protocol can be either tcp or udp and defaults to tcp.
testPort() {
    server=$1; port=$2; proto=${3:-tcp}
    exec 5<>/dev/$proto/$server/$port
    (( $? == 0 )) && exec 5<&-

# Look if the parent is konsole
dcop konsole-$PPID &> /dev/null
if [ $? -eq 0 ]; then
    #OK we are on konsole
    export INKONSOLE=$PPID
    alias vi='kvi'
    alias ssh='kssh'
    export INKONSOLE=0

function touch_konsole {
    # Lil'optimization: only change if PWD change
    if [ "x$PWD_LAST" != "x$PWD" ]; then
        #PRETTY_PWD=`echo $PWD | sed "s\\^$HOME\\~\\ "`
        PRETTY_PWD=`echo $PWD | sed "s,^$HOME,~,; s,\(/[^/]*\)/.\+\(/.\+/.\+\)$,\1/..\2,"`
        # You can have a the user name too
        if [ $? -ne 0 ]; then # our great imagination forces us to make comprobations
            # If you know other situations for this, or you have a fix, please, mail it to thc{at}wanadoo{dot}es
            echo "DCOP call failed. Situations causing this are:"
            echo " -There are lost .DCOPserver_* files in your home, please remove them. This can happen when hostname change."
            echo " -If you called 'su(1)', there are various DCOP sessions for the user you come from."
            echo "DCOP calls to Konsole are disabled"
            unset PROMPT_COMMAND

# Look if we have Konsole enviroment
if [ "x$KONSOLE_DCOP_SESSION" != "x" ]; then
    #OK we are on konsole
    export PROMPT_COMMAND=touch_konsole
    export DCOP_OPTIONS=""
    if [ "x$USER" = "xroot" ]; then # when root we must use imagination
        # Let's search directly into the parent
        # Is there a '=' char in the user name? come here an fix this.
        export PPID_USER=`strings /proc/$PPID/environ | grep ^USER= | tr = '\n' | tail -n 1`
        if [ "x$PPID_USER" != "xroot" ]; then
            # Oh! a user "suing" in a konsole?... mi parent is not root's konsole
            export DCOP_OPTIONS="--user $PPID_USER --all-sessions"
            # Here you can do other crazy things like the X auth for root...
            # or salutate you
            # export XAUTHORITY="/home/${PPID_USER}/.Xauthority"
            echo "Hello ${PPID_USER}, you got root"

== Scripting References ==

# [ Advanced Bash-Scripting Guide] A very excellent textbook / self-study manual / reference / source of knowledge on shell scripting techniques. (An alternate location in case the first goes offline [].) Some favorite sections are:
#* [ Converting DOS Batch Files to Shell Scripts]
#* [ Here Documents]
# Jeremy Mates (a great guy and one heck of a shell hacker) has a great page on shell tricks at

== Command Line Limits and Parallel Operations ==

=== Finding the command line limits ===

: All shells have a limit for the command line length. Each UNIX / Linux / BSD system has a limit on how many bytes can be used for the command line argument and environment variables. When you start a new process or type a command these limitations are applied and you will see an error message as follows on screen:

:: Argument list too long

: The following command will expose the current command line length limitations


$ getconf ARG_MAX

: However, when a command is executed the current environment is pre-pended to the actual command. Therefore, to insure you are not exceeding the real limit your command length you need to use the following command to compute the current actual input command line length limitation


$ echo $(( $(getconf ARG_MAX) - $(env | wc -c) ))

=== Overcoming the command line limits ===

: Knowing the command line limits is one step. The next step is overcoming them when needed. There are two ways:

:* Use either the find or xargs commands; or
:* Use the shell for / while loop

: Examples

:* find command example to get rid of "argument list too long" error

$ find /nas/data/accounting/ -type f -exec ls -l {} \;
$ find /nas/data/accounting/ -type f -exec /bin/rm -f {} \;

:* xargs command example to get rid of "argument list too long" error

$ echo /nas/data/accounting/* | xargs ls -l
$ echo /nas/data/accounting/* | xargs /bin/rm -f

:* while loop example to get rid of "argument list too long" error

ls -1 /nas/data/accounting/ | \
 while read file; do 
   mv /nas/data/accounting/$file /local/disk/ ;

:* Alternatively, you can combine above methods:

find /nas/data/accounting/ -type f |
   while read file
     mv /nas/data/accounting/$file /local/disk/

== Reading Java-style Properties Files with Shell ==

=== Method 1 - Scan through the file to get an individual property ===

This works well if you just want to read a specific property in a Java-style properties file. Here's what it does:

:# Remove comments (lines that start with '#')
:# Grep for the property
:# Get the last value.
:# Strip off the property name and the '='

sed '/^\#/d' | grep 'someproperty' | tail -n 1 | sed 's/^.*=//'

It can also be handy to strip off the leading and trailing blanks with this sed expression:


Which makes the whole thing look like this:

sed '/^\#/d' | grep 'someproperty' | tail -n 1 | sed 's/^.*=//;s/^[[:space:]]*//;s/[[:space:]]*$//'

Shell scripts can use this technique to set environment variables, for example:

JAVA_HOME=`sed '/^\#/d' | grep 'jdk.home' | tail -n 1 | sed 's/^.*=//;s/^[[:space:]]*//;s/[[:space:]]*$//'`

=== Method 2 - Convert the properties file to a shell script, then run it ===

This method is good for when you want to read the whole properties file. However, it only works if the property names are valid shell variable names. For example, property names with a '.' in them will cause this to break. Here are the original pseudo code steps that inspired this method.

The steps are:

:# Create a temp file.
:# Stream the properties file while:
:## Removing the whitespace around the equal sign (=)
:## Converting it to Unix LF format (unless you are sure the file was produced on Linux).
:## Escaping any double quotes in the string
:## Double quoting all the values, to preserve spaces.
:# Source in the file. Shell should ignore Java properties file comments automatically.
:# Erase the temp file.

Here is the code:


cat | \
dos2unix | \
sed -re 's,[[:space:]]*=[[:space:]]*,=,;s/"/\\"/g; s/=(.*)$/="\1"/g' >$TEMPFILE

source $TEMPFILE

=== Method 3 reloaded - Use AWK ===

This method is the same as the previous one except we use an inline awk script to replace any non-alpha-digit-underscore characters in the property names with underscore. This solves the problem the previous method has when it processes properties that have '.' in the name, for example.

Some clever person could probably figure out how to do the same thing with sed, but awk works.


:# Create a temporary file.
:# Stream the properties file to stdout and (optionally) filter out dos style linefeeds.
:# Split each line using "=" as the field separator. If there are exactly two fields, then substitute the non-identifier characters with underscore in the first one, and print the second field with quotes around it.
:# If the line doesn't have an =, or seems to have more than two values, just print it out.
:# Source in the temporary file and delete it.

cat | dos2unix |
awk '
 BEGIN { FS="="; } \
 /^\#/ { print; } \
 !/^\#/ { if (NF == 2) { n = $1; gsub(/[^A-Za-z0-9_]/,"_",n); print n "=\"" $2 "\""; } else { print; } }' \
source $TEMPFILE

=== Putting It Into Play ===


# convert the properties to a shell script that can be sourced
rm -f $TMP_PROPS
[ $? -ne 0 ] && echo "Error: unable to convert properties to shell script" $$ return 1

# source the parsed properties
set -v
set +v
rm -f $TMP_PROPS


The file "$HOME/bin/":

# Filter to convert a .properties file to a .sh file that can be sourced
#   converts . to _ in property names
# Use this script in a pipe:
#   cat | prop2sh >
# Note: although Java properties can contain forward references, this does
#       not work for shell variables.  Therefore, if you use variables in
#       the value portion of a property, ensure that variable is defined
#       earlier in the file.
#       E.g:
# and ${}
# value
#          ==> WILL NOT WORK -- reverse the lines

function change_var_name

    # replace '.' with '_'

    echo "$var"
function change_var_value

    # exit if there are no variables with periods
    echo $value | grep -qv '\${.*\..*}' && echo "$value" && return

    # parse out the prefix, the variable and the suffix

    # recursively change the suffix
    suffix=`change_var_value "$suffix"`

    # reconstruct the value
    var=`change_var_name $var`

    # return the value
    echo "$value"
while read LINE
    # simply output the line if it's blank or a comment
    echo $LINE | grep -q ^# && echo "$LINE" && continue
    echo $LINE | grep -q ^$ && echo "$LINE" && continue

    # parse out the property name and value

    # change the name and value appropriately
    NAME=`change_var_name $RAWNAME`
    VALUE=`change_var_value "$RAWVALUE"`

    # strip surrounding quotes (to be added)

    # output the .sh compatible line
    echo "export $NAME=\"$VALUE\""


== Fun with Variables ==

What programming language would be complete without variables? Naturally bash provides shell variables which are generally set/assigned a simple equals statement using following syntax with no white space around the equals sign:

=== Expand Variables In A File ===

Need to somehow expand the "shell variables" in a file to create a new "expanded file"? Try this:

eval "cat <<− E
$(paste /dev/null file)

Note: Using the $(paste /dev/null file) is important because it prevents a line with E in the input file from ending the external cat prematurely.

=== Default Values for Variables ===

If value is not given, the variable is assigned the null string. In shell program it is quite useful to provide default value for variables. For example consider script:
rsync -avz -e 'ssh ' user@myserver:$RSRC $LOCAL

This script can be run as follows:
$ ./ /var/www .
$ ./ /home/vivek /home/vivek

It will sync remote /home/vivek directory with local /home/vivek directory. But if you need to supply default values for a variable you can write as follows:

: ${RSRC:="/var/www"}
: ${LOCAL:="/disk2/backup/remote/hot"}
rsync -avz -e 'ssh ' user@myserver:$RSRC $LOCAL

: ${RSRC:="/var/www"} ==> this means if the variable RSRC is not already set, set the variable to /var/www. You can also write same statement with following code:

if [ -z "$RSRC" ]

You can also execute a command and set the value to returned value (output). For example if the variable NOW is not already set, execute command date and set the variable to the todays date using date +"%m-%d-%Y":

: ${NOW:=$(date +"%m-%d-%Y")}

=== Set A Variable From A Here Document ===

It is possible to set a variable from the output of a here document. This is actually a devious form of command substitution.

variable=$(cat <set -- *' will take the files matching the * glob and assign them to $* (the input parameters for a function), (ie. $* = $1 $2 $3 $4 ....)

set -- *; echo $*

=== basename and dirname ===

Many times, scripters will use external commands like ''basename'' and ''dirname'' and ''tr'' because they don't realize they can instead use ksh/bash builtins. An added bonus is the builtins are faster and require less system resources because no sub-process is spawned. Here is how to get ''basename'' and ''dirname'' using the shell builtins:


However in shell scripts it is often necessary to convert a relative path name, i.e. "../usr/../lib/somefile" to an absolute path name starting with a slash "/", i.e. "/lib/somefile". The following code fragment does exactly this:

DIR=`dirname "$relpath"`
BASE=`basename "$relpath"`
ABS_PATH="`cd \"$DIR\" 2>/dev/null && pwd || echo \"$DIR\"`/$BASE"

==== Finding an Anchor Directory ====

Sometimes it it handy to let things run from where ever they may be. But you want to be able to reference a tree structure based on the script's location. This is a little tricky in a shell. But so long as you can guarantee the name of the "top" of your tree section name you can do this.

The "TopDIR" would be the name of the directory you are trying to get a lock on.

if [ -z "$SCRIPT_HOME" ] ; then
SCRIPT_HOME="$(cd ${0%/*} ; pwd)"

== Fun With Paths ==

=== Replace Path Item ===

Here is a cute function for doing PATH control. I consider it a more intelligent way to make sure something is on the PATH.

# this code replaces an old path item with a new one
# //$ This function will be deprecated and replaced with scripts in $SO_PLATFORM_HOME/bin
function ReplacePathItem

    case "$PATH" in
            PATH=`echo $PATH | sed "s;$OLDITEM;$NEWITEM;g"`



if [ ! -z "$OLD_JAVA_HOME" ]
    ReplacePathItem "$OLD_JAVA_HOME/bin" "$JAVA_HOME/bin"
    export PATH=$JAVA_HOME/bin:$PATH

unset -f ReplacePathItem

=== Clean Up The Path ===

You might want to not have the error messages under normal operations as those can be annoying as well as screw up other scripts.

# Clear out duplicate or bad PATH dirs

for ITEM in `echo $PATH | tr ':' ' '`; do
    [ ! -d $ITEM ] && echo "$ITEM from PATH is not a directory skipping" && continue
    [ ! -r $ITEM ] && echo "$ITEM from PATH is not readable skipping" && continue
    case "$NEWPATH" in
            echo "duplicate PATH entry for $ITEM"


== The Wonders of Set ==

set -a and set -e also do some useful things. set -a (AFAIR) will export all variables to sub-shells and child processes. set -e will die on the first error received when you're running multiple commands at once (eg. cd rob; ls; touch readme; rm readme etc.)

One useful note is that you may use $@ to preserve each as a separate parameter (AFAIR), one early mistake I made was not to double-quote it:
main "$@" is the proper way to pass the current args on to a function in order to preserve them.

== Command History Expansion ==

Setting bash command history expansion to use the up arrow like MATLAB. This is one of those simple once you know it but annoying to figure out settings. Via google, I found two solutions:

* Solution 1: Add the following lines to “.inputrc” in your home directory:

"\e[A": history-search-backward
"\e[B": history-search-forward

* Solution 2: Add the following lines to “.profile” or “.bashrc”:

# make bash autocomplete with up arrow
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'

== Tab Command Completion ==

OK so there are is the whole auto command complete thing. Should the tab key list the available commands or cycle through the options? The jury in my head is still out on which behavior I prefer. I wish they would hurry up and decide so I can get used to it.

Again via Google, I found two solutions:

* Solution 1: Add the following lines to “.inputrc” in your home directory:

TAB: menu-complete

* Solution 2: Add the following lines to “.profile” or “.bashrc”:

# make tab cycle through commands instead of listing
bind '"\t":menu-complete' 

== Arithmetic Evaluations ==

Bash can perform arithmetic evaluations. They are much easier to using expr tool. Below are some examples. Note the absence of $ prefix for variable names.

* increment value of variable 'count' by one


* set total = total+current


* ternary expression


== Commenting Out ==

One line of shell code can be "commented out" using the "#" character. Sometimes however it would be nice to "comment out" more than one line of code, like the C "/* */" comments.

One way to comment out multiple lines is to use the ":" command (that returns "true") and the "here document" in non-interpreted mode. It looks like this:

: << '--END-COMMENT--'
your comment here

This way, there is no restriction with single quotes. Note that any delimiter can be used in place of "--END-COMMENT--".

=== Controlling Output On The Fly ===

You can quickly comment or uncomment commands by using a variable. This could be triggered by a command line argument if you wanted. The basic approach is this:

# Comment out one of the two VAR lines below depending on
# whether you want comments turned on or off
# The VAR line below, when uncommented, will comment out all commands
# with $VAR in front of them
#VAR=": #"; export VAR
# The VAR line below, when uncommented, will allow execution of all
# commands with $VAR in fron of them.
VAR=""; export VAR
$VAR date

== Setting default values for variables ==

In shell scripts it's often useful to provide default values for script variables, i.e.

if [ -z "$Host" ]
Host=`uname -n`

For this kind of assignment the shell has a shorthand:

: ${Host:=`uname -n`}

This means: if the variable "Host" is not already set, execute the command "uname -n" and set the variable to the returned value.

== tr replacement ==

Many times, scripters will use the external command ''tr'' because they don't realize they can instead use ksh/bash builtins. An added bonus is the builtins are faster and require less system resources because no sub-process is spawned. Here is how to get ''tr'' using the shell builtins:

$ word="MiXeD"
$ # replaced: word=$(echo $word | tr [A-Z] [a-z])
$ typeset -l word
$ echo $word
$ # replaced: word=$(echo $word | tr [a-z] [A-Z])
$ typeset -u word
$ echo $word

== Debugging ==

The easiest way to enable shell debugging is via the -x option to the shell, which enables command tracing. The -x can either be specified on the command line, or on the first line of the shell script.

$ cat somescript
echo foo
$ sh -x somescript
+ echo foo

$ perl -i -ple 's/sh/sh -x/' somescript
$ cat somescript
#!/bin/sh -x
echo foo
$ chmod +x somescript
$ ./somescript
+ echo foo

Sometimes the following flags are useful, too:

set -v # print out info on what would be executed instead of executing
set -e # terminate the script at first error
set -u # unset variables are fatal errors

== Neat bash Tricks ==

* Replace 'orig' in previous command with 'repl' and execute the new command


* Copy 'filename' as 'filename.bak'

cp filename{,.bak}

* Create dir1/subdir1 dir1/subdir2 dir2/subdir1 dir2/subdir2 dir3/subdir1 dir3/subdir2

mkdir -p dir{1,2,3}/subdir{1,2}

counter-reset: section; /* Create a section counter scope */
H2:before { /* negate the TOC H2 header to prevent accidental increment */
counter-increment: section; /* Add 1 to section */
content: "Section " counter(section) ". ";
#toc H2:before { /* put in the TOC H2 header formating */
counter-increment: section 0; /* stop section from incrementing here */
content: " "; /* Remove the section notation from the contents header */
H2 {
counter-reset: subsection1; /* Set subsection1 to 0 */
color: #800517 ; /* Firebrick */
#toc H2 { /* make the TOC H2 header formating right */
color: black ;
H3:before {
counter-increment: subsection1;
content: counter(section) "." counter(subsection1) " ";
H3 {
counter-reset: subsection2; /* Set subsection2 to 0 */
color: #306754 ; /* Medium Sea Green */
H4:before {
counter-increment: subsection2;
content: counter(section) "." counter(subsection1) "." counter(subsection2) " ";
H4 {
counter-reset: subsection3; /* Set subsection to 0 */
color: #AF7817; /* Dark Goldenrod */
H5:before {
counter-increment: subsection3;
content: counter(section) "." counter(subsection1) "." counter(subsection2) "." counter(subsection3) " ";

Blackberry Stuff

'''These are the little hints and steps I have found from various lists on the Internet and from experimenting with my Blackberry. I have a Blackberry 8800 with OS 4.2. So these combinations may not all work for you.'''

== Introduction ==

:ALT key = has half moon on top of it. left side of keyboard below the A.
:CAP key = bottom right key (AKA SHIFT key)

== Stuff On The Web ==

* A great place to go look for hints, tips and help.
* [ HOWTO: BlackBerry As Modem For Laptop]

== In General ==

# ALT-Escape - Brings up Taskbar on any screen
# ALT-CAP turns on CAPS lock (an oval with an up arrow shows up in the upper right corner of the screen)
# CAP-ALT turns on NUM lock (a # sign shows up in the oval in the upper right corner of the screen)
# Hold the ALT key while you roll the thumb-wheel to scroll horizontally in any field where you can enter or view text.
# Hold the ALT key while you roll the thumb-wheel to scroll through a field to view options.
# Hold the CAP key while you roll the thumb-wheel to select multiple items in a list.
# Type the first letter of an item in an options list or menu to jump directly to that item.
# Type the first letters of a name or the initials separated by a space to find a contact in the Address Book screen.
# Press C to compose a new message from the home screen or within Messages
# Rearrange the ribbon: In the Home screen (ribbon), hold the ALT key and click the thumb-wheel to bring up a menu. You can then select Move Icon, Hide Icon, or Show All. If you hide an icon, to show it again click Show all and it will appear with an X over it, bring up the menu on it and uncheck hide icon.
# The Blackberry display can be backlit to allow reading messages in the dark. To get the light to come on, hit the backlight button usually in the lower right of the keyboard. Hopefully you have a backlit keyboard as well so you can type in the dark.
# Turning the radio off and back on when coverage is spotty and you've lost signal seems to help. Also going into network and selecting register now will help. And if you have a phone/combo BB then on some you can go into network and select Scan for networks. then select the network you are on, for example T-mobile, then save. After doing this the radio will reset and you may receive a register message and a new service book, which you can accept from the service book menu in options.
# Password protect your BB. To set a password, go to the Options menu to the Security item. Set the desired timeout (I use 15 minutes) and enable/set a password. In conjunction with this, you should also go to the Options menu to the Owner item, and enter your name, phone number, etc. When the password protection kicks in, this is what is displayed on the screen.
# After five incorrect attempts at typing in your password, the password starts echoing (figuring you really need all the help you can get). After ten attempts, it performs a lobotomy on itself and almost all data is cleared. The Memo database, however, is retained and visible.
# Hold down the ALT (half moon) while you type LGLG. This brings up the event log where you can clear events (frees some memory) or view them or copy the contents to mail to someone.

== Typing Shortcuts ==

=== When typing text in an application: ===
* To insert a period, press the Space key twice. The next letter is capitalized.
* To capitalize a letter, hold the letter key until the capitalized letter appears.
* To type the alternate character on a key, press the Alt key and the character key.
* To type an accented or special character, hold the letter key and roll the trackball to the left or right. For example, to type ü, hold U and roll the trackball to the left until ü appears. Release the letter key when the accented or special character appears.
* To type a number in a number field, press a number key. You do not need to press the Alt key.
* To type a number in a password field, hold the Alt key and press the number key.
* To turn on NUM lock, press the Alt key and the Left Shift key.
* To turn on CAP lock, press the Alt key and the Right Shift key.
* To turn off NUM lock or CAP lock, press the Shift key.
* To switch typing input languages, on the Language screen, verify that the Use Input Language Shortcut field is set to Yes. Hold the Alt key and press the Enter key. Continue to hold the Alt key and highlight a language. Release the Alt key.

=== Insert symbols: ===
* To insert an at sign (@) or a period (.) in an email address field, press the Space key.
* To type a symbol, press the Symbol key. Type the letter that appears below the symbol.

=== Working with text: ===
* To highlight a line of text, press the Shift key and roll the trackball.
* To highlight text character by character, hold the Shift key and roll the trackball left or right.
* To cancel a text selection, press the Escape key.
* To cut highlighted text when typing, press the Shift key and the Backspace/Delete key.
* To copy highlighted text when typing, press the Alt key and click the trackball.
* To paste highlighted text when typing, press the Shift key and click the trackball.

== While Dialing ==

# To dial alphanumeric phone numbers hold down the ALT key while typing the letters of the letters portion of the phone number.

== Messages ==

(Some of these work in tasks, memos, and appointments also)

=== In the Message List ===
* To open a higlighted message, press the Enter key.
* To compose a message from the message list, press C.
* To mark a message as opened or unopened, press the Alt key and U.

==== Sorting: ====
* ALT-i: show only incoming mail
* ALT-o: show only outgoing mail (messages you have sent - i.e. checkmark icon)
* ALT-p: show phone log messages
* ALT-s: show Short Message Service (SMS) text messages
* ALT-v: show VoiceMail messages
* To view all your messages again, press the Escape key.

==== Navigating: ====
* t: go to top of message listing
* b: go to end/bottom of message listing
* n: go to next date
* p: go to previous date
* u: go to the oldest unopened item / go to next unopened item
* j: go to the next related message (following threads)
* k: go to the previous related message (following threads)
* backspace: close message listing and return to home screen (ribbon)
* spacebar: bottom of screen, next press is a pagedown
* cap + spacebar: move to the top of the screen, next press is a pageup

==== Deleting multiple messages at once: ====
# Highlight a date (i.e. Fri, Oct 05,2001) in the message list, hit the thumb-wheel and select delete prior. All messages prior to that date will be deleted.
# Hold down the CAP key and use the thumb-wheel to scroll up or down to select messages. Click the thumb-wheel and select 'Delete Messages'. Combine this with a sorting tip to make it even more useful (i.e. To delete the last 5 messages that you have sent (checkmarks), press 'ALT' then the letter 'o' (for outgoing) this will show all of your sent messages, then per this tip scroll down and select the last five.

=== While In A Message ===

==== Filing ====
* i: file a email message to a specified folder

==== Navigating: ====
* enter or spacebar: scroll down a page/screen at a time
* ALT-enter or spacebar: scroll back a page/screen at a time
* b: scroll to end of page
* t: scroll to top of page
* u: jump to closest unread message
* n: jump to next message
* p: jump to previous message
* j: go to the next related message (following threads)
* k: go to the previous related message (following threads)
* backspace: close message and return to message listing
* q: view the email address of a contact, in a message, highlight the contact. Press Q. To view the display name again, press Q again.

==== Reply-to/Forward: ====
* r: reply to or answer this message
* l (L): reply to all
* f: forward this message

==== Select Text ====
# Press the CAP key and then scroll the thumb-wheel to select. If you release the CAP key and scroll the thumb-wheel you will select entire lines. Press the CAP key again at any time to start selecting by character again.
# Hold down ALT key + CLICK the thumb-wheel (click it don't hold it down), release the thumb-wheel and then use it to scroll whole lines. You can press and hold the ALT key at anytime to start selecting by character again.
# Click scroll wheel and select SELECT from the menu. scroll wheel to select entire line. click scroll wheel again and select copy.
With either method, click the thumb-wheel and select 'Cut Selection' or 'Copy Selection'... to paste, open a new message or whatever, click the thumb-wheel, and then select 'Paste Selection'.

==== Composing Message ====
# Press and hold a letter to capitalize it (with Key Rate enabled).
# Press the SPACE key twice to insert a period and capitalize the next letter.
# Press the SPACE key to insert the "@" and "." characters in an Email field. Backspace and type again to over-ride this like you would with AutoText.
# Press and hold a letter key and roll the thumb-wheel to scroll through international/accent characters, equation symbols and other marks.
# Instead of having your one auto signature set up you can do this for multiple signatures:
## Delete your existing auto sig.
## Make an autotext entry where:
##* Typing zz (or any irregular 2 letter combo like zx) replace it with:

Your Name
Your Company
Your phone extension etc.

#: Autotext entries can really hold a lot. give it a try. For example you can zz as your "professional" signature and zx as you "casual" sign off. Once you get used to it, you don't even notice that you're entering 2 extra letters at the end of a message.

== Calendar (from Agenda screen) ==
# Press T to go to "Today" in the Calendar screen.
# Press G to go to a specific date in the Calendar screen.
# The default date range for Calendar synchronization in Desktop Manager may not suit most people. While configuring the Calendar synchronization, click the Advanced button and specify the desired range.

== Battery ==
# The largest draw on battery life is the transmitter, so if you are transmitting a lot, you can expect reduced battery life. And the backlight.
# Being in fringe or no coverage areas will eat up your battery as it attempts to transmit. If you're in an area that is out of range, turn your BB's radio off manually or use Options / Auto On Off to conserve battery life.

== Geek Tips ==
# Rather than looking at the bars, you can change the display of the 'signal strength' to read in real numbers. While at the home screen (ribbon), hold down the half moon ALT key while you type NMLL. The bars should change to read numbers. For the 'signal strength', if you are in the 100 area (that means -100 dBm), you will be transmitting at maximum power (2 Watts), and since coverage is hit and miss at this weak a signal, you may end up transmitting many times before the packets make it through. This might help explain any poorer than normal battery life. I consider anything at -90 to -50 excellent.
# To get the bars back: While at the home screen (ribbon), hold down the half moon ALT key while you type NMLL again.
# Hold down the half moon ALT key -AND- the CAP key at the same time, then the letter 'h'. This brings up the "Help Me!" screen that lists version, app version, pin, imei, uptime, signal strength, batterylevel, file free, and file total.
# Hold down the half moon ALT while you type LGLG. This brings up the event log where you can clear events (frees some memory) or view them or copy the contents to mail to someone.
# ALT-CAP-Backspace will reboot your BB.

{{Leeland:css|title|User:Leeland:Tools:Blackberry - Palm is Better|User:Leeland:Tools:Blackberry - Choose Palm if you can}}

Firefox Tidbits

== Taming the Web ==

=== Meebo IM ===

I like Meebo, it hooks me up to all my IM accounts through one interface that I can get to from any web browser. So when the Meebo team wrote a Firefox extension I was all over it. The basic features are:

* An added "Meebo icon" to the top navigation bar
* The ability to pop out IM messages using the icon in the IM toolbar and they'll float over Firefox while you browse.
* A nice IM sidebar that keeps you update to date on all the recent IM activity (friends logged in, recent chats, etc.).
* A very fast means to add and remove buddies using the buddy list.
* Right now you have to keep a Firefox tab open to Meebo, but they are working on it (hey this is only a 0 day old release at this writing)!

IM and browser united as one! Now this is cool. You will need to add the site to the authorized sources for extensions but Firefox will let you do this with only a couple of clicks.

[ Meebo Firefox extension]

=== StumbleUpon ===

First, a warning. StumbleUpon is hazardous to your productivity! StumbleUpon is one of these social networking Web applications that are becoming so popular lately. This one provides a way to find new Web sites that you may find enjoyable or useful.

This extension adds a StumbleUpon tool bar to Firefox. You can get to all the core functionality of StumbleUpon via this tool bar, including setting up an account. You pick some initial categories of the kinds of sites you're interested in (a few examples: Ancient History, Humor, Self-improvement) as part of the sign-up process, and can always tweak these later.

Once everything is set up, you click the Stumble! button in the tool bar to be taken to a random site that has something to do with your categories. If you don't like the site, click the Thumbs Down button. If you do like it, click Thumbs Up. The more sites you rate, the better your Stumbles will match your tastes. If you rate a site that isn't in the StumbleUpon database yet, you can enter some basic information about it so others can stumble onto it.

StumbleUpon isn't all that practical, but it is fun and can transport you back to the days when just idly surfing the Net turned up all kinds of interesting things.

Last version reviewed: 2.91

=== Yahoo Mail Notifier ===

The Yahoo Mail Notifier is fairly basic; it just puts a small mail icon in your status bar and indicates how many new messages are in your Yahoo mail account. It'll display a little pop-up to catch your attention if you want it to. Clicking the icon takes you to Yahoo mail.

Version reviewed:

=== Gmail Manager ===

The Gmail Manager does all that Yahoo Mail Notifier does and more. If you hover your mouse over it, you'll see the total number of new messages, how many spam messages you have, new message counts for all your labels and how much space your mail is taking up. Below all that is a listing of your most recent 10 messages, showing From, Subject and first line of the body of the mail (you can turn all this off). It also supports multiple Gmail accounts, and you can set it to cause all mailto: links to open up a Compose New Message window in Gmail.

Version reviewed: 0.5.3

=== Greasemonkey ===
Let's get this out of the way right up front: Greasemonkey is not for the faint of heart. It basically allows you to add JavaScript to any Web page, but writing these scripts requires a good knowledge of scripting. The good news is that there are many generous souls out there who share the scripts they create.

When first installed, Greasemonkey does nothing. It just enables the scripting. You'll have to write, or install, scripts before you see any changes on your pages.

So what do these scripts do? Almost anything you can do with JavaScript. For example, I use both Google's Gmail and Reader services. I found a script that causes Reader to appear on the same page as my Gmail. That's a pretty big change. (It's easy to disable a script, and even easier to universally disable Greasemonkey, in case you need to undo a change.)

Want your Google search results to appear in two columns? There's a Greasemonkey script for that.

Then there are lots of scripts that do small things like remove the Edit features from Wikipedia. Most of us are never going to edit these pages, so why not clean them up a bit? Another script, shown here, makes Google search results appear in two columns to provide better use of space on wide monitors. The possibilities are endless.

Check out [] for a script repository. If you want to write your own scripts, try [] or pick up [ Mark Pilgrim's Greasemonkey Hacks] from O'Reilly Media.

Version reviewed:

== Visual improvements ==

=== Firefox Showcase ===
Firefox Showcase is a great extension both because of its usability and because it takes away one feature advantage that Internet Explorer 7 has over Firefox: the ability to display thumbnails of all open windows and tabs.

Once you install the extension, you'll have a new Showcase submenu under the View menu. From here you can choose to show thumbnails of all tabs in the current window or all tabs in all windows. (IE7, incidentally, only shows thumbnails of the tabs in a particular window.)

Additionally, you can choose to show these thumbnails in a new tab or in a floating window.

You also get new options under the Sidebar submenu: a choice to open tabs from the current window, or from all windows, in Firefox's sidebar. No matter how you choose to display the thumbnails, once you do, clicking on one of them takes you to that window/tab combination.

In some ways, this extension is too complex for its own good. You can safely ignore most of the options and just use the extension in its default configuration.

If you habitually find yourself awash in open tabs, clicking around looking for the page you need, Firefox Showcase will save you a lot of aggravation.

Check out the [ Users Manual (]

Version reviewed:

=== Cooliris Previews ===
This interesting extension allows you to preview a Web page before clicking off the one you're on. After installing Cooliris Previews, a small blue icon will appear next to any link you hover your mouse over. Slide the mouse over to that icon and a window pops up containing the destination page. For all intents and purposes, you're on that page, except if you move the mouse off the icon and the pop-up window, the preview vanishes.

At the top of the pop-up window are some additional icons that let you lock the window open, open the preview into a new tab, e-mail it to a friend (though this requires registration at the Cooliris Web site), close the window or use a Back button. (You can surf around in the preview window, ergo the Back button.)

It is in some ways more neat than useful, but for certain sites, such as YouTube, the preview is enhanced. If you preview a link to a YouTube page, only the video opens in the preview window.

In addition to the Preview feature, Cooliris adds a context-sensitive search feature to the right-click menu. Highlight a word or phrase on a page, right-click, choose Cooliris Search and select either, Google Images, Google Search or Wikipedia to search on the highlighted term. If you hover the mouse pointer over any of these four selections, the results will display in a preview window. If you click, they'll open in a new tab.

If you find the preview icon is getting intrusive, you can easily disable previews by clicking an icon in the status bar. You can also disable/enable previewing on a site-by-site basis.

Version reviewed: 2.1

=== Colorful Tabs & ChromaTabs ===

'''Colorful Tabs''' is pretty basic. It colors each of your tabs using lovely pastel colors. After a long day of research, this becomes more than just something pretty and can make life easier on tired eyes.

Colorful Tabs is a sight for sore eyes. Tab colors are assigned randomly.

The version reviewed requires Firefox 2.0 or later. Version 1.4 of Colorful Tabs will work with earlier versions of Firefox. See the link for more details.

Version Reviewed: 1.9

'''ChromaTabs''' is in many ways similar to Colorful Tabs. The difference is that ChromaTabs determines a tab's color based on the hostname in the URL. For instance, any tab displaying a Computerworld page might always be a light green color. If you surf away to a different site, the tab's color will change.

Version reviewed: 1.0

It's a personal preference, really. Colorful Tabs assigns tab color at random, and as long as a tab stays open, its color remains the same, no matter where you surf. With ChromaTabs, the colors shift (and some might not be very appealing), but after a while you can tell at a glance where a Computerworld page is loaded just by scanning for that light green color.

== Matters of convenience ==

=== Google Browser Sync & Foxmarks Bookmark Synchronizer ===
If you use Firefox on more than one computer, you might be frustrated keeping track of what bookmarks are where. Google Browser Sync to the rescue. Using your existing Google account, [ Google Browser Sync] will sync not only bookmarks, but sessions, persistent cookies, passwords and history among instances of Firefox on different machines. (This is user configurable on a broad scale -- i.e., you can choose to sync cookies, or not to sync cookies, but you can't make the decision based on individual cookies.) In addition to your Google account name and password, you'll assign a PIN to add additional security to the transaction.

Version reviewed: 1.3.20061031.0

One downside is that the initial sync can take quite a while, and for some folks, Google Browser Sync might be overkill. If all you want to do is sync bookmarks, try [ Foxmarks Bookmark Synchronizer]. You'll have to set up an account with Foxmarks (the same is true of Google, but many of us already have Google accounts), but the initial sync seems much faster.

As a bonus, you can access your bookmarks by navigating to []. This could be handy if you're on a borrowed machine somewhere.

Version reviewed: 0.84

=== Firefox Google Bookmarks ===

If you are using the ``Google Browser Sync & Foxmarks Bookmark Synchronizer`` then you will likely want to add the menu item to show you the bookmarks stored in Google directly. This extension adds a menu item called GBookmarks to the Firefox menu that give you access to your Google bookmarks from any computer. (Needs Google Account to use this extension.)

Version reviewed: 0.3

=== Session Manager ===
Session Manager is a handy utility to help you manage your Firefox tabs. If you're a Web surfer who habitually visits the same sites every morning, all you need do is open the sites in separate tabs and/or windows, and then use Session Manager to save the session under a name of your choosing. After that, every morning start up Firefox and go to Tools > Session Manager, pick your session and voila, all the windows and tabs open up just as you saved them.

You can also choose a saved session as your "Start Session" (instead of just using a start page) that'll open each time you launch Firefox. As an added bonus, Session Manager tracks your sessions as you surf, and if Firefox (or your system) crashes, you can recover the selection of tabs you had open when it crashed.

One last perk: If you accidentally close a tab, you can easily reopen it from the Session Manager menu.

While Firefox 2.0 has incorporated many of Session Manager's functions, I find Session Manager performs its tasks more elegantly and reliably. And if you're still on Firefox 1.5, Session Manager remains your lone option for these features. Note that the most current version of Session Manager requires Firefox 2.0 or later. Earlier, Firefox 1.5-compatible versions can be found on the Web site.

Version reviewed:

=== All-in-One Gestures ===
This is one of those extensions that does more than you'll probably ever need, but the core function is to assign commands to "Gestures" made with the mouse. For instance, holding down the right mouse button and dragging the mouse a bit to the left issues a "Back" command. A right-mouse button/dragging up combo opens a new tab. And so on.

A few of the many gestures you can assign commands to. (Click image to see larger view)
There are more than 90 commands available, and the gesture to trigger each is user-configurable. Will you ever remember 90 different gestures? Of course not. But find the commands you use often and assign each a gesture; you'll save yourself miles of mousing.

The extension also provides auto-scrolling, as seen in Internet Explorer. This is where you click the middle button, then move the mouse up or down to start the page continuously scrolling. The farther you move the mouse, the faster the page scrolls.

All-in-One offers a wide selection of small navigation improvements like these. It's really one of those "you have to try it to appreciate it" extensions. But the core functionality of accomplishing frequent tasks via a small twitch of the mouse makes this a must-have for its many advocates.

Version reviewed: 0.18.0

=== IE Tab ===
It's a fact of life that there are still Web sites out there that require (or work better with) Internet Explorer. IE Tab to the rescue. Once installed, it places a small icon in your status bar. Clicking this icon swaps out the rendering engine from Firefox's to Internet Explorer's. In my experience, this is particularly helpful with sites that refuse to play video in Firefox.

You can set filters so that certain sites are always displayed in an IE tab. In fact, the extension comes preconfigured with filters for the Microsoft Update site. It also adds an "Open Link in IE Tab" option to the right-click context menu of Firefox.

Since the extension uses IE's engine, this one is for Windows only.

Version reviewed:

=== Download Statusbar ===
You're probably familiar with that sometimes-pesky Downloads window that pops up whenever you download a file in Firefox. Download Statusbar suppresses that window from popping up, and instead provides you the same information in the status bar at the bottom of the browser window. (You can still manually open the Downloads window if you find you need it.)

You can roll your mouse over the filename and get a pop-up tool tip with some extra information about your download, too (where it's being download from, and where it's being saved to, the speed of the download, percentage complete and so on).

An additional feature, still in beta, is to automatically run your virus scanner against downloaded files.

Pretty simple, but it helps to "clean up" the browsing experience. By the way, this is from the same developer as Download Sort, below.

Version reviewed:

=== Download Sort ===
If you find yourself doing a lot of "Save Link As" or "Save Image As" downloading, then Download Sort will be quite a timesaver. It allows you to file downloads by extension, or by a keyword or regular expression in the URL.

Here's how it works. After installing the extension, you set up filters. As an example, you might want any file with the extension .jpg to go into a Pictures folder and anything ending in .zip to go into an Archives folder on your drive.

Now when you right-click on a .jpg image and choose Save Image As, the image immediately downloads into your Pictures folder without any prompting. Right-click on a link to a .zip file and choose Save Link As, and again, the file goes right into your Archives folder. You can optionally have the extension create subdirectories according to date, domain and a few other criteria.

The big drawback here is that Download Sort doesn't intercept normal left-click downloads. It'd be great to see the developer add this capability.

Version reviewed: 2.5.7

=== Nuke Anything Enhanced ===
If you find yourself printing a lot of Web pages, this extension will help you save on ink. Once installed, it adds a "Remove this object" option to the right-click context menu. Place your mouse over information you don't need printed (menu bars, big graphical logos and so on) and use "Remove this object" to zap them temporarily. Clean up the page, then print just what you need.

It can be a little fussy, since you don't know exactly what you're hovering over. Images are pretty straightforward, but menus and other page parts can take some trial and error. There's an "Undo Last Remove" option in case you accidentally zap something you need. Or you can just reload the page to restore it to its original state.

One limitation is that you can't easily nuke Flash content, since when you right-click on Flash, you get its context menu, not Firefox's. Sometimes you can find the container tag that holds the Flash, but it can take some trial and error.

Version reviewed: 0.54

== Information gatherers ==

=== Forecastfox ===
Forecastfox puts an up-to-date weather forecast in Firefox's status bar. Just click one of its icons to see the full weather report at [].

A wide range of options gives you control over how much, or how little, information you want, including a radar image button, severe weather warnings and extended forecasts of up to eight days. You can set up profiles, each with its own set of options. Use these to get the weather in different zip codes if you're a traveler, or create profiles with lots of data for turbulent winter conditions and minimal data for warm, sunny summer days.

The only downside here is that, as with all extensions that rely on data from external Web sites, if the site isn't responding, you won't get any weather updates.

Version reviewed:

=== Answers ===
This one's simple: Just hold down the Alt key (Option key on a Mac) and click on a word, and a window will pop up with information about that word from []. (You can have the results displayed in a full window if you'd rather.) There's a More button in the pop-up that'll open a new tab with the full results page.

For phrases, you still have to highlight, right-click and choose "Look up on" to get results in a new tab.

Version reviewed: 2.2.22

== Web Developer Essentials ==

=== FireFTP ===
Why bounce between applications when you can use FireFTP and have a full-featured FTP client in one tab, and the page you're working on in another?

FireFTP adds an option to Firefox's Tools menu. Click on it and a new tab opens (you can set this to a new window if you prefer) with a traditional two-panel FTP client: local files on the left, remote on the right. By right-clicking on files you can tweak their permissions and all the other things you'd expect to be able to do via FTP.

One nice feature is "View on the Web," which opens the file on the remote server in a new tab. You can set up each host to "snip" out parts of the directory structure (/public_html, for example) so that you get valid URLs rather than the full "physical" path to the file.

The one disappointment is that the client is FTP only. When asked about SFTP support, the author responds (in a FAQ on his page), "Maybe in Version 2.0." Let's hope.

Warning: The combination of FireFTP and Firefox exposes a regression bug in Firefox, causing high CPU usage. If you're still running, you should upgrade to before installing FireFTP.

Version reviewed: 0.94.6

=== Firebug ===
Firebug is an essential tool for developers working on Web pages. It allows you to examine and tweak the HTML, CSS and JavaScript contained in a page, all on the fly. Firebug opens either as a panel at the bottom of the page you're inspecting, or in a separate window.

Exploring all that Firebug can do could be a full article of its own. But for a taste of its power, let's look at basic HTML coding. Once Firebug is activated, running your mouse over an HTML tag in the Firebug window causes a highlighted area to appear on the rendered page, showing where that tag is in the display.

Alternatively, you can put Firebug in Inspect mode. Then running the mouse over the rendered page causes the appropriate line of code to be highlighted. By double-clicking that code, you can edit it and see your changes on the fly. CSS works in much the same way.

For JavaScript development, Firebug finds errors and quickly jumps to where they are in the code. You can change the code and step through a script line by line, set breakpoints and so on. Essentially, it's a full-fledged JavaScript debugging system residing in a browser extension.

Firebug is a great extension and a big topic. If you develop Web pages, you'll want to add this extension to your tool kit. There's plenty of documentation and discussion of what it can do at the [ GetFirebug Web site].

Version reviewed: 1.01

=== Web Developer ===
Web Developer is another virtual Swiss Army Knife for coding. There's some overlap with Firebug, but where Firebug concerns itself almost exclusively with the content of a Web page, Web Developer offers tools to tweak how you're interacting with the page.

For instance, you can modify cookies on the fly, examining, deleting and even manually adding them. You can tweak form settings, clear private data and disable the cache -- all kinds of real-time manipulations.

Web Developer's features can be accessed as a tool bar, or as cascading menus under Firefox's Tools menu. The tool bar is particularly handy since you can see all 12 of the top-level categories of features at a glance (and, of course, it can be easily turned off when you're not in the midst of site development).

This extension can even be useful for regular Web users, thanks to features like the ability to turn off background images. If you've ever struggled to read text displayed, for example, over a background image of someone's dog, you'll appreciate this.

Web developers will probably want to run both Firebug and Web Developer, and the two extensions seem to co-exist peacefully.

Version reviewed: 1.1.3

=== MeasureIt ===
So simple and so useful. After installing this extension, you'll have a small ruler icon in your status bar. When you click on this icon, the client area of your browser window will fade out a little, and you'll have a crosshair cursor.

Use this to drag out a box over a section of the screen. Next to the box is its height and width, measured in pixels. No more guessing as to how wide a sidebar really is, or if the footer is really rendering 150 pixels deep like your style sheet says it should. When you're done, a tap of the Escape key turns off MeasureIt and gives your Web page back to you.

Version reviewed: 0.3.6

=== ColorZilla ===
Another quick and simple tool, this one more for designers than developers. ColorZilla puts an eyedropper icon in your status bar. Click it and you'll get a crosshair cursor. As you run this over a Web page, the RGB values of the pixel under the crosshair will display in the status bar, both as three separate values and as a hex value (e.g., R:255, G:255, B:255 | #FFFFFF).

Additionally, you'll get a border around the style container you're hovering over, an indicator of what the container is and what style it is using (e.g., h2.posttitle or div#content).

ColorZilla also offers a color picker, color palettes and easy access to [ DOM Inspector], a Mozilla tool for examining a page's Document Object Module.

Version reviewed: 1.0

== '''Firefox extensions to avoid''' ==

Popularity shouldn't be the acid test to determine if you should install an extension. The important question is whether it enhances your browsing experience without any nasty side effects. The good news is that the extension community is actually pretty adept at self-policing. Most extensions that are truly "broken" (for instance, they crash your browser or suck up all your CPU power) either get fixed quickly or simply vanish.

But some extensions are "bad" in unapparent ways, or just don't provide enough benefits to be worth running. So, in no particular order here are extensions I avoid.

=== Fasterfox ===

This Web accelerator has a "pre-fetching" mechanism that makes you a very bad Web citizen. Here's how it works: You land on a page and start reading it. While your system is idle, Fasterfox silently starts following links and downloading the destination pages. The idea is that if you then decide to click on one of these links, the page is already cached on your local machine and will pop up very quickly.

This is nice for you, but it can be an incredible waste of bandwidth -- just think about how many links are on a typical page. Even if you don't care about bandwidth, there are reports that some systems administrators are now detecting the extension (and others like it) and blocking clients that are using it. While it is possible to use Fasterfox responsibly, it is best avoided unless you know what you are doing.


=== NoScript ===

This extension is hugely popular and works as advertised, giving you control over which JavaScript, Java and other executable content on a page can run, depending on that content's source domain. You whitelist the sites you consider safe and blacklist the sites you don't.

If you really have a need for this kind of control, then you're already using the extension and will continue to do so. But for the average Web surfer, constantly having to whitelist sites so that scripts can execute in order to give you a fully formed Web experience gets tedious very quickly.

Does NoScript make Firefox safer? Sure. Is it worth the hassle? No. For some reason, paranoia seems to be cool among Web geeks, but for the most part, it is totally unwarranted unless you're sending and receiving sensitive data. Most typical Web surfers who install this extension remove it after the novelty wears off.


=== Adblock and Adblock Plus ===

I understand that these are very popular extensions. But if everyone blocked ads, how would sites such as ours continue to offer content free of charge?

We'll be the first to admit that there are some horribly annoying ads out there. (Buzzing bee, anyone?) But we prefer using Nuke Anything Enhanced to zap the annoying ads while continuing to support the sites we love by allowing most ads to appear.

[ Adblock] or
[ Adblock Plus]

==== Adblock Filterset.G Updater ====

Companion extension to Adblock or Adblock Plus and should be used in conjunction with it. This extension automatically downloads the latest version of Filterset.G every 4-7 days.

[ Filterset.G Updater]

=== PDF Download ===

Here's another extension that works as advertised and is very popular, but it seems like overkill for most users and can cause headaches for others. PDF Download lets you control how Firefox handles PDF files when you click on them -- you can display a pop-up box that lets you choose whether to download, open or view the file as HTML. You can also set it to take one of these actions by default, skipping the pop-up.

Besides the option to view the file as HTML, the real draw for many people is the ability to have PDF files open in the external PDF viewer of their choice rather than the Adobe Reader plug-in for Firefox. Using the external viewer is significantly faster on some systems but not at all on others. Likewise, viewing as HTML sometimes takes longer than opening the PDF with the Adobe Reader plug-in.

So what's the problem? The extension can run into trouble when it tries to handle certain PDFs. Some Web sites send you to a "silent interstitial" page for tracking purposes or some other reason, then redirect you to the PDF. In these cases, the extension won't work. Usually when this happens, PDF Download just gets bypassed, but we've had Firefox freeze up when dealing with redirected PDFs, too. For the little benefit that PDF Download provides, the problems it can cause just aren't worth it.


=== VideoDownloader ===

Homegrown video is hot right now, and why not? Sites like YouTube and Google Video make it easy to put content online for Web denizens to enjoy. The VideoDownloader extension promises a way to download video from these sites and many more for your offline viewing pleasure. Sounds great, right?

The problem is that the extension has to connect to a Web site in order to work, and more often than not, you'll find that instead of a download window, what you get is "Service Temporarily Unavailable." When it does finally work, the download is infuriatingly slow.

Perhaps the extension is a victim of its own success, but until the server issues are addressed, save yourself some aggravation and skip this one.


=== Greasemonkey ===

''Hey, wait just a minute. Wasn't this on our list of best extensions?'' Well, yes it was. Greasemonkey is a really nifty extension to use, as long as you know what you're doing with it. It can potentially get you in trouble because it allows JavaScripts written by other people to run in Firefox. If one of those scripts is malicious, your system could be at risk.

To stay out of trouble, you should use Greasemonkey only with scripts you know are safe, either because you're familiar enough with JavaScript to satisfy yourself or because the script has enough comments at to indicate that people are using it with no ill effects.

If you're not willing to do your homework, skip this extension altogether.


=== ScribeFire (formerly Performancing) ===

This falls into the category of extensions that seem pointless. What we have here is a browser-based tool for writing blog posts. But don't most blogs already have a browser-based editor that works just fine?

Perhaps there's a blogging system out there that needs this kind of helper app, but we're not familiar with it. Until we come upon such a beast, we'd rather skip the overhead of an extension and stick to our blogging software's built-in editor.

Don't take me wrong, ScribeFire is a nice piece of software. I just don't see a need for it at this time. If you do happen to be using blogging software without a decent editor, ScribeFire would be a fine addition to your extension toolbox.

=== TrackMeNot ===

This is another of those extensions for the overly paranoid. The developers apparently became concerned with search engine profiling -- the process in which search engines track your queries and build a demographic profile of you based on those queries -- after a list of three months' worth of search queries from 657,000 AOL members was released on the Web.

We don't mean to downplay privacy concerns, but the technique used in TrackMeNot is questionable. The extension runs in the background while you surf, and sends random search queries to AOL, Yahoo, Google and MSN search engines. What a waste of system resources for both you and the search engines you rely on!


=== Tabbrowser Preferences ===

This extension lets you tweak Firefox's tab settings in various minor ways. For instance, you can add a New Tab button to your tab bar, or control whether the Close Tab icon appears on each tab or at the end of the tab bar. It works fine.

The problem is that if you uninstall the extension, it doesn't reset your tab settings, leaving you with tweaks that you have to undo by going to the about:config page, which many users don't understand or even know about.

The changes the extension makes are fairly subtle, but that's not the point. If you uninstall an extension, you do so for a reason, and once it's uninstalled, the browser should be reset to its default state.


=== Tabbrowser Extensions ===

This extension is a real heavyweight but seems quite popular in certain Firefox circles -- it almost seems to be a rite of passage for Firefox nerds. It gives you lots of control over how tabbed browsing works and even supports plug-ins to add even more functionality. However, it is buggy and conflicts with many other extensions. In fact, even its developers suggest that you not install it!

When the people writing the code suggest you stay away, you should stay away, no matter what your über-Firefox-geek friend says. In any event, the extension hasn't been -- and probably won't be -- upgraded to support Firefox 2.x.


=== Numbered Links 0.9 imposter ===
'''Bonus: Watch out for the [ Numbered Links 0.9 imposter]'''

There's nothing wrong with the Numbered Links 0.9 extension per se. By showing numbers next to links, buttons and other interactive elements on Web pages, it lets you navigate without using a mouse. If you chose to install Numbered Links 0.9 previously, there's no reason not to go on using it if you find it helpful (though you might be interested in [ Conkeror] by the same developer).

However, if you see this extension in your add-ons list and don't recall installing it, then tread carefully. There's a version that has been modified into the FormSpy Trojan, which installs itself as a Firefox extension and keeps the title "Numbered Links 0.9." FormSpy can potentially capture information entered into HTML forms and send them on to a malicious Web site. For more information, see McAfee's [ FormSpy] profile.


SSH Key Authentication

These instructions attempt to explain the basic concepts of SSH Public Key Authentication, what it is, and the steps required to use it.

Logging in with SSH key authentication means that you do not have to use your account password. Instead, you generate a unique 'key' which is just a string of characters. Then you authorize the user of this key (you, hopefully) to log in to your account. You can authorize any key you wish, and adding someone's key would allow them to log in to your account, without even using or knowing your password.

This may sound insecure but due to much research and theory, it can be very secure. But the strength of the method relies on you following certain guidelines. You will generate a pair of mathematically related keys: one public and the other private. You should never give out your private key, nor should you make the file that contains it readable by any other user. This would be like storing your password in a world-readable file. Only your public key will be listed on the server. Ensuring that your private key remains private is the most important of the guidelines that I mentioned. On to the instructions:
edit Windows instructions

You will need an SSH client and a key generator for Windows. The most popular are the programs PuTTY and PuTTYgen. Both are available at

Run PuTTYgen. At the bottom of the window, you can select which type of key you want to generate. You will need to pick between RSA and DSA encryption: go with RSA -- the relevant website is here: Your choice will have no effect on the rest of the steps here. For current applications, 1024 bits is plenty for a key to have. If youre feeling paranoid, 2048 bits should (barring any revolutions in prime factoring) keep your key secure for a decade or two.

Now press Generate and wave your mouse around to seed some randomness in the key generator. Viola, you have a key.

You can then add a passphrase to your key, and use this passphrase whenever you log in to an SSH-enabled server. Alternately you can use no passphrase. Be aware though that if you don't have to use a password to login from your user account, then neither would anyone else that sits down at your computer or otherwise accesses your local account.

The key comment is for your personal convenience, I reccomend youruser@yourlocalmachine and maybe the date, but of course it's up to you. Now save both the private and public key file somewhere (My Documents is a good choice, but pick somewhere that only you will have read access to) and close the PuTTYgen window.

Next, you need to configure PuTTY to use this key. Fire up PuTTY and load your session or create a new one. Then in the Category list on the left, selecct Connection > SSH > Auth. Browse for your key and load it up. Also, you'll want to allow changes of username if your silenceisdefeat account name is different from your local one.

You're almost done, you just need to let the server know that it should let you log in with your new key. Once the server has your key, the ssh, scp and sftp programs use these files automatically. So (optionally, but it is recommended) save your session and connect. Now do the following to authorize your key:

  1. Copy the contents in the Key Generator window ("Public key for pasting into OpenSSH authorized_keys file")
  2. $ mkdir ~/.ssh
  3. $ cd ~/.ssh/
  4. $ touch authorized_keys
  5. Open the authorized_keys file you just created in a text editor.
  6. Paste the contents of that box in to the authorized_keys file. In PuTTY, right click copies and pastes.

You should now be able to log in to using SSH Key Authentication.

These instructions written by silenceisdefeat user samble. Holla on IRC if you use this guide.

edit: I succeeded somewhat differently. In my trial, public key should be rather copied not from the saved public key, but from Public key for pasting into OpenSSh authorized_keys file: textbox in the PuTTYgen window. Note that we can also extract from existing PuTTYgen-type private keys (just load into it, and the public key should be there.). In conclusion, just watch out for the 6th step above! -- octaphial, also silenceisdefeat user
edit OpenSSH instructions

You can get OpenSSH at or through package installers like rpm or apt-get. There are versions for most major non-Windows operating systems (and even one for Cygwin.) Once you have the OpenSSH suite set up, the program you will need to run is "ssh-keygen -t". The -t option is required, and specifies the type of encryption algorithm used in generating a key. You should use RSA encryption to generate your key -- the relevant website is here:

you@yourcomputer:~ $ ssh-keygen -t rsa

You can then add a passphrase to your key, and use this passphrase whenever you log in to an SSH-enabled server. Alternately you can use no passphrase. Be aware though that if you don't have to use a password to login from your user account, then neither would anyone else that sits down at your computer or otherwise accesses your local account.

You now have two new files in the ~/.ssh/ directory. These files are your public and private key and will be named and id_rsa respectively. The ssh, scp and sftp programs use these files automatically. To enable the server to recognize your key, follow these steps:

  1. Log in as normal
  2. $ cd ~/.ssh/
  3. $ touch authorized_keys
  4. Open the authorized_keys file you just created in a text editor.
  5. Paste the contents of your file in to the authorized_keys file verbatim.

You should now be able to log in to using SSH Key Authentication.

These instructions written by silenceisdefeat user samble. Holler on IRC if you use this guide.

Retrieved from " Key Authentication"

From: Alexandre Quesnel
Date: Sat, 6 Aug 2005 03:45:00 -0400
Content-Type: text/plain;
Subject: subclipse with svn+ssh solution

I'm using subclipse 0.9.32 on windows and I was having lots of trouble
trying to get svn+ssh to work. After too much wasted time and giving up
several times I finally found the solution and I though that it would be
well worth my while to post it here.

I got the solution from Tim on the subversion mailing list
but I'll repeat it.

First of all the problem is characterised by getting the following message
in the svn console:

The system cannot find the file specified.

svn: Can't create tunnel: The system cannot find the file specified.

The solution to this is to tell svn where it can find the ssh executable. To
do this you need to add the following line to the svn config file. On
windows the file can be found at:

C:Documents and Settings[user_name]Application DataSubversionconfig

In this file there are sections one of which is tunnels . Make sure this
section head is uncommented (there is no # at the beginning of the line)
After the section head look for a like that starts with 'ssh = ' if you
don't find that line then add the following, if you do find it then modify
it to the following:

ssh = path to ssh client

for example I use:

ssh = D:/TortoiseSVN/bin/TortoisePlink.exe

I find the tortisesvn version of plink better than the original version of
plink because it doesn't pop up an annoying cmd window every time you use
svn. That's it, it should work now. Next you will want to set up an ssh key
with the server so that you don't need to type in your password an
innumerable number of times. Check out: Key Authentication

for a pretty good howto. The steps are pretty simple: generate the keys,
post the public key on the server, connect. Now you will need to fix up the
Subversionconfig file to go along with the changes. Modify the line that
was added before to be:

ssh = path to ssh client -2 -i path to private key file

for example I use:

ssh = D:/TortoiseSVN/bin/TortoisePlink.exe -2 -i

The -2 is because I need to force the use of ssh version 2 from generating a
DSA key. I also don't put a passphrase on my key but that's because I feel
that my workstation is fairly secure. Now subclipse should work with svn+ssh
and have key auth to the server.

Subversion Tricks

== Rollback A Change ==
While working on an issue, I found the following stored in the subversion repository:

$ ls

In subversion (or any version control system really), it is not necessary to make a backup file when committing changes, as the change can be rolled back easily. To roll back in subversion, simply execute a reverse merge:

svn merge -c -1234

1234 is replaced with the commit number that you would like to reverse.

Ubuntu Tidbits

== Table of Contents ==


== From Red Hat to Ubuntu ==

* Rosetta [ switching to Ubuntu from Red Hat Enterprise Linux, Fedora, and Centos]

== Disable Blank Console Screens ==

I have a number of Ubuntu server that boot up in text mode (both VMs and physical boxes). Once they boot I usually access them via ssh sessions so almost never try to type to console directly. However, it is rather annoying to want to see what is being sent to the console because I have to click and hit a keyboard key. So I had to find a way to permanently disable the blank screen feature for the consoles.

Now when you are logged in this is simple to do just enter the command:

$ setterm -powersave off -blank 0

No more screen blanking. However, I need my servers to never blank regardless of sessions or logged in or not. Especially directly from boot when showing the basic login prompt.

The answer to this is to use the [ console-tools] package that allows console options to be controlled.

To stop the screen blanking both the screen saver (BLANK_TIME setting) and the power management standby (POWERDOWN_TIME setting) settings need to be disabled. If these two settings are set to zero (0) in the file /etc/console-tools/config the features will be completely disabled. Alternatively a local settings file called /etc/console-tools/config.d/disable-blank-console can be created containing the following two lines to achieve the same affect.


Actually you can name the file anything you want so long as the name consists of only upper/lower case letters, numbers, underscores, and hyphens.

== Resizing Windows is Hard ==

Gnome windows in Ubuntu 10.x and 11.x are amazingly hard to resize. The issue is the Gnome draggable boarder is only 1 pixel wide. This is a well known issue and in the works to get correct in Natty (but since Natty release has happened and it is still an issue it should be fixed "soon"). Any way this is all documented at [ Bug #160311 Resizing windows by grabbing window borders is difficult]. There are two workarounds. The first is to change the size of the window edges in Gnome by editing /usr/share/themes/Ambiance/metacity-1/metacity-theme-1.xml:

=== Workaround #1 ===

Edit /usr/share/themes/Ambiance/metacity-1/metacity-theme-1.xml. Set the following values in frame_geometry_normal as desired (3 works pretty good). Note this requires restarting x-windows (logout + login) for the change to be used.

<distance name="left_width" value="3"/>
<distance name="right_width" value="3"/>
<distance name="bottom_height" value="3"/>

=== Workaround #2 ===
Use the Keyboard shortcuts to adjust window sizes.

With the window to changed the size of selected/active. Press Alt + F8 to start resizing. Use arrow keys to resize.

Pressing Return will save the resize, escape will revert back to original size.

== VMware issues ==

=== piix4_smbus ... Host SMBus controller not enabled ===

In VMware instances from Ubuntu 8 to the current 11.x I often see the boot error message:

piix4_smbus 0000:00:007.3: Host SMBus controller not enabled!

This is because VMware doesn't actually provide that level interface for CPU access. From Kernel documentation for "i2c-piix4" (<kernel source>/Documentation/i2c/busses/i2c-piix4):

"The PIIX4 (properly known as the 82371AB) is an Intel chip with a lot of functionality. Among other things, it implements the PCI bus. One of its minor functions is implementing a System Management Bus. This is a true SMBus - you can not access it on I2C levels. The good news is that it natively understands SMBus commands and you do not have to worry about timing problems. The bad news is that non-SMBus devices connected to it can confuse it mightily. Yes, this is known to happen...

This error can be fixed by adding the extra line to the bottom of /etc/modprobe.d/blacklist.conf:

blacklist i2c_piix4

== VMware Workstation direct shared folders with host PC ==

VMware Workstation provides a the ability to create shared folders so that the Ubuntu virtual machine (VM) directly access data on your host PC.

Original Article :

The process is:
# Confirm VMWare shared folders module is running with the command

lsmod | grep vmhgfs

#* If not start it and add it to the boot modules:
#*# Start it with the command

sudo modprobe vmhgfs

#*#: * If that fails you need to install VMWare tools see
#*# Add 'vmhgfs' to the /etc/modules file with the command

sudo vim /etc/modules

# Edit the VM settings VM > Settings -> Options (tab)
# Possibly need to reboot
# Mount the shared folder using the vmhgfs filesystem type

sudo mount -t vmhgfs .host:/vmshare /mnt/local

# Profit

# I have not found a way to make it auto-mount. Adding it to fstab causes boot errors because the vmware modules are not yet loaded.
# On my installation of Ubuntu 11.6 the vmhgfs did not show on a reboot. However, it is not actually loaded until it is needed. Once I issued the command

sudo mount -t vmhgfs .host:/vmshare /mnt/local

it did show up for a lsmod.

== Upgrade From Command Line ==

Needless to say I believe in staying on or darn close to the most current versions. This is for security and for future support reasons. I try to NOT take the most current builds right away (I let the rest of the universe beta test the hyper new stuff).

Problem servers have no GUI and I get to them via ssh. So naturally wanted to use the nice update/upgrade utilities from the command line. Thankfully there is a supported way:

To upgrade the applications and libraries this will do:

$ sudo apt-get update
$ sudo apt-get upgrade  

To upgrade the kernel / OS level this is the way:

$ sudo apt-get update
$ sudo apt-get dist-upgrade

== Run Level / inittab ==

Ubuntu doesn't exactly have a run level it uses [ upstart]. There is no real difference between the original run levels 3-5 on Ubuntu.

Ubuntu / Debian Run Levels:
* 0 - Halt
* 1 - Single
* 2 - Full multi-user with display manager (GUI)
* 3 - Full multi-user with display manager (GUI)
* 4 - Full multi-user with display manager (GUI)
* 5 - Full multi-user with display manager (GUI)
* 6 - Reboot

You can find the current run level with the command

N 2

== Set Hostname and Static IP Address ==

Edit the files:
* /etc/hostname
* /etc/hosts
* /etc/network/interfaces
* /etc/resolv.conf

== Service Management ==

* Starting services immediately :

sudo invoke-rc.d {service-name} start

* Stopping services immediately :

sudo invoke-rc.d {service-name} stop

* Enabling a service at boot :

sudo update-rc.d {service-name} defaults

* Disabling a service at boot :

sudo update-rc.d {service-name} purge 

=== sysv-rc and sysv-rc-conf ===

'''Install sysv-rc-conf''' :

sudo apt-get install sysv-rc sysv-rc-conf

* Starting services immediately :

sudo service {service-name} start

* Stopping services immediately :

sudo service {service-name} stop

* Enabling/Disabling boot services :

sudo sudo sysv-rc-conf &

:: [[image:sysv-rc-conf.jpg]]

== Booting Configuration ==

=== Boot to Text Command Terminal ===

Ubuntu 9+ defaults to booting to the X-Window tty () terminal. For Ubuntu server installations where not running X this is annoying because you have to hit to shift to the text terminal. To make Ubuntu directly boot into a text console/command line instead of a X-window do the following.

# Open /etc/default/grub with your text editor with root privilege

sudo vi /etc/default/grub

# Alter the line


to say

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash text"

# Update GRUB 2 to use the new configuration with the commane

sudo update-grub

When using GRUB2 there is an additional problem (detailed here

The GRUB2 directive "vt.handoff=7" needs to set to "vt.handoff=1" otherwise the system boots to a blank screen and looks dead until ALT-F1 is hit.

# Open the file /etc/grub.d/10_linux

sudo vi /etc/grub.d/10_linux

# Alter the reference to vt.handoff=7


to either remove it or set it to a lower number (vt.handoff=1 works best)


# Update GRUB 2 to use the new configuration with the commane

sudo update-grub

=== Change Boot TTY Resolution ===

Using [ GRUB 2] (Ubuntu 9.10 and later):
# Find out what resolutions are available
## Interrupt boot
##* interrupt boot process and display the menu by holding down the SHIFT key until the menu displays on boot - GRUB 2 searches for a depressed SHIFT key signal during boot. If the key is pressed or GRUB 2 cannot determine the status of the key, the menu is displayed.
## press c at the GRUB 2 menu
## enter the command set pager=1 to scroll one page at a time.
## enter the command vbeinfo to find out the available resolutions.
## enter esc Exit back to the menu to finish booting
# Open /etc/default/grub with your text editor with root privilege.
# Add the following new line using your preferred resolution-depth from the list that vbeinfo gave you. It should be like this if you have a 1280x800 screen:


# Save the file and exit. (If you can't save the file you haven't opened it as root, go back and execute gksudo gedit /etc/default/grub providing your password.)
# Execute the command

sudo update-grub

# Restart your computer

== GRUB 2 ==

* [ Ubuntu GRUB 2 documentation]
* [ official GRUB 2 manual at GNU site]

== Enable Ctrl+Alt+Backspace ==

Since Ubuntu 9.04, the Ctrl-Alt-Backspace key combination to force a restart of X is disabled by default (to eliminate the problem of accidentally triggering the key combination). However, Ctrl-Alt-Backspace option has been made a X keymap (XKB) option, replacing the X server "DontZap" option and allowing per-user configuration of this setting. As a result, enabling or disabling the Ctrl+Alt+Backspace shortcut can be done from the keyboard preferences on a per user basis.

Enabling Ctrl-Alt-Backspace for Ubuntu 10.+

# Select "System" -> "Preferences" -> "Keyboard"
# Select the "Layouts" tab and click on the "Options" button near the bottom of the dialog.
# Locate and open "Key sequence to kill the X server"
# Enable "Control + Alt + Backspace" by clicking the check box.
# Close all the dialogs.
# Profit

Windows Keyboard Shortcuts

This is just a random list of keyboard shortcuts for use with any version of the Microsoft Windows Operating System. Each keyboard shortcut is designed to increase user productivity by eliminating the need to reach for the mouse. This is done by executing a combination of keys on the keyboard instead of clicking to perform the same function.

GUI task changing: ALT + TAB will bring up a Window with a list of icons representing programs which are currently running on your computer. While holding the ALT key, press and depress the TAB button to cycle between each icon task.

Cycle To The Next Task: ALT + ESC will switch to next task running on your computer. Hold down the ALT key, press and depress the ESC key to cycle to the next task.

Start Menu: CTRL + ESC will bring up the Start Menu. On some of the newer keyboard, there is a key that has the Microsoft logo: this will do the same thing as pressing CTRL + ESC. Once the Start Menu is opened, you can use your Arrow Keys on the keyboard to navigate through the list of installed programs. Press ENTER to execute the program.

Regaining Control: CTRL + ALT + DELETE will bring up Task Manager and allow you to end a process (terminate a program) if it has crashed or has stopped responding. Select the process which has stopped responding, and then press "END PROCESS" to terminate it.

Jump to next input object: Pressing TAB will generally cycle through the selectable input objects for an application (not necessarily in a good order, but in the order they were added). While a window is open on your Desktop will cycle the cursor to the next button position in the window. The TAB key also works when cycling between text fields when filling out forms on the Internet.

Copy Text Quick: After highlighting text with your mouse, press CTRL + INSERT (alternative to CTRL + C) to copy it to your clipboard.

Paste Text Quick: Pressing SHIFT + INSERT will paste any text that is in your clipboard. Your cursor must also be placed in an area that will accept keyboard input for this to work.

Highlight Text To The Top Of The Page: Click on a line of text anywhere in a document, and press CTRL + SHIFT + HOME to select all text above this area to the top of the page.

Highlight Text To The Bottom Of The Page: Click on a line of text anywhere in a document, and press CTRL + SHIFT+ END to select all text below this area to the bottom of the page.


I grew up using vi. Never got into emacs. Then along came vim. I ignored it of course. Then suddenly vi was gone and vim was quietly mapped to vi. Oh well, had to learn sometime.

== .vimrc ==
Almost all of my vim magic is embodied in my ~/.vimrc file.

au FileType * set tabstop=4|set shiftwidth=4|set noexpandtab
au FileType python set tabstop=4|set shiftwidth=4|set softtabstop=4|set expandtab
au FileType make setlocal noexpandtab|set softtabstop=4

" Make backspace a tad more friendly
set backspace=indent,eol,start

" "highlight" any tabs and all trailing spaces
map \l <Esc>:set list listchars=tab:»·,trail:·<CR>
map \L <Esc>:set nolist<CR>

" Programming "highlight" any tabs and all trailing spaces
map \p <Esc>:set number list listchars=tab:\\|_,trail:.<CR>
map \P <Esc>:set nonumber nolist<CR>

" That awful mixed mode with the half-tabs-are-spaces:
map \M <Esc>:set expandtab tabstop=8 softtabstop=4 shiftwidth=4<CR>

" Mini tabs, small "m":
map \m <Esc>:set noexpandtab tabstop=2 shiftwidth=2<CR>

" Think "little tabs" and "big tabs":
map \t <Esc>:set noexpandtab tabstop=4 shiftwidth=4<CR>
map \T <Esc>:set noexpandtab tabstop=8 shiftwidth=8<CR>

" Turn on and off line numbering
map \n <Esc>:set number<CR>
map \N <Esc>:set nonumber<CR>

" Working with split files ( :sp name-of-another-file-to-edit )
" move to the file in the split above my current window,
" I was typing Ctrl-W, Up (move up a window) Ctrl-W, _ (maximize the menu).
" That's four keystrokes (more if you count Ctrl and Shift), and they are
" all over the keyboard. To help avoid this problem, I created this mapping:
map <C-J> <C-W>j<C-W>_
map <C-K> <C-W>k<C-W>_

" By default, Vim displays the current line of each minimized file, which
" (to me) isn't much help and takes up too much screen real estate. So I use
" this line to change that to no lines:
set wmh=0

" One may also do the dual thing for vertical splits/window changes:
set wmw=0
nmap <c-h> <c-w>h<c-w><bar>
nmap <c-l> <c-w>l<c-w><bar>

" remap the color scheme by hitting f4 and wrapping
nnoremap <f4> :exec "colorscheme ".substitute(substitute(expand($VIMRUNTIME."/colors/*.vim"),'.*[/\\]'.colors_name.'\.vim\c[[:cntrl:]]*[^[:cntrl:]]*[/\\]\([^.]*\).*','\1', 'g'),'[^[:cntrl:]]*[/\\]\([^.]*\).vim\c.*','\1','')<CR><CR>
nnoremap <F5> :echo g:colors_name<CR>
colorscheme desert



Pretty good comparison of Java vs. C#


'''If you know already know Java, Python is cake.'''

== Jython Quickies ==

* Jython Homepage :
* Jython Users' mailing list :
* Jython Developers' mailing list :
* Jim Hugunin's Jython story:
* Samuele Pedroni (''currently chief priest in the Jython temple'') you can refer to as '''the main maintainer and co-author of ''Jython Essentials'''''( His email address is pedronis at

== Thoughts ==

Anyone who knows me will agree that I am far from religious about technologies. I’ve been wrong enough times to be unsurprised when I run into a good argument pointing a direction I wasn’t thinking of going. But sometimes I’m sure of something, and right now I’m 100% take-it-to-the-bank bet-my-career sure of this: dynamic languages (what we used to call "scripting languages") are already playing a large role in Enterprise Software Development; and their role is going to be growing in size, at least for the next little while. Jython represents a chance to have that growth happen inside the Java technology environment, rather than outside.

Why Dynamic? There are four outstanding references on this:
* Paul Graham :
* Bruce Eckel :
* Robert C. Martin :
* Tim Bray :

To try to sum up dynamic languages allow for faster and more agile software development while the real benefits from compiler based coding are entirely replaced via TDD / BDD style disciplines.

When your paycheck is on the line theory doesn’t matter, because the action is in the practice, and a whole lot of programmers out there have noticed that, in practice, they can get the job done quicker in Perl or Python or Ruby or whatever, and it runs just as fast and breaks less and they get promotions and raises. So whether or not you like dynamic languages, you better warm up to ’em because they’re not going away any time soon.

== Rosetta Stone ==


source file


import package.class

Not really as you still need to use the class references


in python does the same as source in bash

== Unit Testing With Jython ==

I am really starting to push the envelope with PyUnit extensively for unit testing EJB applications. I recommend you take some time to learn Python to fully utilize this amazing tool! The only problem is it's easy to get addicted to Python/Jython.

* which was originally published here (

=== Info on PyUnit ===

== Python links ==
* Python Software Foundation :
* Python Tutorial :

== Books on Jython ==
* "Jython Essentials" as an excellent reference :
* "Jython for Java Programmers"

== Samples ==

* pushtotest TestMaker
* an UML tool
* recent: An administration tool for BEA WebLogic :)

'''IBM DeveloperWorks has a bunch of material, some also very recent, on Jython'''
* Charming Jython
* Learn how to write DB2 JDBC tools in Jython
* Recent tutorials:



Template looks like this:

// display current date and time at author location
// =======================================
// copyright Leeland Artra, Stephen Chapman, Felgall Pty Ltd,
// 11 Jul 2001, 25 Nov 2004, 12 Oct 2006, 6 Jun 2009
// and
// permission is given to use this script
// provided that all comment lines in the script are retained

function doTime(name, dst, loc, mtz, stdz, dayz, showDate) {
  // dst : daylight savings  O, set to 1 for yes, update this as you go on and off daylight saving time
  // loc : set to your location (e.g. 'Seattle, WA' or "Mountain Time" or whatever you want)
  // mtz : Time zone offset (hours ahead of UTC, negative if behind)
  // stdz : Standard Time Zone Name standard time indicator (e.g. 'PST', 'EET', etc.)
  // dayz  : Daylight Saving Time Zone indicator (e.g. 'PDT', 'EEST', etc., blank if you don't have daylight saving)
  // showDate : Show just week day or full date

  var newP = document.createElement("span");
  var txt = loc + ' current time ';
  var newT = document.createTextNode(txt);
  var newP2 = document.createElement("span"); = name+'_time';
  var txt2 = setDsp(mtz,dst,stdz,dayz,showDate);
  var newT2 = document.createTextNode(txt2);
  var frag = document.createDocumentFragment();
  var d2 = document.getElementById(name);

  setInterval('updDsp(\''+name+'_time\','+mtz+','+dst+',"'+stdz+'","'+dayz+'","'+showDate+'")', 5000);

// This is used to prevent continuous looping looking for elements not on the page
var pageLoaded = 0;
window.onload = function() { pageLoaded = 1; }

function timeLoaded(i,dst,loc,mtz,stdz,dayz,showDate) {
  if (document.getElementById(i) != null) {
    // Need to wait just a bit for javascript to fully load otherwise we get a garbage date...
  } else {
    if (!pageLoaded) {
function updDsp(time_name,mtz,dst,stdz,dayz,showDate) {
  var obj = document.getElementById(time_name); = setDsp(mtz,dst,stdz,dayz,showDate);
function setDsp(mtz,dst,stdz,dayz,showDate) {
  var dayname = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday','Friday', 'Saturday'];
  var month = ['January','February','March','April','May','June','July','August','September','October','November','December'];
  var now = new Date;
  now.setUTCMinutes(now.getUTCMinutes() + (mtz + dst)*60);
  var dow = now.getUTCDay();
  var minute = now.getUTCMinutes();
  var hour = now.getUTCHours();
  if (hour > 11) {
    ampm = 'PM';
    hour -= 12;
  } else {
    ampm = 'AM'
  if (hour == 0) {
    hour = 12;
  if (minute < 10) {
    pad = ':0';
  } else {
    pad = ':';
  var txt = hour + pad + minute + ' ' + ampm + ' (';
  if (dst) {
    txt += dayz;
  } else {
    txt += stdz;
  txt += ') on ' + dayname[dow];
  if (showDate == 1) {
    txt += ' ' + now.getUTCDate() + ' '  + month[now.getUTCMonth()]  + ', ' + now.getUTCFullYear();
  if (showDate == 2) {
    txt += ' ' + month[now.getUTCMonth()] +' '  + now.getUTCDate() + ', ' + now.getUTCFullYear();
  return (txt);

== Current Time ==

timeLoaded('seattle',1,'Seattle, United States',-8,'PST','PDT',1);
timeLoaded('vienna',1,'Vienna, Austria',1,'CET','CEST',1);
timeLoaded('pune',0,'Pune, Maharashtra, India',5.5,'IST','IST',1);
timeLoaded('cyprus',1,'Levkosía, Cyprus',2,'EET','EEST',1);
timeLoaded('sidney',1,'Sydney, Australia',10,'AEST','ADST',1);

The current time is


Now ain't that cool?

var gmt = new Date;
var lsm = new Date;
var lso = new Date;
lsm.setMonth(2); // March
var day = lsm.getDay();// day of week of 31st
lsm.setDate(31-day); // last Sunday
lso.setMonth(9); // October
day = lso.getDay();
if (gmt < lsm || gmt >= lso) dst = 1; 

Change the Titles

title = "User:Leeland - The Code Janitor";
counter-reset: section; /* Create a section counter scope */
H2:before { /* negate the TOC H2 header to prevent accidental increment */
counter-increment: section; /* Add 1 to section */
content: "Section " counter(section) ". ";
#toc H2:before { /* put in the TOC H2 header formating */
counter-increment: section 0; /* stop section from incrementing here */
content: " "; /* Remove the section notation from the contents header */
H2 {
counter-reset: subsection1; /* Set subsection1 to 0 */
color: #800517 ; /* Firebrick */
#toc H2 { /* make the TOC H2 header formating right */
color: black ;
H3:before {
counter-increment: subsection1;
content: counter(section) "." counter(subsection1) " ";
H3 {
counter-reset: subsection2; /* Set subsection2 to 0 */
color: #306754 ; /* Medium Sea Green */
H4:before {
counter-increment: subsection2;
content: counter(section) "." counter(subsection1) "." counter(subsection2) " ";
H4 {
counter-reset: subsection3; /* Set subsection to 0 */
color: #AF7817; /* Dark Goldenrod */
H5:before {
counter-increment: subsection3;
content: counter(section) "." counter(subsection1) "." counter(subsection2) "." counter(subsection3) " ";

var msg1 = "{{{1}}}";
var msg2 = "{{{2}}}";
var msg3 = "{{{3}}}";
var i = 2;
if ( ! msg1 || msg1 == "{{{1}}}" | msg1 == "title" ) { msg1 = document.title; }
if ( ! msg2 || msg2 == "{{{2}}}" | msg2 == "title" ) { msg2 = document.title; }
if ( ! msg3 || msg3 == "{{{3}}}" | msg3 == "title" ) { msg3 = document.title; }
function Blnk() {
if (i==2) { document.title = msg1; i = 0; }
else if (i==0) { document.title = msg2; i = 1; }
else { document.title = msg3; i = 2; }
setTimeout("Blnk()", 5000);

Save that to a template then reference it like this:

{{Leeland:css|User:Leeland - The Code Janitor|User:Leeland - Bugs are Developer Malpractice|User:Leeland - Did you see that?}}


Anything that doesn't fit nicely in the other main categories of this book simply drop in here. (In other words this is the `.*` or '%%' or "*" section depending on where you cut your teeth first for wild card matching.)

Password Notes

Computer security is not surprisingly greatly underestimated and very misunderstood. To access a computer normally requires 2 items a login name or ID (identification) and a password. The combination of those to items constitutes a key. Like putting a good strong deadbolt on a house, or locking the doors of a car, the login ID and password lock a computer.

Since login ID's are generally your name, or some variation (like first name plus first letter of last name, or vice-versa) the ID is not too difficult to get at. In fact most people's login IDs are used for their email address, which essentially makes them public information similar to the physical address of a home or office.

This means that the password is secret part of access security. The value of a strong password is far more than most estimate. For example, if a password is broken (discovered, guessed, or stolen) not only would all the data be at risk such as financial data, identity data (SSNs, license numbers, birth date, etc.) all the data stored about others (birth dates, full names, children's names, addresses, possibly family information) is also at risk. Further, the cost is also the time and effort that ensues to change all the numbers, clear bad data, and re-gain control of any identities that were stolen. In other words the importance of a secure password cannot be over stated. The password is how a computer verifies that someone logging in is really that person.

Passwords can be stolen or broken in many different ways. For example associates might know enough to guess passwords based on kids names, pet names, birthdays, or anniversaries. The top reason criminals gain unauthorized accesses to a computer is: they guessed someone's password. Often a person's password can be found on a piece of paper next to the their computer. Other times passwords can be stolen simply by watching a person type the password in. Of course there are amazing software applications which are very good at guessing common or short passwords.

== Passwords You Should Never Use ==

According numerous reports, most people still haven't answered the call by security experts to implement more robust passwords. In fact, in a list of the most easy to hack passwords, simply typing '123456' took a truly forgettable top prize.

The company Imperva released a list of passwords most likely to be hacked based on 32 million instances of successful hacking. Imperva named their report "Consumer Password Worst Practices," and some of the entries near the top are truly simple.

The top three passwords all included the simple streaming of numbers: first '123456' followed by '12345' and then '123456789'. Similar entries reappeared at eight and nine on a top ten list. However, the fourth most-hacked password was actually just the word 'Password' followed by 'iloveyou' and 'princess' at spots five and six. (Source:

What the report shows is that people still aren't using effective strategies to protect their sensitive information online. Using these kinds of passwords to protect your email account or, worse yet, banking information, could lead to theft or identity fraud.

=== Top 10 Worst Passwords ===

The following is a list of the most predictable passwords, and should not be used under any circumstances (Source:

# 123456
# 12345
# 123456789
# Password
# iloveyou
# princess
# rockyou
# 1234567
# 12345678
# abc123

== Better Passwords ==

A key finding is that 1 in 3 people choose passwords comprised of six or fewer characters; more than half use passwords based on only alpha-numeric characters; and almost 50 per cent used variations on their name, popular slang terms, or simple strings of consecutive characters from the average QWERTY keyboard -- such as 'asdfg'.

=== Choosing an effective password ===

# The password should be at least 6 characters.
# Mix upper case and lower case characters in your password.
# Mix numbers in the password, not necessarily at the end of the password.
# Mix special characters (for example, #@~).
# Do not use real words, names of people or names of place.
# Do not use a password that someone who knows you may guess (for example, your spouse, child or dog's name).
# Choose a password that will be easy to remember (a line from a song, an expression, initials, associative passwords, etc...).
# Do not write your password down. If you must write it down, protect it as you would your credit card.
# Do not use successive characters on the keyboard as your password (such as qwerty, asdfgh, 12345, etc...).
# Do not use a password that includes user information that is easily obtainable (such as names or initials, a telephone number, ID number, etc...).

Passwords should be simple enough that they won't be too easily forgotten, but the idea is to make cracking the code virtually impossible for either an unknown or known hacker. Doing that seems hard, and if left to purely random characters it would be nearly impossible to remember more than a couple such passwords. However, a few password strategies have been proposed which create strings of random seeming characters while remaining easy for people to remember.

First Letter Method

One good method is to use the first letter of each word in a phrase you can easily remember. For example, "Seattle is great and I even like the rain." would be 'SigaIeltr'.

Poor Spelling Method

Another method is to intentionally use misspelled words strung together using a number or special character. This can be kind of fun such as 'don=tak-dis' or '4braekfast'.

Two Words Stuck Together Method

Just putting some words together with punctuation and no spaces can make a reasonable password. For example "cup glasses pen" put together could be "cuP:Glasses+Pen"

Hacker Leet Speak Method

Many hackers use numbers or punctuation instead of letters to do a basic encrypt of text, as in: h3llo is hello or he!!o is also hello. Don't copy any of these examples, but U s33 wh4t 1 m34n.

=== Changing passwords ===

Many security experts have gotten the idea that changing passwords often is a good idea. This is not really recommended. Password changing should occur based on length of time needed to break it and the value of the data it is protecting. Many companies have a password policy in place where passwords have to be changed every 90 days.

The more often someone is required to change their password the more passwords they have to think up. Therefore, there is a trend over time for the quality of the password to drop as apathy sets in. Many people have reported that they eventually develop some simple algorithm that really creates a poor password, but it still meets the requirements enforced simply because they grew tired of constantly coming up with strong passwords. Therefore, the average quality a frequently changed password will almost always be less than that of a less frequently changed password.

The strength or quality of a password is governed by the amount of time necessary to break the password using brute force methods. Password breaking applications can determine any password less than 6 digits in under a day. The longer and more random the password is the greater the period needed to break it. Strong passwords of 8 digits or more still require more than 10 years of continuous attack to break by brute force. As such changing a password once every five years for a strong 8 character password would be sufficient.

If a password is going to be broken, it will almost always happen through means other than brute force. The standard attack methods (social engineering, PostIt by computer, dictionary word attack, or using personal information) take much less time than a reasonable expire a password. A password that can be broken by the standard attack methods will generally fail in under an hour. Therefore, a strong password changed less frequently is better than a weak one changed frequently.

Changing passwords frequently:

  • Does not prevent brute force attacks,
  • Decreases the quality of passwords used,
  • Increases the likelihood that they will get written down, and
  • Does not help in detecting broken passwords.

However, there are still attacks such as keyboard sniffing, watching someone type the password, or just an insecure link that allows the password to be transmitted in clear text that can compromise even the strongest password. It is true that the longer a password is used the greater the chance it will be detected, stolen, guesses, in other words broken. Also many people have difficulty remembering passwords and therefore the longer a password is used the harder it might be to get use to a new password. It is because there are so many ways to break a password that it is still a good practice to change passwords at some interval.

No matter what is chosen it is important to never reuse previous passwords. Further it might be worth remembering previous passwords if you use the same password in more than one location. Recording past passwords in a secure location could save a lot of trouble in the future if a restored archive is using a prior password long forgotten.

Whatever you do, do not write a password on a sticky piece of paper and put it near your computer.


This section deals with tricks and techniques used to effectively test applications and services.

DB Schema Testing

== Overview Database Schema Version Control ==

It seems that version control for database schema changes has traditionally received little developer attention. Almost everyone just goes ahead and changes database schema, trying to keep it backwards-compatible.

That’s possible if the change is small or non-intrusive (as it is in most cases). For more complex changes you could create ad-hoc script which makes necessary alterations and data conversion. In the most serious cases you stop the service, backup the database (this step is usually skipped by everyone once in lifetime, and only once), upgrade the code, run the conversion procedure, start the service back and hope that it all went ok.

This path of less resistance works reasonably well, and rarely leads to disasters. However, it is still error-prone, and the trend is to make the schema upgrades more relaxed, both error developers and production admins.

More integrated development environments seem to get built-in database schema versioning naturally. Less integrated ones get consistent migration strategy only at individual product level, and the tools are usually ad-hoc.

For example, Ruby on Rails has a very refreshing idea of [ Active Migrations]. They work seamlessly, because:

* Rails is tightly integrated with underlying database;
* migrations are not tied to using SQL: to create table or add new field to it, you use Ruby itself; this helps with thinking in right way about data changes;
* keeping Rails application under Subversion is the encouraged practice;
* rake utility knows about migrations, and helps in creating and deploying them;
* [ capistrano] utility also knows about migrations, and helps in pushing them to remote production/testing servers;

Perl is, of course, less integrated, and its primary offer, [ DBIx::Migration] could use more attention. Being tied to using SQL is its main drawback.

I believe that mimicking Active Migrations functionality should be a new standard for every major open-source language, such as Perl, Python, or PHP. That’s just like everyone currently expects something similar to CPAN for every new language which tries to attract serious attention.

Meanwhile, Microsoft is taking advantage of its integrated development environment, and makes it easier to keep database schema under common version control.

[ Jeff Lynch] writes in [ Microsoft Visual Studio Team Edition for Database Professionals]:

…with Visual Studio Team Edition for Database Professionals, database development now becomes a fully supported part of your application’s development lifecycle. Now all database development is done “off line” and in a “sandbox” environment (this should make your dba stand up and sing!). All user defined functions and stored procedures can be fully unit tested using representative test data automatically generated by the toolset. And best of all, this new Visual Studio sku fully integrates into Team Foundation Server so your database schema (SQL scripts) can be put under source control just like any other C#, BizTalk or Web Application!

[ Sachin Rekhi] writes in [ Extensibility in Team Edition for Database Professionals]:

'''Test Conditions.''' The database unit testing feature allows you verify tests using either SQL assertions or easily configurable UI client-side test conditions. We ship a set of test conditions in the box, including row count, scalar value, empty resultset, etc. But these test conditions are completely extensible so you can imagine creating your own to do more powerful test verification.

'''Check-in Policies.''' Team System also allows you to create custom check-in policies that require certain actions to be performed prior to check-in. For example, a testing policy that ships with TFS enforces that a specific set of tests is run prior to checking in your code. You can implement other such db specific policies if you desired.

Several third-party tools are available to take control of your database schema for MS SQL Server, e.g.:

* Skilled Software: [ SQL Source Control 2003];
* Best SoftTool Inc.: [ SQLSourceSafe];

[ Pramod Sadalage and Peter Schuh (2002)] [] suggest that Agile DBAs maintain what they call a database change log and an update log, the minimum that you require for simple stovepipe projects where a single application accesses your database. However, to support more complex environments where many applications access the your database you also require a data migration log. Let’s explore how you use each log:

# Database change log. This log contains the data definition language (DDL) source code that implements all database schema changes in the order that they were applied throughout the course of a project. This includes structural changes such as adding, dropping, renaming, or modifying things such as tables, views, columns, and indices.
# Update log. This log contains the source code for future changes to the database schema that are to be run after the deprecation period for database changes. [ The Process of Database Refactoring] argues that changing your database schema is inherently more difficult than changing application source code – other developers on your project team need time to update their own code and worse yet other applications may access your database and therefore need to be modified and deployed as well. Therefore you will find that you need to maintain both the original and changed portions of your schema, as well as any scaffolding code to keep your data in sync, for a period of time called the “deprecation period.”
# Data migration log. This log contains the data manipulation language (DML) to reformat or cleanse the source data throughout the course of your project. You may choose to implement these changes using data cleansing utilities, often the heart of extract-transform-load (ETL) tools, examples of which are listed in Table 1.

You may choose to implement each logical script as a collection of physical scripts, perhaps one for each development iteration or even one for each individual database refactoring, or you may choose to implement as a single script that includes the ability to run only a portion of the changes. You need to be able to apply subsets of your changes to be able to put your database schemas into known states. For example you may find yourself in development iteration 10 to discover that you want to roll back your schema to the way it was at the beginning of iteration 8.

== Introduction ==

Have you seen this situation before?

* Your team is writing an enterprise application around a database
* Since everyone is building around the same database, the schema of the database is in flux
* Everyone has their own "local" copies of the database
* Every time someone changes the schema, all of these copies need the latest schema to work with the latest build of the code
* Every time you deploy to to a staging or production database, the schema needs to work with the latest build of the code
* Factors such as schema dependencies, data changes, configuration changes and remote developers muddy the water

How do you currently address this problem of keeping the database versions in working order? Do you suspect this its taking more time than necessary? There are many ways to approach this problem, and the answer depends on the workflow in your environment. The following article describes a distilled and simplistic methodology you can use as a starting point.

* '''Since it can be implemented with ANSI SQL, it is database agnostic'''
* '''Since it depends on scripting, it requires negligible storage management, and it can fit in your current code version management program'''

== Background ==

Any way you look at it, managing development is a hard problem. As your development team and application grow in size, as the requirements change, your overhead costs of management inevitably grows as well. There is a constant need of documentation, regression testing, and version management within this flux to maintain
stability, all without losing "momentum".

As evolving applications change build versions, there are many "application hosts" that need to be synchronized. Often, this leads to a large amount of unnecessary bugs, and an excess percentage of time dedicated solely to the synchronization of versions of the "latest build" among various test/staging/and production platforms.

This article is one of many designed to help you effectively manage your changes with minimal time involved. Since code version management programs have been around before I was born, this article deals with database version management.

== Your options ==

CVS, SourceSafe, ClearCase, SourceGear, SubVersion... The code versioning programs go on and on... What about databases? How do we seamlessly upgrade the schema/data of our production database with minimal or no downtime? How do multiple developers "get latest" on the database schema and data?

You should be aware that some great database "diff" programs already exist. [" StarInix], [ RedGate], and [ SQL Delta] come readily to mind. Some of the higher end ones come with a hefty price tag, because they generate synchronization scripts for you. ''Remember that time you trusted an all-purpose program to generate your HTML for you?'' ''Do you really want to trust an all-purpose program to automatically change your database schema for you?''

Don't get me wrong, these programs are a great starting point for viewing and solving differences, but I highly recommend a more "hands-on" approach to a versioning management policy. Why?'''''When you write the scripts yourself, you not only have a higher degree of control, you have a higher degree of consciousness of what is changing in your database, when it changed, and who initiated the change.'''''

== A Proposed Solution ==

As each database presents its own unique challenge, I don't offer you a one-size-fits-all downloadable application. I offer a time-tested and provably effective methodology that's flexible enough to adapt to the workflow of your development cycle, provided you're ready to get your hands a little dirty with sql scripting code!

=== What you will need ===
* ''An existing source control system''
** Anything, really. You can use the same repository as your code base, or simply a single text file on an accessible network share as a last resort.
* ''Database permissions to run powerful scripts''
** (Nominated developers will need something such as "DBOwner" access where they need to make changes)
* ''A competent knowledge of SQL''
** You will need to know how to write scripts to update the schema and data of your specific brand of database. 90% of the time these are relatively simple scripts.

=== Known limitations to this methodology ===
* There are some data types that can't be manipulated through the use of text-based scripts. These include:
** Binary (image, bitmap, blob)
** Variable-length text (Text-16)
** This may vary depending on your Database platform. Can you employ replication?
* Data changes that rely on temporal data (''datetimes, automatically generated IDs genrated by seeds'') can't be guaranteed consistent throughout multiple databases.
** If your code is relying on consistent autonumbers between database versions, make sure this isn't a conceptual design flaw!

== The Strategy ==
=== The Versioning Table (in essence) ===

For the sake of flexibility and verbosity, declare a table within the database (I'll call it DBVersion) serving as a version indicator. Each row should be able to store some form of a version identifier (EG: if you use FogBugz, this would be your [ CASEID]) , a brief description, when the change happened, who initiated the change, and any extra information you may find convenient.

This table will take the guesswork out of determining the last update to the database.
PK | DBVersionID

=== The Script ===

In conjunction with the table, we need a script that performs actions driven by the values in the table. The following is the script algorithm:

Start transaction [ALL]
    For each version change I to N Do:
    If(not exists (select (1) from DBVersion where DBVersionID=I))
        Start transaction [i]
            <[do your update stuff here]>
            If failure, rollback and exit
            Else Insert into DBVersion values(I,<>,getDate(),User_ID())
        Commit transaction [i]
    Next [i]
Commit transaction [ALL]

Now observe this closely. This script is designed to update only as much as it needs to, without performing the same update twice. If the database already has record of a certain version change, the script will simply move on. The script will run on databases under different versions with a consistent outcome, and a clear audit trail of how and when the changes occurred. If, for some reason, the script fails, the nested transaction structure enforces the database to roll back to its state before execution.

If the script is built properly in this manner, synchronizing the database can be as easy as "getting the latest" version of the code, and running the script before testing. Is someone complaining the latest build doesn't work? Tell them to run the script again and take a hike!

If there is one fundamental rule to the script, it is '''NEVER DELETE FROM THE SCRIPT'''. Occasionally, it seems tempting to delete past mistakes in the script, but this ruins the serial record of changes inside DBVersioning table! Instead of deleting, append an additional version change that amends previous mistakes. After all, most scripts will (hopefully) not require a critically long execution time.

Even though its not necessary that the DBVersioning methodology is implemented from the inception of the database, there are clear advantages of having a build script from the ground up.

=== Real-Life Example Using SQL Server ===

Every major project begins with a single step. Adopting this versioning and "getting into the habit" is the hardest part, so I will supply some code for our beloved SQL Server Northwind database as a starting example.

--Enforce this is Northwind
USE Northwind
--To ensure ACID, begin the main transaction

   --make sure the DBVersion table exists
   if(not exists (select (1) from dbo.sysobjects where id = object_id(N'[dbo].[DBVersion]') and OBJECTPROPERTY(id, N'IsUserTable') = 1))
     print N'Building the foundation ''DBVersion'' Table'
     --build the table
     CREATE TABLE DBVersion (
      [DBVersionID] int NOT NULL ,
      [Description] varchar (255) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
      [ExecutionDate] datetime NOT NULL ,
      [UserID] int NOT NULL

     --any potential errors get reported, and the script is rolled back and terminated
     if(@@error <> 0)

     --insert the first version marker
     INSERT INTO DBVersion values (1,'Build the DBVersion Table',getDate(),User_ID())
   --finished step one
   --Continuing, adding to this script is relatively simple! Observe...
   if(not(exists(select (1) from DBVersion where DBVersionID = 2)))
     print N'Adding a ''DateCreated'' field onto the Customers table for auditing purposes'

     --add the column, or whatever else you may need to do
     ALTER TABLE Customers add DateCreated DateTime not null default getDate()

     --any potential errors get reported, and the script is rolled back and terminated
     if(@@error <> 0)

     --insert the second version marker
     INSERT INTO DBVersion values (2,'Adding the DateCreated field to customers',getDate(),User_ID())


--from here on, continually add steps in the previous manner as needed.

Note that as the above code becomes more verbose, these scripts can reach a daunting length in a short amount of time, so it may be to your benefit to store multiple "chapters" of the conceptual evolution of your database.

=== Modifications ===

The above script is a bare-bones example for you to build upon for your specific needs. Change it as you like, the core concept lies within the algorithm.

* Maybe you could write a stored procedure that takes in a version identifier and some update batch code as arguments.
* Maybe some parts of your data will rely on Replication services for updates
* Maybe theres some extra grunt work involved with updating legacy databases

== Database Unit Testing ==

Whenever you change your database schema, perhaps as the result of a database refactoring, you must be able to regression test your database to ensure that it still works.

* [ DBFit]
* [ DBUnit]
* [ NDbUnit]
* [ OUnit] for Oracle (being replaced soon by [ Qute])
* [ SQLUnit]
** [ TSQLUnit] (for testing T-SQL in MS SQL Server)
* [ Visual Studio Team Edition for Database Professionals]
* [ DTM Database Tools]

== References and Suggested Readings ==

* [ Database Testing: How to Regression Test a Relational Database]
* [ Agile Database Best Practices]
* [ Agile/Evolutionary Data Modeling]
* [ The Cultural Impedance Mismatch Between Data Professionals and Application Developers]
* [ The Process of Database Refactoring]
* [ Survey Results (Agile and Data Management)]
* [ Database Schema Versioning Management 101]

Script Based Unit Testing

== ShUnit ==

:Target: All POSIX (Linux/BSD/UNIX-like OSes)
:Latest Version: 1.3 (15 Jan 2005)

:A unit testing framework for the Bourne shell family of shells modeled after JUnit. This has not been updated since 2005 and I think the project is defunct.

== shUnit2 ==

:Target: All POSIX (Linux/BSD/UNIX-like OSes)
:Latest Version: 2.1.1 (13 Jul 2007)

:shUnit2 is a xUnit based unit testing framework for shell scripts (eg. sh, bash) that is modeled after the JUnit framework. It is designed to make unit testing in shell as simple as possible.

''This is the one I am using right now and has a very active mailing list and development support.''

=== Frame Work ===

Beginning in August of 2007 I began researching into creating a unit test framework to support the Install Tools Team work. As almost all of the Install Tools are one form of script or another we needed a testing set up that was concentrated on scripts. I chose to use mostly KSH based scripts. Although everything runs in BASH scripts by sticking to KSH only syntax I figure we will have the ability to support more platforms.

I created a posix style directory structure in the IS-1.6 starting at '''opt/test'''.

==== test/README.txt ====
The testing sets in this directory are for doing unit and system testing on the Install System Tools and installation environments for Qpass.

These tests are intended only for use by the Install Tools Team members or directly by the Install Tools themselves. These tests should NOT be run by hand unless directed to do so by a team member.

NONE of these tests are guaranteed to produce reliable results except in specific environments or under very controlled conditions. You are welcome to examine them for your own purposes but without any promise of support. They are included with the Install Tools as some of the tools call on specific tests at various points, or because they might be needed for diagnosing specific issues, or for continuous integration support.

The tests are broken up into logical sub-sets to allow running individual tests or sections of tests individually and to keep the more dangerous tests in their own containment areas.

The categories are:

; light safe tests (bin-light-safe) : small unit tests which exercise individual script(s)/function(s) ON THE LOCAL MACHINE that complete within a few seconds (less than 3 seconds per test), require little CPU/IO resources and that DO NOT ALTER any files or directories.

; light destructive tests (bin-light-destructive) : small unit tests which exercise individual script(s)/function(s) ON THE LOCAL MACHINE that complete within a few seconds (less than 3 seconds per test), require little CPU/IO resources and that DO ALTER/DELETE/CREATE files and/or directories.

; heavy safe tests (bin-heavy-safe) : larger or complex unit tests which exercise individual or compound scripts or functions ON THE LOCAL OR REMOTE HOSTS which may require noticeably lengthy amounts of time or CPU/IO resources and that DO NOT ALTER any files or directories.

; heavy destructive tests (bin-heavy-destructive) : larger or complex unit tests which exercise individual or compound scripts or functions ON THE LOCAL OR REMOTE HOSTS which may require noticeably lengthy amounts of time or CPU/IO resources and that DO ALTER/DELETE/CREATE files and/or directories.

Control scripts that run testing suites and helper scripts are in the bin directory and library functions are in the lib directory.

Remember Test-Driven Design is NOT about writing tests it is about better design. These tests should be used to help enforce the DRY principle ('t_repeat_yourself).

- The Qpass Install Tools Team

==== Conventions ====

# Test scripts are named based off the category and the name of what they are testing. Tests of library includes
# Recommended to write one test script per major function.
# Recommended to write one over wrapper script for an application or library.
# If possible write tests to cross check for structural problems as well as general results. For example if a particular variable is being referenced in a sub-script and gets pushed up to a higher level for any reason include a test that checks to make sure that variable is not being referenced in the library or application script to keep future enhancements from accidentally referencing it again.
# Follow TDD (Test Driven Design principles:
## '''BEFORE ALTERING CODE''' write the test(s) to demonstrate the function is implemented properly or the bug is fixed
## Confirm the test(s) '''FAILS'''
## Check in test(s) to the version control system
## Get a quick peer code review of the test(s) '''BEFORE ALTERING CODE'''
##* If any changes come from the review check in the updated test(s) to the version control system
##* Using version control you can actually work on updating the code before the test review as you can revert the changes at any time to demonstrate the testing at a pre-alteration state (don't forget to make a copy of the changes before doing this).
## Do the work needed to get the test to pass
## Once code is passing the test(s) check in changes to the version control system
## Get a complete peer code review of the changes and test(s)
##* If any changes come from the review check in the changes to the version control system
## Refactor if needed
##* If any changes are made check in the changes to the version control system

==== Sample Unit Test Script ====

# Notice that the test script exits with exit $__shunit_testsFailed. This is important so that larger wrapper scripts can test the return results and know if any tests failed within the function. An exit status of zero is both a script true and means no failures.
# There should be a test for the initial function design that confirms the function is doing its job. But any bug should have one or more specific sets of functions to explicitly exercise the bug and thus to confirm that the bug does not come back.

#! /bin/sh

# ENV Setup stuff
# Locate the test base directory
if [ -z "$TEST_HOME" ] ; then
    TEST_HOME="$(cd ${0%/*} ; pwd)"
# Locate the IS base directory
if [ -z "$IS_TOOLS_HOME" ] ; then
    IS_TOOLS_HOME="$(cd ${0%/*} ; pwd)"

# Script containing the function(s) being tested

[ ! -e $TARGET_SCRIPT ] && echo "${0##*/} : Failed target script ($TARGET_SCRIPT) does not exist" && return 1


# Helper Functions

function  internal_Helper () {

# suite tests

function test_SOME_ITEM_MAIN_TEST () {


# suite functions

  # clean up test dir
  rm -r $SAFE_WORK_DIR 2>/dev/null
  mkdir -p $SAFE_WORK_DIR
  [ ${RESULT} -ne 0 ] && echo "problem  with command mkdir -p ${SAFE_WORK_DIR}" && exit 1

# load and run shUnit2
. ${TEST_HOME}/lib/sh/shunit2

exit $__shunit_testsFailed

counter-reset: section; /* Create a section counter scope */
H2:before { /* negate the TOC H2 header to prevent accidental increment */
counter-increment: section; /* Add 1 to section */
content: "Section " counter(section) ". ";
#toc H2:before { /* put in the TOC H2 header formating */
counter-increment: section 0; /* stop section from incrementing here */
content: " "; /* Remove the section notation from the contents header */
H2 {
counter-reset: subsection1; /* Set subsection1 to 0 */
color: #800517 ; /* Firebrick */
#toc H2 { /* make the TOC H2 header formating right */
color: black ;
H3:before {
counter-increment: subsection1;
content: counter(section) "." counter(subsection1) " ";
H3 {
counter-reset: subsection2; /* Set subsection2 to 0 */
color: #306754 ; /* Medium Sea Green */
H4:before {
counter-increment: subsection2;
content: counter(section) "." counter(subsection1) "." counter(subsection2) " ";
H4 {
counter-reset: subsection3; /* Set subsection to 0 */
color: #AF7817; /* Dark Goldenrod */
H5:before {
counter-increment: subsection3;
content: counter(section) "." counter(subsection1) "." counter(subsection2) "." counter(subsection3) " ";