Thursday, December 16, 2010

How to compile mame in Ubuntu 10.04 Lucid Lynx

UPDATE: A newer guide is available at Compiling Mame on Ubuntu 14.04 Trusty Tahr.

Using the following instructions you should be able to compile Mame on Ubuntu 10.04 Lucid Lynx, though it should also work in most any other version of Ubuntu too.
  1. Download the latest source from http://mamedev.org/release.html Grab the .zip version. The following command will grab the latest source as of the time I wrote this tutorial.
    wget http://mamedev.org/downloader.php?file=releases/mame0140s.zip --user-agent="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6" -O mame_source.zip
    Here we change the user/agent string, because the server is set to prevent robots, like wget, from programmatically downloading the file.
  2. Unzip the file, and then unzip that file. Place it anywhere you like, perhaps your home directory.
    mkdir mame
    unzip mame_source.zip
    unzip mame.zip -d mame
    rm mame.zip
  3. Next we install all the libraries and dependencies necessary to build mame.
    sudo aptitude install build-essential libgtk2.0-dev libgnome2-dev libsdl1.2-dev
  4. Finally, lets get to building it.
    cd mame
    make
  5. Let's setup a rom folder and get some roms.
    mkdir roms
    cd roms
    wget http://mamedev.org/roms/sidetrac/sidetrac.zip --user-agent="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6"
  6. Now that we have legally obtained an arcade ROM, let's play it!
    cd ..

Once you know what libraries to install, compiling Mame from source is quite easy in Ubuntu. Drop me a line in the comments if this is helpful, or if you run into any problems or have any suggestions.

Ubuntu Up and Running Book Review

Note: This is a review of the O'Reilly book Ubuntu: Up and Running.  I received this book through O'Reilly's Blogger Review Program.  When I received this book I was already in the middle of Machine of Death, and JavaScript: The Definitive Guide.  So it took me a little longer than I expected to finish this book, and finally, get around to writing this review.
This book touts itself as "A Power User's Desktop Guide."  This rather accurately defines the target audience of the book.  It is not for your average user, but for a technically inclined user who is interested in getting his or her hands dirty with Ubuntu.
This book is very thorough, going step-by-step through each process, often times in what can be grueling detail for someone if such detail is not necessary.  I committed myself to not skipping any parts, and that made some parts of this book very hard to read, as it went through each and every single little click to perform certain tasks.  However, this attention to detail and verbosity probably makes this a very good read for the person who needs to be taken all the way to the finish line to understand the concepts being taught in this book.  This level of detail also makes it a good read for those who may not consider themselves all that technical literate, but are willing to put in the time and effort needed to really learn Ubuntu.  The first chapter of the book introduces the reader to Ubuntu, Linux, and the whole FOSS movement.  It lets the reader know that Ubuntu is more than just an operating system, but it is the leader in a movement that has been fighting the good fight for over thirty years.  After finishing this chapter, the reader should have a good idea of common open source idioms, such as free as in beer versus free as in freedom, and be familiar with some of the leaders in the open source movement including RMS, Torvalds, and Shuttleworth.
With the history out of the way, the second chapter goes into installing Ubuntu.  Here the level of detail this book goes through is revealed.  Installing Ubuntu is a trivial task.  I've given disks to non-technical people, and they where able to get Ubuntu installed without any hand-holding.  However, this book doesn't just cover a vanilla install.  It covers dual-booting, an installing in VirtualBox, and installing through Wubi, as well as upgrading from a previous version.
For each type of install, this book goes step-by-step, screen-by-screen, explaining everything along the way.
I did feel that this book should have stressed the advantages of doing a real hard-disk install over a Wubi or virtual installation.  In my experience the other types of installations provide a significantly reduced experience over a real HD install.
I also felt like there where some areas covered that where really unnecessary, and could give the impression that Ubuntu may be harder to get up and running than it really does.  This includes going over the different Function Key Options.  I feel that for the sake of brevity, and being relevant, these areas could be skipped.  A person who is likely to use these areas of the installer is unlikely to need this book anyhow.
The book then moves on to configuring your installation, and then covers areas of use such as chapters devoted to multimedia, OpenOffice, etc.  I did feel that the order of chapters could have been laid out better.
After getting the user to install Ubuntu, it probably would have been best to have an introduction to the desktop, going over the basic features of the Gnome interface, including the Menu Bar, the different widgets in the top-right, and the default folders in the home folder.  This would make the user comfortable with the basics of Gnome, and would encourage the user to discover the other features, such as web browsing, multimedia, etc. on their own.
I believe Chapter 7, which covers the command line, should have been moved to the back.  It makes more sense, I believe, to cover all the tools that have been developed so that Linux users no longer have to master the command line to perform your usual tasks.  This could help dispel the fear that many users come in with, that if they don't master the command line, then Linux is not for them.
While the true power-user, be they a Linux user, or otherwise, will spend a good deal of time on the command-line, I think it is important to stress to new users that the most important tasks can be performed through point-and-click.
It also would have been helpful if this book had waited for Ubuntu 10.04 to come out, and spent more time focusing on the features of 10.04, since it is an LTS release, and would make this book more relevant over the next three years that this version of Ubuntu will be supported.  10.04 also alleviates some of the problems this book mentions such as the immaturity of the Empathy IM client in Ubuntu 9.10, making the suggestion of trying Pidgin somewhat more mute.  It could have also covered in better the Ubuntu One music store, if it had spent more time focusing on Ubuntu 10.04.  While this book says that it covers Ubuntu 9.10 and 10.04, it really covers 9.10, with some mentions of 10.04 enhancements.
With all that said, I would stay give this a book a "would recommend" as it is very detailed, and can go a long way in taking the interested reader from being interested in Ubuntu, to reasonably well versed in Ubuntu.
The chapter covering the command line is thorough and clear.  In fact, I learned a few things that I didn't about using some CLI tools installed in Ubuntu.
I look forward to future versions of this book covering newer versions of Ubuntu, and hopefully, through the Blogger Review program, is touched up in some of its rough edges.
I already have some friends and acquaintances lined up to borrow this book, as they have an interest in getting their feet a little more wet with Ubuntu.

