Updated 2009-07-16 17:02:47 by LVwikignoming

#!/bin/sh
 # execute in wish from path \
 exec wish "$0" ${1+"$@"}

 # Alias.tcl - Takes an "alias" from an input box and runs the alias'
 #                        corresponding command
 #
 # Author: Luciano Espirito Santo
 #
 # More on the purpose of this program:
 # The very first time I used Linux, I really liked the concept of aliases: the
 # ability to create short or easier to remember alternative names to commands
 # or even sequences of commands.
 # In Windows, I found a very convenient way to do this with PowerPro
 # ( http://ppro.org/ ). It provides an input box and a scripting back end. So I
 # implemented this application with it and got terribly used to it.
 # A few years later, using Linux a lot more often, I missed my aliases and
 # decided to reimplement the whole thing, this time with Tcl.
 # Of course, Linux/Unix already provides its own alias capability. The
 # advantages of my application over Bash's aliases, for example, are few and
 # small. Basically, it's just another approach to it.
 # In Windows, the advantages are obvious: there is no alias capability in
 # Windows that I ever heard of, unless Bash (again) under Cygwin and a couple
 # of other applications that are very similar to this one, but do not run on
 # Linux and therefore are totally inadequate to people who dual-boot often and
 # otherwise would be forced to keep two isolated alias lists. Keep this
 # application and the alias list in a partition accessible to both (or all)
 # your operating systems and you should find no trouble in keeping the alias
 # list always updated, regardless of which OS you're running at the time.
 #
 # History
 #
 #      Version 1.0     2005-03-02      Luciano Espirito Santo
 #      First beta.
 #
 #      Version 1.1     2005-03-02      Luciano Espirito Santo
 #      - Added some voodoo magic to cope with kfmclient because it won't do
 #           the right thing with full paths or single program names that
 #           otherwise could be found easily in $PATH.
 #
 #      Version 1.2     2005-03-09      Luciano Espirito Santo
 #      - Fixed conflict between kstart and kfmclient. Now they are (hopefully)
 #          never run together.
 #      - Fixed bug revealed by empty/blank lines in the alias file.
 #      - KDE's own Run box would pop up right after attempts to run an invalid
 #          alias. Fixed that too.
 #
 #      KNOWN ISSUES:
 #      - For the time being, this application requires KDE if used in
 #          Linux/Unix. We could get by without it, but the commands would become
 #          longer, more awkward and prone to errors.
 #      - Many commands are likely not to work due to all very well-known
 #           issues related with Tcl's exec command.
 #
 #      This program offers no guarantees! Use it at your own risk!
 #
 #      TO DO:
 #      - Anybody want to write another "voodoo" proc to work around exec's problems?
 #      - Lots of improvements planned. Stay tuned!
 #
 #      LICENSE: BSD
 #
 # How to use it:
 #
 #         - Save this file in any directory with read and write permission.
 #
 #         - Create another file called Alias_list.txt in that same directory. Copy
 #           the sample provided right after the script. That is where you will
 #           maintain your alias list.
 #
 #         - The following rules apply to the alias list:
 #           Each alias-separator-command group must be in its own single line.
 #           Format:
 #           alias        =        command [optional arguments]
 #           In other words:
 #           + the alias (any character except the "equals" sign), followed by:
 #           + any number of "blanks" (space or tabs), followed by:
 #           + an "equals" sign (=), followed by:
 #           + any number of "blanks" (space or tabs), followed by:
 #           + any command, with options and/or arguments.
 #
 #           - Some commands may cause an error. Please see the "KNOWN ISSUES" above.
 #
 #           - The first "equals" sign works as a delimiter, so that symbol cannot be
 #           used in any alias. It can be part of any command, though.
 #
 #           - You may comment out any line you want. Lines starting with the
 #           "hash" symbol (#), with or without "blanks" (space or tabs), before or
 #           after the "hash" symbol, are ignored.
 #
 #           - If you use Linux/Unix and KDE, open KDE's Control Center and, in the
 #           HotKeys section, assign a shortcut key to this command:
 #           '/path/to/tcl/wish  /path/to/Alias.tcl'
 #
 #           - If you use another window manager, I suggest you use a nice program
 #           called "xbindkeys". It works in any window manager. Just assign a
 #           shortcut key to this command: '/path/to/tcl/wish  /path/to/Alias.tcl'
 #           Note, however, that you still must have KDE installed.
 #
 #           - If you use Windows, create a new shortcut to this application (in
 #           your Start menu or any other place), right-click it, select
 #           "Properties" and assign a keyboard shortcut to it.
 #
 #           - When (and if) the input box appears, type in an alias and press the
 #           Enter/Return key. If you want to cancel the action and close the box
 #           without running anything, press the Esc key.
 #
 #           - Whenever you want to edit the alias list, launch the program and
 #           press the F5 key.
 #
 #           - If you know Tcl, you may want to change the value of a few variables
 #           in the next section, "SET VARS".

 # ================================================
 # SET VARS
 # myAliasFile: Alias list file location
 # myAliasBg: input box's (entry widget) background color
 # myAliasFg: input box's (entry widget) foreground font color
 # myAliasFont: input box's (entry widget) font face
 # myAliasWidth: input box's (entry widget) width (measured in characters)
 # myAliasAlign: input box's (entry widget) text alignment
 # mySoundEnabled: Boolean: whether to play a sound in case of error
 # myErrorSound: sound file to play in case of error
 # myHasTitle: set to "yes" if you want the box to have a title/caption
 # myWindowTitle: the title/caption of the box
 # ================================================

 if          { $::tcl_platform(platform)  ==  "windows" }          {
         set         myAliasFile                "[ file normalize [pwd]/Alias_list.txt ]"
         set         myAliasBg                "#ffffff"                ;# or "white"
         set         myAliasFg                "#000000"                ;# or "black"
         set         myAliasFont                "Arial 12"                   ;#or "{Times New Roman}12"
         set         myAliasWidth                40
         set         myAliasAlign                center
         set        mySoundEnabled                "yes"
         set        myErrorSound                "D:/System/Sound/uh-oh.wav"
 }

 if          { $::tcl_platform(platform)  ==  "unix" }          {
         set         myAliasFile                "/am/d/home/xxxx/Alias_list.txt"
         set         myAliasBg                "#ffffff"                ;# or "white"
         set         myAliasFg                "#000000"                ;# or "black"
         set         myAliasFont                "Helvetica 18"
         set         myAliasWidth                40
         set         myAliasAlign                center
         set        mySoundEnabled                "no"
         set        myErrorSound                "/am/d/System/Sound/uh-oh.wav"
 }

 set        myHasTitle        "yes"
 set        myWindowTitle        "A rose, by any other name..."

 # this is an ugly hack, will fix it someday. DO NOT CHANGE IT.
 set  myNotFound  0

 # ================================================
 # PROCS
 # packages: sound (optional)
 # p.1.playwav: plays a sound file
 # p.2.error: plays error sound and clears input box
 # p.3.center_window: place a window at the center of the screen
 # p.4.check_alias_file: checks existence of alias list file
 # p.5.get_alias: reads input box, parses alias list and returns final command
 # p.6.which: searches a program in $PATH
 # p.7.voodoo: voodoo magic to workaround kfmclient's idiosyncrasy
 # p.8.run_alias: the final command that runs the alias's corresponding command
 # p.9.edit_alias: open alias list file in text editor so it can be edited
 # ================================================

 # ----------------------------------------------------------------
 # proc 1 of 9
 # plays a sound file
 proc         p.1.playwav  { argWavFile }          {

         if          { $::mySoundEnabled  !=  "yes" }          { return }

                 package require sound
                 snack::sound  s  -file  $argWavFile;  s  play  -block  1
 }

 # ----------------------------------------------------------------
 # proc 2 of 9
 # plays error sound and clears input box
 proc  p.2.error  {}        {

         p.1.playwav  $::myErrorSound
         $::w.frame0.aliasbox  delete  0  end
 }

 # ----------------------------------------------------------------
 # proc 3 of 9
 # place a window at the center of the screen
 # argW = window to be placed at the center of the screen
 proc  p.3.center_window  { argW }          {
         wm  withdraw  $argW
         update  idletasks
         set  x  [ expr [ winfo screenwidth $argW ] /2 - [ winfo reqwidth $argW ] /2 - [ winfo vrootx [ winfo parent $argW ] ] ]
         set  y  [ expr [ winfo screenheight $argW ] /2 - [ winfo reqheight $argW ] /2 - [ winfo vrooty [ winfo parent $argW ] ] ]
         wm  geom  $argW +$x+$y
         wm  deiconify  $argW
 }

 # ----------------------------------------------------------------
 # proc 4 of 9
 # checks existence of alias list file
 # complains loudly if file is not found
 proc  p.4.check_alias_file  {}                 {

         if          { ! [ file exists  $::myAliasFile ] }          {
                 p.2.error
                 $::w.frame0.aliasbox  insert  end  "ALIAS LIST FILE NOT FOUND"
                 $::w.frame0.aliasbox selection range 0 end
                 set  ::myNotFound  1
                 return  "0"

         }  else  { return  "1" }
 }

 # ----------------------------------------------------------------
 # proc 5 of 9
 # reads input box, parses alias list and returns final command
 proc  p.5.get_alias  {}                 {

         # first check the existence of the alias list file
         if          { ! [ p.4.check_alias_file ] }          {
                 return
         }

         array set  ::myAliasHash  {}

         set  myRegex  {([^=]+)\s*=\s*(.*)}

         # Parse $::myAliasFile and build the array myAliasHash
         set  myFP  [ open  $::myAliasFile  r ]

         while         { ! [ eof $myFP ] }        {

                 set myLine [ string trim [ gets $myFP ] ]

                 # Bug fix! This excludes empty lines of the alias file
                 if          { [ regexp  {^$}  $myLine ] }          {
                         continue
                 }

                 # this excludes commented out lines in the alias file
                 if          { [ regexp  {^#.*$}  $myLine ] }          {
                         continue
                 }

                 regexp  $myRegex  $myLine  =>  myKey  myValue
                 set  myKey  [ string trim  $myKey ]
                 set  myValue  [ string trim  $myValue ]
                 set  myAliasHash($myKey)  $myValue
         }

         close $myFP

         # if alias is found in hash, return command
         # if alias is not found in hash, produce error
         set  myRun  [ array  get  myAliasHash  $::myAlias ]
         if        { $myRun  ==  "" }        {
                 p.2.error
                 return  "!!ERROR!!"
         }
         if        { $myRun  !=  "" }        {
                 set  myRun  $myAliasHash($::myAlias)
                 return $myRun
         }
 }

 # ----------------------------------------------------------------
 # proc 6 of 9
 # searches a program in PATH
 # myFilename = name of file to search in PATH; an executable
 proc  p.6.which  { myFilename }        {

         switch  $::tcl_platform(platform)        {
                 windows        { set myPathList [ split $::env(PATH) \; ] }
                 default         { set myPathList [ split $::env(PATH) : ] }
         }

         foreach  dir  $myPathList        {
                 foreach  ext        { "" .bin .exe .dll .com .bat }                {
                         set myCTL [ file join $dir "$myFilename$ext" ]
                         if        { [ file exists $myCTL ] \
                                 && [ file readable $myCTL ] \
                                 && [ file executable $myCTL ] }  {
                                 return $myCTL
                         }
                 }
         }
 return ""
 }

 # ----------------------------------------------------------------
 # proc 7 of 9
 # applies some voodoo on the command string so as to workaround kfmclient's
 # and maybe even exec's quirks in the future
 proc  p.7.voodoo  { argRun }        {

         # if it is an Internet address, do nothing.
         if          { [ regexp -nocase  {^(ht|f)tp.*}  $argRun ] }          {
                 return  $argRun
         }

         # we need to go to / due to kfmclient's idiosyncrasy: it always
         # assumes the HOME directory. Full paths are useless to it.
         if          { $::tcl_platform(platform)  ==  "unix" }          {
                 cd /
         }

         # "command" may have arguments (multiple words). Isolate the program:
         set  myProgram  [ lindex  $argRun  0 ]

         # check if full path to program was provided and if exists.
         # If yes, nothing else to do.
         if          { [ file exists [ file normalize "[pwd]/$myProgram" ] ] }  {
                 return $argRun
         }

         # if full path to program was NOT provided, find out where it is.
         # find it with 'which' then replace Program with Full Path
         set  myFullPath [ p.6.which  $myProgram ]
         regsub  $myProgram  $argRun  $myFullPath  myRun
         return  $myRun
 }

 # ----------------------------------------------------------------
 # proc 8 of 9
 # the final command that runs the alias's corresponding command
 proc  p.8.run_alias  {}        {

         # First we get alias and corresponding command
         set  myRun  [ p.5.get_alias ]

         # If p.5.get_alias returns error, alias does not exist.
         # Return and start all over again
         if          { $myRun  ==  "!!ERROR!!" }          {
                 return
         }

         # If no error is found, apply voodoo on it.
         set  myRun  [ p.7.voodoo  $myRun ]

         # Run command on Windows
         if          { $::tcl_platform(platform)  ==  "windows" }          {
                 eval exec start $myRun &
                 exit
         }

         # Run command on Unices
         if          { $::tcl_platform(platform)  ==  "unix" }          {

                 # This avoids using kfmclient with kstart
                 set  myProgram  [ lindex $myRun 0 ]
                 if          [ regexp  {^[^ ]*kstart$}  $myProgram ]  {
                         eval exec $myRun &

                 }  else  { eval exec kfmclient exec $myRun & }
         }

         # and here is the ugly hack again
         if          { $::myNotFound  !=  1 }          {
                 exit
         }
 }

 # ----------------------------------------------------------------
 # proc 9 of 9
 # open alias list file in text editor so it can be edited
 proc  p.9.edit_alias  {}        {

         if          { $::tcl_platform(platform)  ==  "windows" }          {
                 eval exec start $::myAliasFile &
         }
         if          { $::tcl_platform(platform)  ==  "unix" }          {
                 cd /
                 eval exec kfmclient exec $::myAliasFile &
         }

         exit
 }

 # ================================================
 # DRAW WINDOW
 # Using these widgets:
 # toplevel .popbox
 # frame  $::w.frame0
 # entry $::w.frame0.aliasbox
 # ================================================

 catch  { destroy .popbox }
 set w  [ toplevel .popbox ]
 wm  withdraw  .
 wm  geometry  $::w  +50+200
 wm  title  $::w  $myWindowTitle
 wm  overrideredirect  $::w  [ expr { $myHasTitle=="yes"  ?  "0"  : "1" } ]

 # --------------------------------
 frame  $::w.frame0
 $::w.frame0  configure  -relief  sunken
 $::w.frame0  configure  -bd  0

 pack  $::w.frame0  -fill  both  -expand  1

 entry $::w.frame0.aliasbox
 $::w.frame0.aliasbox  configure  -takefocus  1
 $::w.frame0.aliasbox  configure  -relief  sunken
 $::w.frame0.aliasbox  configure  -bd  0
 $::w.frame0.aliasbox  configure  -width  $myAliasWidth
 $::w.frame0.aliasbox  configure  -background        $myAliasBg
 $::w.frame0.aliasbox  configure  -foreground        $myAliasFg
 $::w.frame0.aliasbox  configure  -font  $myAliasFont
 $::w.frame0.aliasbox  configure  -justify  $myAliasAlign
 $::w.frame0.aliasbox  configure  -textvariable  myAlias

 pack  $::w.frame0.aliasbox  -expand  1

 # ================================================
 # BINDINGS
 # ================================================

 bind  $::w.frame0.aliasbox  <Return>  { p.8.run_alias }
 bind  $::w  <Key-Escape>  { exit }
 bind  $::w  <Key-F5>  { p.9.edit_alias }
 wm  protocol  $::w  WM_DELETE_WINDOW  { exit }

 # ================================================
 # RUN
 # ================================================

 # Disappoint Mac users
 if          { $::tcl_platform(platform)  ==  "macintosh" }          {
         wm  withdraw  $::w
         tk_messageBox -message "This application does not run on Macs" -title "Ooops!"
         exit
 }

 focus $::w.frame0.aliasbox
 p.3.center_window $::w

LES: Here is the alias list sample. It MUST be called "Alias_list.txt" and reside in the same directory as the application:
 # ================================================
 # ALIAS LIST
 # ================================================

 2d                =        ~/Desktop/todo.txt
 4                =        http://wiki.tcl.tk/4
 chat                =        D:/TclTk/chat/tkchat.kit
 clt                =        http://groups.google.com/groups?ie=UTF-8&group=comp.lang.tcl
 ff                =        /usr/local/bin/firefox
 ffex                =        http://texturizer.net/firefox/extensions/
 ggf                =        http://www.googlefight.com
 pkg                =        ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/README-all.html
 rot                =        http://www.rottentomatoes.com
 sp 1                =        /usr/local/bin/adsl on
 sp 0                =        /usr/local/bin/adsl off
 tz                =        http://www.timeanddate.com/worldclock/
 ws                =        http://www.wordspy.com/
 xc                =        http://www.x-rates.com/calculator.html