Sunday, May 24, 2009

Remapping keys for closing windows

Requires: Windows 2000/XP, AutoIt3, and PS Hot Launch.

Really simple tweak today. In Windows, the shortcut to close a window is "Alt-f4." That annoys me. I mean, it takes two hands, the keys are far apart, blah blah blah... Really, this is one of those things that I probably should have just gotten used to, but... I didn't. Instead, I found a way to use "ctrl-down", which works well for me since I'm using "ctrl-(other arrow keys)" for navigating windows explorer. I find it easier and faster to use the right ctrl with the arrow keys for quick navigation. My solution uses an Auto It script and PS Hot Launch.

The AutoIt script is really simple:

      WinClose( "" )

It'll close the program that currently has focus. Simply save that code and map it (using PS Hot Launch) to "ctrl-down." You can use PS Hot Launch to to make up a lot of your own commands and tweak all your keyboard shortcuts without actually changing the keys. The only thing to be careful about is not ruining pre-existing shortcuts.

As I was writing this, I realized the title could be misread to mean closing the Windows Operating System. In case that mistake lead you to read this, I'll give you a tip to close that from Command Line. Simply type in:

      shutdown -s -f -t 0

This'll shut down the computer, not give you program warnings, and do it without a delay. Do "shutdown -?" for more options. Cinchy. You can put the code in a batch file and then map it to a key or put it on your desktop and click on it when you want to shutdown. I think it's a better method than the click through GUI.

Thursday, April 02, 2009

Sorting A Million Integers

Have you ever heard the popular programming interview question, "Show me how to sort one million 32 bit integers in 2MB of RAM?"

I've read about it, but have never actually been asked it in an interview. I think the correct response is that one million 32 bit integers would not fit in 2MB of RAM. But if you assume the problem implies you have an unlimited amount disk space, then the problem is actually solvable. I recently read a good article going over the solution to this problem, so I thought I'd share the links.

The article (and its follow up) assume you know about heaps and heapsort. In case you don't, a heap is a tree-like data structure and heapsort is a very fast sorting algorithm. You can read about them more on wikipedia, Heap here and Heapsort here. If you're unfamiliar with heapsort, I'd strongly suggest you read about Sorting Algorithms in general. Although you'll probably never need to program one for work, it's really nice topic to know about and will probably help you learn to judge your own algorithms better. By the way, here is a java implementation of Heapsort based on the pseudocode from its wikipedia article.
  1. public class Heap {  
  2.   
  3.   public static int[] swap( int[] a, int first, int last ) {  
  4.     int tmp = a[first];  
  5.     a[first] = a[last];  
  6.     a[last] = tmp;  
  7.     return a;  
  8.   }  
  9.   
  10.   public static int[] heapSort( int[] a, int count ) {  
  11.     a = heapify(a, count);  
  12.   
  13.     int end = count - 1;  
  14.     while( end > 0 ) {  
  15.       //swap the root (maximum value) of the heap with the last element of the heap  
  16.       a = swap( a, 0, end );  
  17.   
  18.       //decrease the size of the heap by one so that the previous  
  19.       //max value will stay in its proper placement  
  20.       end--;  
  21.   
  22.       //put the heap back in max-heap order  
  23.       a = siftDown(a, 0, end);  
  24.     }  
  25.   
  26.     return a;  
  27.   }  
  28.   
  29.   public static int[] heapify(int[]a, int count ) {  
  30.     //Start is a's index of the last parent node  
  31.     int start = (count-2)/2;  
  32.   
  33.     while( start >= 0 ) {  
  34.       //Sift down the node at index start to the proper place such that  
  35.       //all nodes below the start index are in heap order  
  36.       a = siftDown(a, start, count-1);  
  37.       start--;  
  38.     }  
  39.   
  40.     return a;  
  41.   }  
  42.   
  43.   public static int[] siftDown(int[] a, int start, int end) {  
  44.     int root = start;  
  45.   
  46.     //While the root has at least one child...  
  47.     while( root *2 + 1 <= end ) {  
  48.       //root*2 points to the left child  
  49.       int child = root * 2 + 1;  
  50.   
  51.       //If the child has a sibling and the child's value is less than its sibling's...  
  52.       if( child + 1 <= end && a[child] < a[child+1] ) {  
  53.         //Then point to the right child instead  
  54.         child = child+1;  
  55.       }  
  56.   
  57.       //If out of max heap order...  
  58.       if( a[root] < a[child] ) {  
  59.         //Then repeat to continue sifting down the child now  
  60.         a = swap(a, root, child);  
  61.         root = child;  
  62.       } else {  
  63.         return a;  
  64.       }  
  65.     }  
  66.   
  67.     return a;  
  68.   }  
  69. }  
With the Heapsort explanation out of the way, here is the article. It's very clear and well written by Guido van Rossum, the creator of Python. Here is the link.

In the comments, someone pointed out the link to a faster algorithm, which you can read about here.

Guido van Rossum replied with:

"That's a cool way of doing it, but not for Python, where an int takes 12 bytes and the list takes another 4 bytes per value. Given some other overhead you'd end up having to make a lot of passes over the full input (the same number as would be feasible with my version -- I used 100 but it could likely be less), which would probably be slower. It's interesting that a heap features in both algorithms. I'm not convinced that it's the fastest way to sort though, despite the O(N log N) complexity. Python's built-in sort is wicked."

By the way, if you like reading articles about problem solving using programming, I suggest you also read Solving Every Sudoku Puzzle and How to Write a Spelling Checker, both by Peter Norvig. They're very good articles. Does anyone know about any other very well written problem solving articles?

Tuesday, March 24, 2009

Manga Meeya