Monday, November 1, 2010

Introducing Git-Notifier

This evening I finished the preliminary base code for a project I have been working on called Git-Notifier.  Inspired by Gitifer for OSX, Git-Notifier provides pop-up notifications for updates of a remote git repository using libnotify in Ubuntu.
As it stands, the program is currently a bash script, and configuration is a manual process.
To get started, download the come from http://github.com/maxolasersquad/Git-Notifier/archives/v0.1 in either tar.gz or .zip format.  Extract the .gitnotifier folder to your home directory.  Extract the gitnotify.sh file to wherever you would like to execute it from.  I keep this file in my home directory as well.
Next cd into the ~/.gitnotify directory and clone the remote repository using the -n switch.  For example, to get notified of updates to the Git-Notifier application, do the following
cd ~/.gitnotify
git clone -n git://github.com/maxolasersquad/Git-Notifier.git
Finally, edit the file ~/.gitnotify/gitnotify.ini file.  To the right of repos=, list out each git project you have cloned.  Using the same example from above, the line would look like this.
repos=Git-Notifier
If you have cloned any more repositories, simply list them all in the repos line, separated by a space.
Finally you can change how often the program looks for updates, and the pretty style to display the results. To see the different options for the pretty styel, run man git show.
Once you have cloned the directories you want, with the -n switch, and you have configured the ini file appropriately, simply run the gitnotify.sh script.  As commits are made to the remote repository, you should get notifications on your deskop.
The code could probably use some love, and a few more features would be nice as well.  Once I am happy with how this script works, I'd like to port it over to python.  I'd also like to have better support for non-Ubuntu flavors of Linux, though I'm not sure what other notifications are out there that can be easily programmed from Bash.
Any bugs, fixes, improvements, etc. would be appreciated on the github site.

Wednesday, October 20, 2010

Migrating from subversion to git

At work we have been migrating from subversion to git.  The projects that we still have in subversion I check out using git-svn.  This allows me to use much of the git happiness while we make the transition.  It also performs another very nice function; it allows us to migrate our repositories to git keeping our full version control history.
What follows demonstrates, from a Linux machine, to migrate a repository from http://svn.foo.com/bar to ssh://git.foo.com/repos/bar.git.  This assumes you already have git, subversion, and git subversion installed.  It also assumes that you have read access to the subversion repository, and a git repository you have direct access to.

git svn clone http://svn.foo.com/bar/trunk bar

Next we need to log into the machine hosting our git repository.  We do this over ssh.

ssh foo.com
cd ~/repos
mkdir bar.git
cd bar.git
git init --bare

Back on our machine.

cd bar
git remote add origin ssh://git.foo.com/repos/bar.git
git push origin master

You should now have a central git repository at ssh://git.foo.com/repos/bar.git  You can create your branches, and do whatever git-style version controlling that you wish.  The final step, of course, is to remove the old subversion repository so that others do not accidentally make changes that never find themselves in the real copy of the code.
One limitation is that this process does not import any branches or tags from the subversion repository.  I'm not sure if there is a clean way to do this.

Thursday, October 14, 2010

Shuffling cards in PHP

The following demonstrates what I believe to be an efficient means of shuffling a deck of cards, using PHP.




$cards = array(array('suit'=>'Spade', 'rank'=>'01'), array('suit'=>'Spade', 'rank'=>'02'), array('suit'=>'Spade', 'rank'=>'03'), array('suit'=>'Spade', 'rank'=>'04'), array('suit'=>'Spade', 'rank'=>'05'), array('suit'=>'Spade', 'rank'=>'06'), array('suit'=>'Spade', 'rank'=>'07'), array('suit'=>'Spade', 'rank'=>'08'), array('suit'=>'Spade', 'rank'=>'09'), array('suit'=>'Spade', 'rank'=>'10'), array('suit'=>'Spade', 'rank'=>'11'), array('suit'=>'Spade', 'rank'=>'12'), array('suit'=>'Spade', 'rank'=>'13'), array('suit'=>'Spade', 'rank'=>'14'),
               array('suit'=>'Club', 'rank'=>'01'), array('suit'=>'Club', 'rank'=>'02'), array('suit'=>'Club', 'rank'=>'03'), array('suit'=>'Club', 'rank'=>'04'), array('suit'=>'Club', 'rank'=>'05'), array('suit'=>'Club', 'rank'=>'06'), array('suit'=>'Club', 'rank'=>'07'), array('suit'=>'Club', 'rank'=>'08'), array('suit'=>'Club', 'rank'=>'09'), array('suit'=>'Club', 'rank'=>'10'), array('suit'=>'Club', 'rank'=>'11'), array('suit'=>'Club', 'rank'=>'12'), array('suit'=>'Club', 'rank'=>'13'), array('suit'=>'Club', 'rank'=>'14'),
               array('suit'=>'Diamond', 'rank'=>'01'), array('suit'=>'Diamond', 'rank'=>'02'), array('suit'=>'Diamond', 'rank'=>'03'), array('suit'=>'Diamond', 'rank'=>'04'), array('suit'=>'Diamond', 'rank'=>'05'), array('suit'=>'Diamond', 'rank'=>'06'), array('suit'=>'Diamond', 'rank'=>'07'), array('suit'=>'Diamond', 'rank'=>'08'), array('suit'=>'Diamond', 'rank'=>'09'), array('suit'=>'Diamond', 'rank'=>'10'), array('suit'=>'Diamond', 'rank'=>'11'), array('suit'=>'Diamond', 'rank'=>'12'), array('suit'=>'Diamond', 'rank'=>'13'), array('suit'=>'Diamond', 'rank'=>'14'),
               array('suit'=>'Heart', 'rank'=>'01'), array('suit'=>'Heart', 'rank'=>'02'), array('suit'=>'Heart', 'rank'=>'03'), array('suit'=>'Heart', 'rank'=>'04'), array('suit'=>'Heart', 'rank'=>'05'), array('suit'=>'Heart', 'rank'=>'06'), array('suit'=>'Heart', 'rank'=>'07'), array('suit'=>'Heart', 'rank'=>'08'), array('suit'=>'Heart', 'rank'=>'09'), array('suit'=>'Heart', 'rank'=>'10'), array('suit'=>'Heart', 'rank'=>'11'), array('suit'=>'Heart', 'rank'=>'12'), array('suit'=>'Heart', 'rank'=>'13'), array('suit'=>'Heart', 'rank'=>'14'));
$shuffled_cards = array();
while (sizeof($cards) != 0)
{
  mt_srand((double)microtime()*1000000);
  $card = mt_rand(0, sizeof($cards) - 1);
  $shuffled_cards[] = $cards[$card];
  unset($cards[$card]);
  $cards = array_values($cards);
}
print_r($shuffled_cards);