A while ago, I wrote a post about CDisplay, an awesome Comic Book Reading Tool. I had several problems with it (such as its non customizable shortcuts, full screen mode that didn't work with my dual screens, random crashes while opening certain archives, difficulty in dealing with archives within archives...), but I didn't think that anything better existed out there. I tried CDisplayEx, but that was way worse. I was even tempted to write my own manga viewer called "CDisplayEx+Alpha." But there's no need, because I found MangaMeeya, which is everything I ever wanted in a comic reader and more.

My favorite features about MangaMeeya are its ability to display two pages at once, its smart look ahead feature, its ability to view pages either left-to-right or right-to-left, its fit-to-page image resizing, its ability to automatically deal with compressed files, and its customizability. On top of all that, MangaMeeya is blazingly fast. In addition to comic reading, it works really well as a generic image viewer... you can even delete images using MangaMeeya and use it to organize your galleries. I really can't even imagine a better sequential image viewing tool.

I don't really know the history of MangaMeeya or where it came from. I don't even know its home page. I think that it's a Japanese commercial product, but that development has ended over some copyright dispute. At some point, some guy unofficially translated it into English. If anyone has more information about MangaMeeya, please let me know, since I would like to pay for the program.

Link to Download English MangaMeeya:  Download Here

Context Menu

Although its internal file browser is nice, you will definitely want to add MangaMeeya to your right click context menu. That'll allow you to right click on any file or folder and have it open in MangaMeeya. You can do this by going into the registry and adding new shell commands to the '*' and 'Folder' extensions. Or you can download this batch file I wrote and then run it in the directory where MangaMeeyaCE-English.exe is located. I've tested it on Windows XP SP2, Windows XP SP3, and Windows Vista. It will modify your registry, but I can't imagine how it could ruin anything. Still, I assume no responsibility if you somehow use it to wreck your system. The context item will be named CMangaMeeya, which should always put it near the top of your right click list.
  1. @echo off  
  2. if not exist MangaMeeyaCE-English.exe (  
  3.   echo MangaMeeyaCE-English.exe Not Found In Current Directory.  
  4.   echo Please run this file in the MangaMeeya Directory!  
  5.   echo.  
  6.   pause  
  7.   exit  
  8. )  
  9.   
  10. echo Creating Registry file...  
  11.   
  12. echo Windows Registry Editor Version 5.00 > mangameeya.reg  
  13. echo. >> mangameeya.reg  
  14. echo [HKEY_CLASSES_ROOT\Folder\shell] >> mangameeya.reg  
  15. echo [HKEY_CLASSES_ROOT\Folder\shell\CMangaMeeya] >> mangameeya.reg  
  16. echo [HKEY_CLASSES_ROOT\Folder\shell\CMangaMeeya\command] >> mangameeya.reg  
  17. echo @="\"%cd:\=\\%\\MangaMeeyaCE-English.exe\" \"%%1\"" >> mangameeya.reg  
  18. echo. >> mangameeya.reg  
  19. echo [HKEY_CLASSES_ROOT\*\shell] >> mangameeya.reg  
  20. echo [HKEY_CLASSES_ROOT\*\shell\CMangaMeeya] >> mangameeya.reg  
  21. echo [HKEY_CLASSES_ROOT\*\shell\CMangaMeeya\command] >> mangameeya.reg  
  22. echo @="\"%cd:\=\\%\\MangaMeeyaCE-English.exe\" \"%%1\"" >> mangameeya.reg  
  23.   
  24. regedit /s mangameeya.reg  
  25.   
  26. echo Successfully Added Manga Meeya to Context Menu as CMangaMeeya!  
  27.   
  28. pause  
My CDisplay Clone Set Up

Since I've used CDisplay for a few years, I've gotten used to many of its key shortcuts. Since MangaMeeya is so customizable, I switched many of the key shortcuts to be more CDisplay-like. If you're in the same boat as me, feel free to use my configuration settings. Simply download my file and swap it with the file in your MangaMeeya directory.

My Configuration Settings File:  Download Here

My Frequently Used Commands:

Right Arrow: Goes to the next section or page.
Left Arrow: Goes to the previous section or page.
Ctrl+l: Foes to the next directory or compressed file. It's a lowercase 'L', not the number one.
Ctrl+Shift+l: Goes to the preview directory or compressed file. It's a lowercase 'L', not the number one.
l: Allows you to select a folder to open. It's a lowercase 'L', not the number one.
o: Allows you to select a file to open. It's a lowercase 'O', not the number zero.
`: Exits the program
Ctrl+Return: Switches between full screen mode and Windowed mode.

Well, if you read comics on your computer, I hope that you give MangaMeeya a try. It's definitely worth it.

Thursday, March 12, 2009

Simple Anagrammer

While watching my family feverishly play word jumble one day, I decided to write a program to list anagrams. They're still addicted to that game, but I've long since finished up my program. Although it wasn't much effort, I thought I'd go over what I came up with. I wrote code to find single word anagrams, sub anagrams, and multi word anagrams. Since those may be my own nomenclature, I'll explain more clearly. What I'm calling a Single Word Anagram is a single word made up from all the letters of one or more words, such as "cat" to "act" and "resume" to "sure me." What I'm calling Sub Anagrams are possible shorter words made up from one or more words, such as "cat" to "catharsis" and "user" to "sure me." What I'm calling Multi Word anagrams are two or more words made up from all the letters of , such as "sure me" to "resume." Now let's get into my implementation. You should know that my implementation does assume you're more concerned about processing time than memory space.

Also, I originally wrote the code in java, but that language is not very suited for code snippets... it's so, so verbose. So all my code examples are going to be in ruby, with an emphasis of being as concise as possible. I left out all the optimizations I did for my java program, I'm only intending to give a general example of how the code works. So the code runs very, very slowly for long words. But, you'll get the idea of what's going on.

Word List

Before I get into my algorithms, I should go over obtaining a good word list, which is probably the most important step. A word list is exactly what it sounds like and it's what you'll use to determine if a word is real or not. There are lots of word lists out there and you should choose one depending on how thorough you want to be and what language you want to use. There's one in /usr/dict/words if you're using unix, but it isn't very good. I like the very flexible set I found at word list called scowl. It gives you a lot of different word lists you can pick and choose from. The word lists are found in "[scowl home]\final"; they're arranged by a description followed by their part number. A part number of 10 includes a small set of very common words, a part number of 95 contains a very large set of uncommon words. No two sets for a description contain the same word. For my word list, I used english-words.10, english-words.20, english-words.30, and english-words.40. You can trim down the word lists however you want.

By the way, I also found a word list at wordnet that you can also use as a dictionary for looking up definitions. It's free, which is nice.

Single Word Anagrams

After you have your word list, you need a good algorithm. Let's say you want to find all the anagrams of a word. The most straightforward solution is to find all the permutations of the letters in that word and then figure out which ones are real by comparing to your word list. Of course, this is horribly inefficient. I mean, for every n length word you're doing n! comparisons. It's doable on today's machines, just very, very slow.

A better solution is to create a special dictionary (/hash table) for yourself. The key will be the letters of a word arranged alphabetically, the value will be a list of words with that key. So, for example, the key "abt" will contain the word list "bat, tab". You'll have to iterate over your whole word list to create the dictionary, which will take a few seconds, but it is a one time thing. And once it's created, listing the anagram is as easy as arranging the letters of your target word alphabetically and looking it up in the dictionary you created. Make sure everything is lower case for your comparisons.

Here's my ruby code:
AnagrammerBySort.rb - Part 1
view plaincopy to clipboardprint?
  1. def words text  
  2.   text.downcase.scan(/['\w]+/)  
  3. end  
  4.   
  5. def getWordKey word  
  6.   return word.split("").sort!.to_s.strip  
  7. end  
  8.   
  9. def train features  
  10.   model = Hash.new{|hash,key| hash[key] = Array.new}  
  11.   features.each {|f| model[getWordKey(f)].push(f)}  
  12.   return model  
  13. end  
  14.   
  15. NWORDS = train(words(File.new("words.txt").read))  
  16.   
  17. def singleWordAnagram word  
  18.   return NWORDS[getWordKey(word)]  
  19. end  
Sub Anagrams

To find sub anagrams, you first need to be able to determine if a word is a subset of another word. Remember that duplicates count for anagrams, in that "aabc" is not a subset of "abcd." There are many, many ways of checking subsets. In my ruby code example, I just went for the most concise way. It actually performs really poorly.

Once you have a way to determine subsets, all you need to do is iterate over all the keys in your anagram dictionary to determine if they are subsets of your target word. You could also create a new dictionary that maps all words in your word list to their sub anagrams, which would make many future sub anagram lookups instant (it won't be instant for words you haven't seen before.)

Here's my ruby code:
AnagrammerBySort.rb - Part 2
view plaincopy to clipboardprint?
  1. # Whether word1 a subset of word2, duplicates are not ignored.  
  2. def isSubset( word1, word2 )  
  3.   word1.split("").uniq.each { |f|   
  4.     return false if(word1.count(f) > word2.count(f))   
  5.   }  
  6.   return true  
  7. end  
  8.   
  9. def subAnagrams word  
  10.   solution = Array.new  
  11.   NWORDS.keys.each {|f| solution.push(NWORDS[f]) if(isSubset(f,word)) }  
  12.   return solution.flatten!  
  13. end  
(Edit: I found this page which explains a much faster (although much more memory intensive) method for finding Sub Anagrams that uses a tree algorithm *somewhat* similar to a tree. If you want to get deeper into anagrams, you should definitely take a look at it as well as its follow up. The comments are really useful to read too.)

Multi Word Anagrams

Multi Word Anagrams are easy after knowing how to make single word anagrams and sub anagrams. This is how I did it:

1) Make a candidate list of all possible sub anagrams of your target word.
2) Iterate the candidate list. In each iteration, remove the first word on the candidate list and compare it to all other words on the candidate list.
    A) Add a combination of the words (Word A Word B) to the candidate list if they are a subset of the target word.
    B) Record all words that have the same key as the target word key.
3) All the words you've recorded is your solution.

This has to be done carefully or you'll quickly run out of memory. You should add logic to make sure a word is added to the list only if you want to evaluate it later.

Here's my ruby code:
AnagrammerBySort.rb - Part 3
view plaincopy to clipboardprint?
  1. def multiWordAnagrams word  
  2.   solution = Array.new  
  3.   candidates = subAnagrams(word)  
  4.   while candidates.size > 0      
  5.     f = candidates.delete(candidates[0])  
  6.     candidates.each {|g|      
  7.       if( getWordKey(word) == getWordKey(f+" "+g) )   
  8.         solution.push(f+" "+g)  
  9.       elsif( isSubset( getWordKey(f+" "+g), getWordKey(word) ) )  
  10.         candidates.push(f+" "+g)  
  11.       end  
  12.     }  
  13.   end  
  14.   return solution  
  15. end  
  16.   
  17. # Testing #  
  18. require 'pp';  
  19. print "Enter word: "; word = gets.strip  
  20. print "Single Anagrams: "; pp singleWordAnagram(word)  
  21. print "Sub Anagrams: "; pp subAnagrams(word)  
  22. print "Multi Word Anagrams: "; pp multiWordAnagrams(word)  
Alternative Dictionary

I think alphabetical keys work very well for full word anagrams, but not as well for sub anagrams. The solution I preferred involved using a different key for the anagram dictionary. First assign each letter of the alphabet a prime number greater than 1. 'a' can be 2, 'b' can be 3, and so on. All characters you don't care about (such as apostraphes, periods, and spaces) should be assigned 1. You can find a list of prime numbers here. Use the smallest numbers you can, otherwise you'll have bigger problems later. The key for the new dictionary will be the product of the letters in the word. Like before, the value will be a list of words sharing the key. Since you're multiplying prime numbers, you'll know that if Word A / Word B is 0, then the words are anagrams of each other. If Word A % Word B is 0, then the Word A is a sub anagram of Word B. To determine sub anagrams, traverse all the keys in your dictionary and find the anagram lists that divide into the target word key evenly. Finding Multi Word Anagrams will also be basically the same as before, but switching out the way you determined words are anagrams or sub anagrams.

One thing to watch out for with the prime number method is the size the of the key. In java, a 32 Bit Int won't even hold the word resume. I used a long, but even that has issues if I'm trying to find the anagrams for a sentence, much less a paragraph. You can use code to deal with really large numbers or you can just just report an error if the word can't be represented. How you deal with this problem is up to you.

I admit, the prime number algorithm doesn't actually go noticably faster, although there should be significantly less comparisons being made. Furthermore, it does have a limit in the words based on the maximum size of the key. I still like this algorithm better. Anyways, here's the code using the prime number methodology:
  1. #!/usr/bin/ruby  
  2.   
  3. LETTERS = { 'a'=>2,'b'=>3,'c'=>5,'d'=>7,'e'=>11,'f'=>13,'g'=>17,'h'=>19,  
  4. 'i'=>23,'j'=>29,'k'=>31,'l'=>37,'m'=>41,'n'=>43,'o'=>47,'p'=>53,  
  5. 'q'=>59,'r'=>61,'s'=>67,'t'=>71,'u'=>73,'v'=>79,'w'=>83,'x'=>89,  
  6. 'y'=>97,'z'=>101 }  
  7. LETTERS.default=1  
  8.   
  9. # Warning: Requiring 2 Character Minimum, includes apostrophes  
  10. def words text  
  11.   text.downcase.scan(/['\w]{1}['\w]+/)  
  12. end  
  13.   
  14. # Warning: Not checking for value overflows  
  15. def getWordKey word  
  16.   product = 1  
  17.   n = word.length  
  18.   (0..n-1).collect {|c| product *= LETTERS[word[c,1]] }  
  19.   return product  
  20. end  
  21.   
  22. def train features  
  23.   model = Hash.new{|hash,key| hash[key] = Array.new}  
  24.   features.each {|f| model[getWordKey(f)].push(f)}  
  25.   return model  
  26. end  
  27.   
  28. NWORDS = train(words(File.new("words.txt").read))  
  29.   
  30. def singleWordAnagram word  
  31.   return NWORDS[getWordKey(word)]  
  32. end  
  33.   
  34. def subAnagrams word  
  35.   solution = Array.new  
  36.   NWORDS.keys.each {|f| solution.push(NWORDS[f]) if(getWordKey(word)%f==0) }  
  37.   return solution.flatten!  
  38. end  
  39.   
  40. def multiWordAnagrams word  
  41.   solution = Array.new  
  42.   candidates = subAnagrams(word)  
  43.   while candidates.size > 0   
  44.     f = candidates.delete(candidates[0])  
  45.     candidates.each { |g|   
  46.       if ( getWordKey(word) == getWordKey(f+" "+g) )   
  47.         solution.push(f+" "+g)  
  48.       elsif( getWordKey(word) % getWordKey(f+" "+g) == 0 )  
  49.         candidates.push(f+" "+g)  
  50.       end  
  51.     }  
  52.   end  
  53.   return solution  
  54. end  
  55.   
  56. # Testing #  
  57. require 'pp';  
  58. print "Enter word: "; word = gets.strip  
  59. print "Single Anagrams: "; pp singleWordAnagram(word)  
  60. print "Sub Anagrams: "; pp subAnagrams(word)  
  61. print "Multi Word Anagrams: "; pp multiWordAnagrams(word)  
Those are the basic solutions to all the problems I played with. The above should be good inspiration to make your own anagrammer. There's still lots of fine tuning and optimizations you can add that I didn't get into. For example, you may want to ignore words with apostrophes or words less than two characters. You could work in a multi-threaded fashion as well. There are also lots of word list tweaks you can do. There's also a lot of features you can add; fun behavioral tweaks. For example, you might want to look for sentences from your multi word anagrams by throwing them through a grammar checker. There are lots of really fun anagrams out there and I'm sure you could write a program to find many more.

Tuesday, March 10, 2009

Limited User Accounts

I'm usually working on a Solaris machine at work, but occasionally I need to use Windows. I don't do much development on it, but I use it a lot for creating presentations, reports, and design documents, as well as sending e-mails. When I use Windows at work (which isn't really that often), I'm forced to use a machine where I've only got a Limited User Account (LUA). I understand the account type is necessary the way we're doing things at work. But it was annoying because the LUA account prevents me from installing a lot of the programs I'm used to having and a lot of the OS tweaks I'm used to doing.

Apparently, there are a bunch of people who purposefully don't use an Administrator account, primarily for safety reasons. They've got a website over at http://nonadmin.editme.com where they explain how they use Windows with an LUA and are still be able to do all their day to day tasks. That website was an excellent resource. While it hasn't convinced me to use an LUA at home, I no longer have any complaints about using it at work. I got all my most important programs installed. I thought I'd share the two most important workarounds for me.

1. Installing DLLs

There's a program called RegSvrEx that will allow you to register your DLLs when you have a LUA. It's a replacement for RegSvr. Instead of registering the DLL globally (which is potentially dangerous for everyone, I guess), it has an option to just register the DLL for your account (which can only be dangerous for you.) So download the program from http://www.codeproject.com/w2k/regsvrex.asp and run it from command line using "RegSvrEx /c [path to dll]".

2. Adding Right Click Options

Notepad sucks. It really, really sucks. Not only will it mess up UNIX text files, it takes forever to open large files, looks really ugly... man, Notepad isn't good for much. As a text editor, I usually use Textpad. I couldn't get Textpad to install with an LUA, so I went with Notepad++, which is also excellent. But everything still opens with Notepad. Bah. This workaround allowed me to add Notepad++ to my right click menu, which saved me from so many "Open With" dialogues. It's really simple to do. Create a "notepadplus.reg" file with the following contents for your preferred editor and then execute it. This script is easily modifiable for whichever program you want to add to your menu. Please note that you will be modifying your registry:
  1. Windows Registry Editor Version 5.00  
  2.   
  3. [HKEY_CLASSES_ROOT\*\shell]  
  4.   
  5. [HKEY_CLASSES_ROOT\*\shell\[texteditor name]]  
  6.   
  7. [HKEY_CLASSES_ROOT\*\shell\[texteditor name]\command]  
  8. @="\"[path to editor, don't forget to escape '\' characters]\" \"%1\""  

Friday, December 05, 2008

Syntax Highlighters (Code to Html)

Since I'm writing a blog that occasionally contains code, I figured I needed to have a nice way to display code. This post will go over the solutions I've used. If you want to search for solutions on your own, the best base term to use is "syntax highlighter." Of course, the term "syntax highlighter" applies to more than just formatting code for html... but the term "code to html" doesn't return very good results.

Code2Html

When I first started blogging, I used code2html at http://www.palfrader.org/code2html. It's a fully functional syntax highlighter... but the result really isn't that pretty. It's actually very bare bones. But it was pretty easy to use. You can do the "code to html"-ing on their website or you can download a stand alone application (it's a perl script) to do the conversion offline. The documentation is good and I had no problem using it. I could easily produce the following code example:
1 alert("Hello World!")
Ruby Rio and Syntax

I really didn't have a problem with using code2html until I started getting interested in ruby, which led me to find blogs with code examples like this (I'm using an image as the example for convenience in this case):
The entire process to format the ruby code this way is very well described on Wolfmans Howling's blog at http://blog.wolfman.com/articles/2006/05/26/howto-format-ruby-code-for-blogs. But as cool as it is, it only works well with ruby code, and I eventually decided I needed something to highlight the other languages I use too.

Syntax Highlighter

That led me to Syntax Highlighter, which is the solution I currently use. It's an interesting take on syntax highlighting in that it does the conversions on the fly and is entirely written in javascript. It goes through the web page's dom tree at load time and replaces any code blocks contained by a "<pre name='code'>" tag and converts its content to syntax highlighted code. It supports many options, such as line numbering and columns. Take a look at http://code.google.com/p/syntaxhighlighter, the documentation is good and clear. Syntax Highlighter is very good looking and very simple to use.

However, I prefer static html code and don't find it inconvenient to have to convert and paste the code myself. I figured that if I do the highlighting once myself, it's less strain on the user's computer. I also wanted to add a title to the code block as well as change the appearance a little. Since all the javascript is freely available, I made a lot of my own modifications to resolve those issues. It was pretty fun to do and a good javascript exercise for me. It did take some work to finesse everything, but I think I got it all working correctly.

You can download my solution here. Because it's a javascript-only solution, you can run it offline. Just open up the index.html file in your browser, enter your code, and press the convert code link. Then you can copy the code from the result textbox to whatever page you want. Just remember to include a css file, shCoreStaticInclude.js, and the clipboard.swf file in whatever page you paste the code on. I wrote up detailed instructions here. If you want to give it a try online, I have it at http://syntaxhighlighter.relic19.net. That page has a much more detailed description of the process I just described. Here's a sample from my modified syntax highlighter:
  1. alert("Hello World!");  
(Edit: Syntax Highlighter has since updated to version 2.0. It can be now found at http://alexgorbatchev.com/wiki/SyntaxHighlighter. I have no intention of updating my Static Syntax Highlighter at this time. If substantial enhancements to appearance have been made, I'll consider it, but right the only graphical change I've noticed is the new icon usage for the toolbar.)

Quick Highlighter

While I was playing with Syntax Highlighter I came across another top notch syntax highlighter called Quick Highlighter at http://quickhighlighter.com. It converts code to html, much like code2html. You can allow the css to be embedded to avoid having to link to a separate css file. It supports way more languages and formatting options compared to Syntax Highlighter. It's almost as pretty as Syntax Highlighter too. The only downside is that it seems to work exclusively through a web interface, which is why I ultimately decided against using it. Syntax Highlighter is a lot more customizable for me since I can download and muck around with the source code, changing the way it operates, a feat I couldn't as easily do with Quick Highlighter. However, if I found quickhighlighter before I found any of the other solutions, then I probably would have never switched from it. Here's a sample from Quick Highlighter:

  1. alert("Hello World!");


Gist

I also came across Gist, at http://gist.github.com/, as a syntax highlighter. The nicest thing about Gist is that it does all the work for you. You type in code on their webpage and it gives you code to embed on your webpage. You don't need to add any extra includes or css files or even register yourself at their website... Gist takes care of everything for you. It's open source, so you can use your own server if you don't trust Gist. I like my Syntax Highlighter more because of the appearance and options... but really, it's just my personal taste. I definitely see the usefulness, in the simple convenience, of this solution.

To use it, simply go through their website and enter your code to get the embedded code snippet, such as...
  1. <script src="http://gist.github.com/24490.js"></script>  
Add that code snippet to your html and it will show up as...


More Highlighters

Here are some other highlighters. Check them out if you'd like. I didn't see anything extraordinarily special about them, so I don't want to do full write ups... but options are always nice, I guess. They have good documentation of their own. Let me know in the comments if you know of any other syntax highlighters!
http://wiki.ahnfelt.dk/Highlighter.html
http://pygments.org/
http://shjs.sourceforge.net/

Saturday, November 29, 2008

C Pointers

So, do you still get confused about C Pointers? Don't fret, unless you've had a fair amount of practice, it's understandable not to have it all straight. But if you want to get them straight fast, I'd suggest you take a half hour and read this excellent three part article on pointers:

1. http://www.freshsources.com/19930276.HTM
2. http://www.freshsources.com/199302BB.HTM
3. http://www.freshsources.com/199302F2.HTM

Need some C pointer reminders? It's especially good to review them before a major interview if you don't use them every day, since you know they'll probably ask you to reverse a string or something. So here's a quick review:

You take the address of something with &:

     // Address of a is:
     ptr = &a;

If you've got an address you can "reach into" it with *:

     // Contents of ptr is *ptr
     a = *ptr;

The only odd case is when declaring a pointer. Declare a pointer to an int as:

     int *p;

which is most easily thought of as "*p is an int." So if you reach into p with *, then you'll get an int.

One more time, in rules form:

1. &: Always takes the address of a variable. Mentally read & as "address of."

2. *: There are two cases:

    a. Declaring types
    b. Returns the contents of the address stored in a variable. Mentally read this as "content of."

Hopefully, you'll never forget your pointers now. By the way, fresh sources has tons of other good in-depth articles that cover C, C++, and Java. You should take a look. And if you're still trying to get comfy with C, but you haven't read and worked through K&R's C Programming Language... umm... what are you waiting for?

Sunday, October 19, 2008

Reactivating Dynamic Drives for Windows XP Professional

Requires: Windows XP Professional and optionally Ruby, AutoIt

Here's a quick thing that I learned and set up. First, the problem. I frequently use a usb external hard drive at home. As a dynamic disk, everytime I plugged the hard drive in I had to right click my computer, select manage, select disk management, select my external hard drive, and reactivate the disk. Or (after I removed the "My Computer" icon from my desktop) control panel->administrative tools->computer management->disk management->external drive->reactivate disk. It's not too much of a hassle, but it is stupidly annoying to have to go through that sequence often. If you have multiple disks that you want to manually individually activate, always have certain dynamic drives plugged in and want them activated at startup, or (like me) you just don't want to touch the mouse if there's another way, then you'd want a way to automate this process. So I found a way to avoid all the mindless clicking.

It's not really anything new. It's just making use of the diskpart command line utility. Here's documentation on diskpart. I wrote a small ruby script to run the diskpart for me. You can use a static batch script and text file to do this as well. In my actual implementation, I have a different ruby script that makes sure I'm activating the "right" disk. But this sample solution will always activate disk 1:
activateDynamicDisk.rb
view plaincopy to clipboardprint?
  1. require 'win32ole'  
  2. f = File.new"online.txt""w" )  
  3. f.print( "select disk 1\n" )  
  4. f.print( "online\n" )  
  5. f.close  
  6. au3 = WIN32OLE.new("AutoItX3.Control")  
  7. au3.Run( "diskpart /s c:\online.txt" )  
  8. File.delete( "online.txt" )  
Really easy, eh? You can easily set it up to run every time windows boots up. I should also mention dynamic drives only work with Windows XP Pro, so my external drive is unusable with a home edition. Now, the next trick is figuring out how to run this script whenever I plug the device in. I wonder how that's done.

Wednesday, October 15, 2008

Requirements for Tweaks

On this blog, I'm going to occasionally talk about tweaks I make to my machines (of various OSes.) When I say tweak, I mean a special configuration or short script to make my computer more suited to my needs. So basically a hack, but without the bad connotation people give the word "hack" nowadays.

As I've been reinstalling machines, I've been making notes about what I do to them and thought it would be useful to have an easy to find reference online, so I'll be posting cleaned up versions of these notes. Each tweak requires a certain environment (OS and programs), so I'm going to go over the common requirements in depth in this post. That way, when I reference a program, I'll just link here. I'll add to the following list as needed. Here's the list so far:

Windows XP Professional (SP2/SP3) - This is one of Microsoft's Windows Operating System (OS) versions. I'll leave it up to you to figure out how to get the OS and how to install it. XP Professional is different than XP Home, so the tweaks I describe may not be compatable with both. I use Professional myself and don't have access to a Home machine to test things on.

Windows Vista Ultimate - This is one of Microsoft's Windows Operating System (OS) versions. It's newer than XP. I'll leave it up to you to figure out how to get the OS and how to install it. Vista is different than XP, but many of the tweaks I describe should be compatable with both. The only version I have access to is Ultimate.

Solaris 10 - This is one of Sun's Solaris Operating System (OS) versions. It is UNIX based and is notably different from previous versions due to its zones. This is one of the Operating Systems I use most at work.

AutoIt - Auto It is a freeware automation language for Windows. It allows you to easily simulate a windows user with a script... you can send key presses or text to any windoe application. AutoIt scripts end with au3. AutoDownload an installer from http://www.autoitscript.com/autoit3/

PS Hot Launch - PS Hot Launch is a Windows program that allows you to map keyboard shortcuts to files. Invoking the keyboard shortcut will execute the file specified. For example, you can make ctrl-win-w open microsoft word and win-v open your winamp. Using too many of these is not a good idea because you'll probably start running into conflicts. But for a few applications, this program can work wonders. Play around with it. Download this from: http://www.pssoftlab.com/pshl_info.phtml

Ruby - Ruby is a dynamic, reflective, general purpose object-oriented programming language. It's a scripting language very much like Perl. Ruby scripts end with either rb or rbw (windowless). In order to run ruby scripts, you need the Ruby VM. You can download it from http://www.ruby-lang.org. There are installers for all of the popular Operating Systems under downloads. Once you've installed ruby, add the fxruby gem by running "gem install fxruby" in a command/terminal prompt. Fxruby is what I use for GUIs.

Batch Files - A batch file is a Windows text file, with the extension .bat or .cmd, containing a series of commands intended to be executed by the command interpreter. When a batch file is run, the shell program (usually COMMAND.COM or cmd.exe) reads the file and executes its commands, normally line-by-line. A batch file is analogous to a shell script in Unix-like operating systems. When working in windows, it's very useful knowing how to batch script as it allows you to lots of automation.

Sunday, October 12, 2008

Creating Windows Icons

So I'm writing a Windows C# application (I'll talk about it later) and I wanted to use my own icon for it. I needed a .ico image, Visual Studio wouldn't let me use any other image file types. I usually use Adobe Photoshop for my editing, but that program doesn't support natively icons. I found a Photoshop plugin for icon files at this website and it worked perfectly. There was no hassle at all.

I created a 16x16 icon and added it to all my forms easily. I set the icon to be used as the default icon through the project properties (under the application submenu) and that worked perfectly too. I'm really happy with how it turned out.

This post was really just to give out the link to the website for the photoshop icon plugin. Oh yeah, I'm probably going to be posting a good deal of C# stuff for a while since I've been playing with it a lot recently. Just some fair warning...

Sunday, October 05, 2008

Virii and XP Royale

Yeesh, it's been a long time since I posted here. Like just about everyone, I'm pretty busy... but the "busy-ness" gives me some good things to write about. So I'll be posting a lot more regularly for a while.

The most recent "busy-ness" was a full reinstallation of one of my desktop computers. It wasn't something that I really planned to do... I ended up catching a virus while testing google adsense (some ringtone ad) with an Internet Explorer 5 browser. I wasn't being that careful on that machine, since I didn't really didn't expect to catch anything through adsense links. It was a serious virus too. Like most of them, it put a bunch of adware on the machine (which would pop up sporadically), but it also tried to add some fetching programs and some dll files. Nasty, nasty... anyways, I didn't entirely trust that the anitvirus on the machine was able to catch everything, so I decided to wipe the whole harddrive (I used Active@KillDisk) and reinstall. I put on Windows XP Professional SP3 instead of Vista and went through the process of configuring the machine to my needs. It kinda needed to be done anyway, the machine was getting cluttered and I didn't lose any data since it's not a machine I really use for anything important.

One nice thing I found while reinstalling was the Windows XP Royale Noir theme. I like its look better than Windows Vista. It's a really sleek, black XP theme that I read the developers never fully released. Someone got a hold of it and finished it up, then made it available. I liked it so much I switched my main desktop computer to the theme... even though I'm pretty sure it makes the machine run slower.

Download and check out the theme here: http://www.softpedia.com/get/Desktop-Enhancements/Themes/Royale-Noir-Theme.shtml

To use the theme, you will need patch your uxtheme dll by running this program: http://www.softpedia.com/get/System/OS-Enhancements/UXTheme-MultiPatcher.shtml

Tuesday, March 25, 2008

Binary Interfaces

Here's something I dug up from an old folder that may have some merit being posted. Heh... It's from when I first started working... I've learned a lot about programming since then, so don't judge me for my choices from back then, ok?

This is the result of research into creating a binary interface for one of our protocols that exclusively used XML strings. The goal was to pass binary data through XML. We wanted to use a standard if possible, if not, then something close to a standard. Today, I'd probably spend all my time arguing to not use XML for binary transfers. Since we were using Java, Java RMI would have been sufficient for what we were doing back then with very little hassle. If not, I bet it would be easier to find solutions.

But here's what I came up with back then:

Option one is to encode the binary data into XML. Simply create a tag and place the bytes in. The only problem is that XML has a limited character set, so the byte stream will need to be encoded into characters and then decoded after it’s received. There are different algorithms for encoding. If large data sets are anticipated to be transferred and the byte value distribution within the set to be skewed, it would be best to go with the Huffman encoding approach (which encodes based on byte frequency). If we anticipate the data set to be small or we want to make a quick application, it’s best to go with a Base-64 encoding scheme (which encodes based on a standard 3 byte to 4 character scheme). The better algorithm (Huffman encoding) gives about 1 character per 1 byte of data and the second best, but more popular, algorithm (Base 64) gives about 1.5 characters per one byte of data. This method is simple, though definitely not ideal due to the overhead of encoding and decoding as well as the not-quite 1 to 1 byte to character conversion. Also, the encoding is not a standard and so the receiver may not know how to decode the data.

Option two is a Multipart/Related MIME. The essential idea for this is to send a MIME multipart/related message over http that can contain multiple types of data, such as xml and an image. There is a SOAP standard that allows for this (which even allows for the xml to reference the data, which is used if the binary data is to be one of the parameters), though it does not seem to be an XML standard. However, at this point, there IS no XML standard (according to my research) and this seems to be as close to a standard as anything else. Multipart/Related MIME messages are currently used for e-mail attachments.

Option Three is DIME messaging, which is a new specification for handling binary data with SOAP messages (or other messages.) It doesn't seem to be widespread, but it seems to be getting there. It is quite similar to Multipart/Related MIME messages, but offers a few key benefits. Essentially, it sacrifices flexibility for simplicity; so it is faster and simple to create. Also, it allows to break up large data sets so that they can be sent in chunks. Furthermore, DIME will have standards that will apply to more than just http. Microsoft will be motivated to focus on DIME for binary attachments in its future with SOAP tools and platforms.

Those are the most feasible options found trying to keep the current XML messaging intact.

Sources:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnservice/html/service01152002.asp - DIME
http://www.w3.org/TR/SOAP-attachments - Multipart MIME
http://www.javaworld.com/javaworld/javatips/jw-javatip117.html?tip - Encoding

We ended up going with Option 1 using the Base 64 approach. For transferring mostly 70KB files around, it's performed decently.

Friday, February 29, 2008

TSOL Tomcat 5.5 Installation

1. TOMCAT 5.5.x Installation

I had this sitting around, I figure someone out there might have a use for it. And so I present, instructions for installing Tomcat 5.5.x on Trusted Solaris 8:

1. Download the Tomcat binary 5.5.x from http://jakarta.apache.org/tomcat/ and extract it into a local directory referred to from here on as $CATALINA_HOME.

2. In $CATALINA_HOME, copy the contents of $CATALINA_HOME/conf into $CATALINA_HOME/confOriginal. Then delete both directories $CATALINA_HOME/logs and $CATALINA_HOME/conf.

3. Make multi-level directories for $CATALINA_HOME/logs and $CATALINA_HOME/conf. Copy all files in $CATALINA_HOME/confOriginal into $CATALINA_HOME/conf under every label you intend to run a server under.

4. If using Tomcat 5.5.x and J2SDK 1.4 or 1.3: Download compat.jar and unzip it in ../$CATALINA_HOME. Verify that it has placed xercesImpl.jar and xml-apis.jar into $CATALINA_HOME/common/endorsed

5. Go into $CATALINA_HOME/bin and run startup.sh. Verify that the installation was successful by visiting http://localhost:8080 in your web browser. You should see the Tomcat startup page. Shutdown by running shutdown.sh in $CATALINA_HOME/bin.

6. The 5.5.x version of Tomcat does not come with the admin service. It can be useful, so install that as well following these steps:
1. Unzip jakarta-tomcat-5.5.x-admin.zip & copy conf\ and server\ folders and contents over your own Tomcat conf & server folders (into $CATALINA_HOME folder)
2. Copy $CATALINA_HOME\server\webapps\admin\admin.xml to $CATALINA_HOME\webapps\admin.xml
3. Add a line to $CATALINA_HOME\conf\tomcat-users.xml to allow a user to login to the manager & admin modules, e.g.:
4. Restart Tomcat
5. Visit http://localhost:8080/admin and login using admin/admin1

7. Verify the installation by visiting http://localhost:8080/admin.

Wednesday, January 09, 2008

Scanlation Notes

This post no longer exists.

Monday, December 31, 2007

Testimony 2007

Since it is the Christmas month, where the birth of Christ is celebrated, I thought it would be appropriate to take a break from talking about programming to post my testimony as a Christian. This is a testimony that I gave in church at what feels like a few months ago, although it may actually have been a year ago. It was written to be about five minutes long. Re-reading it, I realize I used a fair amount of Christian lingo, so it might not mean the same thing if you don't know much about Christianity.

Oh yeah, in case you didn't know (or guess by now), I'm a Christian. Denominationally... I'd say I'm non-denominational, but if you need to be told a denomination, I guess you could call me methodist. I basically believe in having a close, personal relationship with the loving God/Jesus/Holy Spirit through prayer, fellowship with other Christians, and studying the bible. I don't believe I have any business judging other people or forcing my beliefs on others. However, I won't hide what I believe either and I hope my beliefs show in my everday life because they are the most important part of who I am. So read it or ignore it, this is my one of many accounts about why I belive in God. It's not meant to convert anyone or anything like that, just to let you know a little bit more about who I am. So here goes:

My parents are both Christian and they brought me up as a Christian. We had bible studies every night, where my dad read to us from some family devotions book, swapping our names in for the characters in the life application stories. I prayed before going to bed every night and before every meal. My parents brought my sisters and I to church every Sunday, where I would attend the Sunday School I enjoyed so much, especially in fourth grade. That class was focused on filling out worksheets, which was actually a lot of fun to me. You see, we'd always show up late, and I enjoyed being the first done with the most correct answers despite always coming in late.

When I was seven years old, I prayed the important prayer of asking Jesus to come into my life. Probably a few months after that, I was baptised at the beach. Things are good when you give your life up to Jesus.

But are they really? Honestly, my life has been really blessed. Did God really have anything to do with it or was that just the way it was? Things go well and I feel at peace when I pray, but is it because God is answering my prayer or is it due to self reflection? To feed those doubts was the fact that so many of the arguments against Christianity, like evolutionary debates and religious social studies, resonate quite well with me. Furthermore, I had the feeling that being a Christian and the existence of a god is stifling and inconvenient. I thought life would certainly be more efficient and fun without God. For example, praying takes up time. Figuring out and acting on what I believe to be "God's will" seemed really limiting.

With that mentality, I decided that it not only made sense not to believe in God, but it was a good decision for my life. So no more praying, no more paying attention in church (of course I'd still have to go with my parents), no more trying to figure out God's will. But... that didn't go as well as I hoped.

Without my relationship with God, something was just missing. I knew it wasn't something as simple as "I feel purposeless" or the "feeling of loss." At church recently, we talked about the fingerprints of God... touches God leaves that shows He's there, even if you can't see Him. The touch of God was missing from my life that I didn't want Him to lead. For me, the most explainable missing fingerprint was a sense of knowing my place in everything, the sense of somewhat understanding everything around me.

Soon after, I prayed to God and He spoke to me. I was inspired to pursue a deeper relationship with God. To start off, I just prayed a lot more. I studied the bible closer and would have long discussions about it with other Christians. That led me to worship more geniunely and involve myself more in God's work. I didn't feel like I was making myself do something I didn't want as I tried to followed God's will more while developing a relationship with Him. God has blessed my life greatly, with my friends, and with my wife Carrie, and with my family, and with work. There's nothing so far in my, albeit short so far, life that I really regret and a great part of that is because I truly believe God has been guiding me and I've been listening. Things don't always work out in the short term, but I've almost never minded thanks to God, and something better has always come around so far.

My story isn't very dramatic. I've spent almost all my life as a Christian pursuing a relationship with God. Having a relationship with God gives me an almost perpetual sense of fulfillment, joy, and peace in my life. Of course, even though I believe a relationship with God makes it easier, I know I could get all of that without Him. But there's still that unexplainable touch of God that only comes from a relationship with Him. And that really means everything to me... I think it might have to be experienced to really understand.

And of course, I still have doubts, although it hasn't escalated to the situation I described earlier again. But it really very much is a cycle to me. Doubt, study, and prayer continually bring me in a closer relationship with God. The thing that helps me most though doubt is that I know God exists, since I have a relationship with Him, and I've seen Him transform lives.

Friday, November 30, 2007

TCP Monitor

At work, I've done a lot of client/server work. Whether using RMI JDBC, or SOAP Messages, or our own custom protocols, a lot of my work has involved sending messages back and forth. The easiest way for me to debug programs was with tcpmon. This incredibly useful tool sits between a client and server. It displays any data passedit recieves to the screen, then forwards the message to its destination. Go to the page to read more about it. I've really found it invaluble.

Heh heh... this post was actually just to keep up with an update a month. I'm glad I thought of something useful to post.

Sunday, October 21, 2007

SQL Server and JDBC

This describes how I set up JDBC to interact with a Sql Server Express 2005 database. Sql Server Express 2005 was installed on a Windows XP machine. The database was being accessed using JDBC on a remote Trusted Solaris machine running Java 1.2. The following instructions should apply to more configurations than just that though.


- Installing Sql Server Express 2005 -

1. Install Sql Server Express.

Download it from http://www.microsoft.com/sql/downloads/trial-software.mspx#EXC. During install, selecting all the default options is fine. The only thing you would want to change is using Mixed Mode authentication instead of Windows Authentication. If you didn't select that option, I'll tell you how to do it later, so don't worry about it.


2. Allow TCP/IP Connectivity

Configure TCP/IP connectivity for use with IBM Director Server. By default, SQL Server Express Edition does not have TCP/IP connectivity enabled after installation. This means you won't be able to remotely access the database. In addition, the TCP port must be set for use with IBM Director Server. Complete the following steps:

1. Click Start > All Programs > Microsoft SQL Server 2005 > Configuration Tools > SQL Server Configuration Manager.
2. In the left pane of the SQL Server Configuration Manager window, expand SQL Server 2005 Network Configuration and then click Protocols for SQLEXPRESS.
3. In the right pane of the SQL Server Configuration Manager window, right-click TCP/IP and click Enable. Click OK to acknowledge the warning message.
4. Right-click TCP/IP again and click Properties.
5. In the TCP/IP Properties window, click the IP Addresses tab, and then expand IPALL.
6. Under IPALL, clear the TCP Dynamic Ports field and then type a port number in the TCP Port field. Usually, the port number should be set to 1433.
7. In the TCP/IP Properties window, click OK, and then click OK to acknowledge the warning message.
8. In the left pane of the SQL Server Configuration Manager window, click SQL Server 2005 Services.
9. In the right pane of the SQL Server Configuration Manager window, right-click SQL Server (SQLEXPRESS) and click Restart.


3. Install SQL Server Management Studio

Download SQL Server Management Studio Express from https://www.microsoft.com/downloads/details.aspx?familyid=C243A5AE-4BD1-4E3D-94B8-5A0F62BF7796&displaylang=en. If you didn't allow Mixed mode Authentication for the Sql Server Express earlier, complete the following steps:

1. In SQL Server Management Studio Object Explorer, right-click the server, and then click Properties.
2. On the Security page, under Server authentication, select "SQL Server and Windows Authentication mode."
3. In the SQL Server Management Studio dialog box, click OK, to acknowledge the need to restart SQL Server.
4. In Object Explorer, right-click your server, and then click Restart. If SQL Server Agent is running, it must also be restarted.
5. In Object Explorer, expand Security, expand Logins, right-click sa, and then click Properties.
6. On the General page, you may have to create and confirm a password for the sa login.
7. On the Status page, in the Login section, click Enabled, and then click OK.


4. Create Your Database

- Create the database using SQL Server Management Studio. Add a user to the database by going into [database_name]/security/users.
- Give the user permissions for remote access.
a) Right Click [database_name], select properties.
b) Go into properties.
c) Select the user and grant all permissions.


- JDBC Access -

Microsoft provides a JDBC Driver for SQL Server 2005, which can be downloaded from http://msdn2.microsoft.com/en-us/data/aa937724.aspx. There's a "sqljdbc.jar" in the pack that you'll need to put on your classpath. Then you can use the following Driver and Url:

Driver: com.microsoft.sqlserver.jdbc.SQLServerDriver
Url: jdbc:sqlserver://[host]:[port, default 1433];"databaseName=[database_name]"

However, I needed to use Java 1.2 for my application and Microsoft's JDBC driver does not support it. It doesn't support 1.3 either. I found another great free driver at http://sourceforge.net/project/shownotes.php?release_id=369359&group_id=33291 which fit my needs: free, supports java 1.2, and easy to use. There's a jtds.jar in the pack that you'll need to put on your classpath. Then you can use the following Driver and Url.

Driver: net.sourceforge.jtds.jdbc.Driver
Url: jdbc:jtds:sqlserver://[host]:[port, default 1433]/[database_name]

I'm assuming you know how to use JDBC already, so this guide ends here.