We start by initializing our deck of fifty-two cards.  Each member of our array is an associative array holding the suit and rank of each card.  We then initialize a blank array where we will store our shuffled cards.
The while loop will execute until all of the cards have been popped out of our initial array.
At the beginning of each iteration we start a new pseudo-random seed.  Then, we get a random number between zero, and the number of cards we have left to shuffle, minus one (because arrays are zero indexed.)  Next, we append the randomly selected card to our shuffled array and remove it from our cards array.
When we remove a card from our numerically indexed array, the array becomes sparse.  So if the random number was four, then we are left with indexes one, two, three, five, six and so on.  If we hit four through another iteration, there would be no value to add to our shuffled array.
To make our array dense again, we can simply call the array_values function on our cards array and assign it back to cards.
This solution buys us a few niceties, compared to other solutions I have come across.  First, at any given time a card is only in one array (except the split moment that we are doing the move).  The second is that we don't have to worry about our random number being the same as a previously picked random number, and thus waste time picking another number until we hit a unique number.
Finally, this solution is really just an exercise in efficiently shuffling an array.  In a real application I would not use this solution at all.  PHP comes with a nice little function named, easy enough to remember, shuffle().  So let's take a look at how one should really implement the solution.


$cards = array(array('suit'=>'Spade', 'rank'=>'01'), array('suit'=>'Spade', 'rank'=>'02'), array('suit'=>'Spade', 'rank'=>'03'), array('suit'=>'Spade', 'rank'=>'04'), array('suit'=>'Spade', 'rank'=>'05'), array('suit'=>'Spade', 'rank'=>'06'), array('suit'=>'Spade', 'rank'=>'07'), array('suit'=>'Spade', 'rank'=>'08'), array('suit'=>'Spade', 'rank'=>'09'), array('suit'=>'Spade', 'rank'=>'10'), array('suit'=>'Spade', 'rank'=>'11'), array('suit'=>'Spade', 'rank'=>'12'), array('suit'=>'Spade', 'rank'=>'13'), array('suit'=>'Spade', 'rank'=>'14'),
               array('suit'=>'Club', 'rank'=>'01'), array('suit'=>'Club', 'rank'=>'02'), array('suit'=>'Club', 'rank'=>'03'), array('suit'=>'Club', 'rank'=>'04'), array('suit'=>'Club', 'rank'=>'05'), array('suit'=>'Club', 'rank'=>'06'), array('suit'=>'Club', 'rank'=>'07'), array('suit'=>'Club', 'rank'=>'08'), array('suit'=>'Club', 'rank'=>'09'), array('suit'=>'Club', 'rank'=>'10'), array('suit'=>'Club', 'rank'=>'11'), array('suit'=>'Club', 'rank'=>'12'), array('suit'=>'Club', 'rank'=>'13'), array('suit'=>'Club', 'rank'=>'14'),
               array('suit'=>'Diamond', 'rank'=>'01'), array('suit'=>'Diamond', 'rank'=>'02'), array('suit'=>'Diamond', 'rank'=>'03'), array('suit'=>'Diamond', 'rank'=>'04'), array('suit'=>'Diamond', 'rank'=>'05'), array('suit'=>'Diamond', 'rank'=>'06'), array('suit'=>'Diamond', 'rank'=>'07'), array('suit'=>'Diamond', 'rank'=>'08'), array('suit'=>'Diamond', 'rank'=>'09'), array('suit'=>'Diamond', 'rank'=>'10'), array('suit'=>'Diamond', 'rank'=>'11'), array('suit'=>'Diamond', 'rank'=>'12'), array('suit'=>'Diamond', 'rank'=>'13'), array('suit'=>'Diamond', 'rank'=>'14'),
               array('suit'=>'Heart', 'rank'=>'01'), array('suit'=>'Heart', 'rank'=>'02'), array('suit'=>'Heart', 'rank'=>'03'), array('suit'=>'Heart', 'rank'=>'04'), array('suit'=>'Heart', 'rank'=>'05'), array('suit'=>'Heart', 'rank'=>'06'), array('suit'=>'Heart', 'rank'=>'07'), array('suit'=>'Heart', 'rank'=>'08'), array('suit'=>'Heart', 'rank'=>'09'), array('suit'=>'Heart', 'rank'=>'10'), array('suit'=>'Heart', 'rank'=>'11'), array('suit'=>'Heart', 'rank'=>'12'), array('suit'=>'Heart', 'rank'=>'13'), array('suit'=>'Heart', 'rank'=>'14'));
shuffle($cards);
print_r($cards);

Thursday, October 7, 2010

Sucrose Progression


This was made using the Cat and Girl Maker  The artwork is all Dorothy Gambrell, the dialog is mine.  The style is meant to closely resemble Dorothy's style for her Cat and Girl comics.

Sunday, October 3, 2010

Updating JOSM in Ubuntu

If you are a contributor to Open Street Map, and you run Ubuntu, then you probably use JOSM from time-to-time.  It has a lot of great tools that are not available in the online editor.  My favorite is the Orthogonalize Shape tool.
Anyhow, I got tired of being told to update each time I started the application.  Plus, I like being up to date.  Since Ubuntu only gets an updated version of JOSM with each new release of Ubuntu, I decided to find out how to cleanly update JOSM in a way that integrates well with the currently installed JOSM.  Here's what I did.

  1. Go to http://josm.openstreetmap.de/
  2. Under the Download section, click on the "tested JOSM" link and save it.
  3. Under a terminal run the following.
    sudo cp ~/Downloads/josm-tested.jar /usr/share/josm/
    cd /usr/share/josm/
    sudo ln -f -s josm-tested.jar josm.jar

  4. Change ~/Downloads/josm-test.jar to the location in which you saved the josm-tested.jar file.
Now when you run JOSM the way you always have, it will load the latest stable version of JOSM.
When you first start JOSM you should go ahead and update your plugins.
This was testing in Ubuntu Lucid 10.04, but should work with any version of Ubuntu, and with any version of JOSM.  If it doesn't for any reason, respond in the comments below.

Friday, September 3, 2010

Listing invalid indexes in Oracle

I have often seen it asked online many times how to get a list of invalid indexes. The information was mostly incomplete, and often contained misinformation. Determined to solve this problem once and for all I wrote a query to get a list of invalid indexes on our Oracle 10g database.

Before we get on to the query, I'd like to note what makes finding invalid indexes so difficult, and how to overcome these obstacles.

Finding invalid global indexes is rather easy. Global indexes are your standard indexes you use and love every day. They are simply an index on a column, or columns in a standard non-partitioned table. Oracle provides a nice little view called all_indexes, which has a column named status. This view will return a list of all indexes that the user running the query has permissions to view. There is also the dba_indexes view and the user_indexes view. The status column can be one of three values, VALID, INVALID, or N/A. Global indexes will never have a status of N/A.

We run into trouble if we try to query WHERE status != 'VALID' if our database has any partitioned tables with partitioned indexes and potentially bring back valid indexes. Partitioned tables, and their indexes, save the lives of millions of babies every year, so we must give them our full respect, and do the necessary extra work that is required to account for the quirks they sometimes bring to our code.

Partitioned indexes will always have a status of N/A in the all_indexes view. Each of these indexes will have a partitioned index, which we can see by querying the all_ind_partitions view. Again, we also have the dba_ind_partitions and user_ind_partitions views available to us as well. This table contains all partitioned indexes, along with a status column. This status column works a little bit differently. Its possible values are USABLE, UNUSABLE, and N/A.

If our index'es partitioned indexes are marked as usable then we know our index is valid. However, we could again run into a case where our status column is marked as N/A. Partitioned indexes can go a level deeper and have their own subpartitioned indexes. Determined, we pick up our shovel and keep digging deeper.

Oracle provides us with a third view named all_ind_subpartitions. Like the above views, we also have user_ind_subpartitions and dba_ind_subpartitions. Again, we have a status column. This time, though, we can only have two possible values, USABLE, and UNUSABLE. At this point, we can determine if our index is valid or not by looking at its partition's subpartition's status.


Okay, so this is all well and good, but what we really need is a query that will give us the name of indexes that need rebuilding. Global indexes do not have partitions, and not all partitioned indexes have subpartitions, yet we want to know all invalid indexes, regardless of their makeup. The following query accomplishes this.

select
 index_name
from
  all_indexes
where
  owner not in ('SYS', 'SYSTEM')
  and
  status != 'VALID'
  and (
    status != 'N/A'
    or
    index_name in (
      select
        index_name
      from
        all_ind_partitions
      where
        status != 'USABLE'
        and (
          status != 'N/A'
          or
          index_name in (
            select
              index_name
            from
              all_ind_subpartitions
            where
              status != 'USABLE'
          )
        )
    )
);

I'll leave dissecting how this query works as a lesson to the reader. Here I am also leaving out the indexes owned by SYS and SYSTEM, but you can remove that line if you need those indexes listed as well.


I do presently have an anonymous block that makes a cursor out of this query, loops through it, and builds all indexes, partitioned indexes, and subpartitioned indexes, as is appropriate. I am still working on making sure that it is battle hardened. After that I'll post about how I accomplish this. Combined they provide an elegant solution to finding all of your invalid indexes and rebuild them.

If you find this helpful, or have some additional information to provide, please do so in the comment section below.



Edit: The script is available at Rebuilding invalid indexes in Oracle

Thursday, August 5, 2010

Dynamic column ordering and paging in PL/SQL

Update 09/30/2010: There was a problem with how I was doing the dynamic ordering.  I have resolved this and updated the post.

Update 10/08/2013: Oracle 12c finally has support for sane paging without jumping through all of these hoops. Until I get a chance to update this post you can read more at http://www.oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1.php

The following demonstrates how a PL/SQL function or procedure can take in parameters, and dynamically sort based on those parameters.
Let's start with an example.  This example accepts two parameters, a VARCHAR2 that will be the column we want to sort on, and a REF CURSOR.  The REF CURSOR is an out variables that the calling program can use to iterate through to get the result set.

TYPE cursortype IS REF CURSOR;
PROCEDURE sort_example
(
  sort_column VARCHAR2,
  result_set OUT cursortype
) AS

BEGIN

  OPEN result_set FOR
    SELECT column_one,
      column_two
      FROM example_table
      ORDER BY DECODE
      (
        UPPER(sort_column),
        'COLUMN_ONE',
        column_one,
        'COLUMN_TWO',
        column_two,
        column_one
      );

EXCEPTION
  WHEN OTHERS THEN
    NULL;//Real error handling should go in this exception block.


END;

The DECODE statement examines the sort_column passed in and matches it to an actual column in the table(s) that we are selecting from.  If the variable passed in is not in our list of columns then it will default, in this example, to column_one.
Using this technique we have to define, up front, all of the columns that we are going to allow the calling procedure to order by.  If we want to allow ordering by any column, we must go through and list them out one-by-one.
It is also a good idea to add some extra validation, beyond the DECODE clause, to make sure that sort_column is an actual column in the table.  In our real packages that do this, we have an out variable named message.  If an invalid column name is passed in we notify the calling program by sticking a message to that effect in our message variable.
To make our procedure even more robust, it is nice to have the ability to decide if the column should be sorted in ascending or descending order.
This gets a little tricker, and to accomplish we need to understand how ROWNUM works in PL/SQL.  When a query is executed, Oracle assigns each returned result a pseudo-column nmaed ROWNUM, with the first returned row having a ROWNUM of 1, the second 2, etc.
Through a subquery, and ROWNUM, we can dynamically sort in either ascending or descending order.

TYPE cursortype IS REF CURSOR;
PROCEDURE sort_example
  (
    sort_column VARCHAR2,
    sort_direction VARCHAR2,
    result_set OUT cursortype
  ) AS


BEGIN

  OPEN result_set FOR
  SELECT *
    FROM
    (
      SELECT column_one,
        column_two
        FROM example_table


        ORDER BY DECODE
        (
          UPPER(sort_column),
          'COLUMN_ONE',
          column_one,
          'COLUMN_TWO',
          column_two,
          column_one
        )
    )
    ORDER BY
      CASE
        WHEN UPPER(sort_direction) = 'DESC' THEN
          ROWNUM * -1
        ELSE
          ROWNUM
      END;

EXCEPTION
  WHEN OTHERS THEN
    NULL;//Real error handling should go in this exception block.

END;

Here we have our original query, initially sorting on the passed in column, that is subqueried and then sorted again, either on the rownum, or the opposite of rownum.  If the calling program passed in 'DESC' for the sort_direction, then we sort descending, if anything else is passed in we sort ascending.

We can take this maddening query a step further, utilizing rownum again, to handle paging.  We use this in our applications to push paging out of our interface code, and onto the database.  It is much quicker for our database to only return ten or so results we want to show on each page, then to return the whole set of data, and have our PHP application parse through the data.
So here is our final procedure, with dynamic sorting and paging.

TYPE cursortype IS REF CURSOR;
PROCEDURE sort_example
  (
    sort_column VARCHAR2,
    sort_direction VARCHAR2,
    start_row NUMBER,
    end_row NUMBER,
    result_set OUT cursortype
  ) AS

BEGIN

  IF start_row < 1 THEN
    start_row = 1
  END IF;
  IF start_row > end_row THEN
    end_row = start_row;
  ELSIF end_row IS NULL THEN
    end_row = start_row + 10;
  END IF;


  OPEN result_set FOR
  SELECT *
    FROM
    (
      SELECT ROWNUM rnum,
        column_one,
        column_two
        FROM
        (
          SELECT *
            FROM
            (
              SELECT column_one,
                column_two
                FROM example_table


                ORDER BY DECODE
                (
                  UPPER(sort_column),
                  'COLUMN_ONE',
                  column_one,
                  'COLUMN_TWO',
                  column_two,
                  column_one
                )
            )
            ORDER BY
              CASE
                WHEN UPPER(sort_direction) = 'DESC' THEN
                  ROWNUM * -1
                ELSE
                  ROWNUM
              END
        )
    )
    WHERE rnum >= start_result
      AND rnum <= end_result
    ORDER BY rnum;

EXCEPTION
  WHEN OTHERS THEN
    NULL;//Real error handling should go in this exception block.

END;

Yes, this one is a beast.  Starting at the child-most query, we have our SELECT statement, ordering the data by the column passed in.  In the parent query we then use ROWNUM, and the sort_direction argument to sort either ascending or descending.  We subquery these results, capturing the ROWNUM into an aliased column named rnum, so that in it's parent query we can refer to it's ROWNUM, and not some other ROWNUM from any other part of the query.  In the parent-most query we use our new rnum column to get only the data from the rows asked for.
Before you do any querying, we check the validity of the start_result and end_result arguments to make sure they actually make sense.  If they don't then we do some manual patching.  You may want to handle this differently, such as throwing an exception.  It is really up to you.  We have a DEFAULT_PAGING environmental variable we store in our database.  When end_result is NULL, then we set it to start_result plus our DEFAULT_PAGING variable.
You may look at this and think it is complicated, and probably performs very poorly.  In our testing, as long as your inner-most query has the proper indexes and constraints, the performance is actually pretty good.  In our environment, it is definitely much faster than having our PHP parse through all the results, sort and order the whole thing, and then cut out only the rows we actually want, especially when we have result sets that are well over 100,000 rows long.
There is one final step we take, so that our interface application knows how many results there are in the whole query, so that it knows how many pages it can expect there to be.  This example won't show it, but we actually check for a cached rowcount first, and we have all sorts of logic to try to guarantee integrity of those results, but I'm not going to get into all that here.







TYPE cursortype IS REF CURSOR;
PROCEDURE sort_example
  (
    sort_column VARCHAR2,
    sort_direction VARCHAR2,
    start_row NUMBER,
    end_row NUMBER,
    result_set OUT cursortype,
    rowcount OUT NUMBER
  ) AS

BEGIN

  IF start_row < 1 THEN
    start_row = 1
  END IF;
  IF start_row > end_row THEN
    end_row = start_row;
  ELSIF end_row IS NULL THEN
    end_row = start_row + 10;
  END IF;

  OPEN result_set FOR
  SELECT *
    FROM
    (
      SELECT ROWNUM rnum,
        column_one,
        column_two
        FROM
        (
          SELECT *
            FROM
            (
              SELECT column_one,
                column_two
                FROM example_table


                ORDER BY DECODE
                (
                  UPPER(sort_column),
                  'COLUMN_ONE',
                  column_one,
                  'COLUMN_TWO',
                  column_two,
                  column_one
                )
            )
            ORDER BY
              CASE
                WHEN UPPER(sort_direction) = 'DESC' THEN
                  ROWNUM * -1
                ELSE
                  ROWNUM
              END
        )
    )
    WHERE rnum >= start_result
      AND rnum <= end_result
    ORDER BY rnum;

  SELECT COUNT(*)
    INTO rowcount
    FROM example_table;

EXCEPTION
  WHEN OTHERS THEN
    NULL;//Real error handling should go in this exception block.

END;

With any luck this will help someone out.  With better luck, someone will drop by and offer some tips on how to do this even better.