diff --git a/twander.py b/twander.py index 8424cf1..cd3ecd3 100755 --- a/twander.py +++ b/twander.py @@ -4,7 +4,7 @@ # For Updates See: http://www.tundraware.com/Software/twander PROGNAME = "twander" -RCSID = "$Id: twander.py,v 2.53 2003/01/11 23:24:15 tundra Exp $" +RCSID = "$Id: twander.py,v 2.54 2003/01/13 18:45:28 tundra Exp $" VERSION = RCSID.split()[2] @@ -266,7 +266,8 @@ CONF = "" # Config file user selected with -c option COMMENT = r"#" # Comment introducer string ENVVBL = r'$' # Symbol denoting an environment variable -MAXHIST = 32 # Maximum length of command history +MAXDIR = 32 # Maximum number of directories to track +MAXHIST = 32 # Maximum length of Command History MAXNESTING = 32 # Maximum depth of nested variable definitions QUOTECHAR = '\"' # Character to use when quoting Built-In Variables @@ -330,6 +331,8 @@ # Warnings +wBADEXE = "Could Not Execute Command:\n\n%s" +wBADVAL = "Option Assignment Has Bad Righthand Side.\nIgnoring Line %s:\n\n%s" wCMDKEY = "Configuration File Entry For: \'%s\' Has No Command Key Defined." wCONFOPEN = "Cannot Open Configuration File:\n%s\n\n%s" wDIRSCREDEF = "Directory Shortcut Defined More Than Once.\nIgnoring Line %s:\n\n%s" @@ -370,8 +373,8 @@ # List of internal program variables to dump during debug sessions DebugVars = ["RCSID", "OSNAME", "HOSTNAME", "OPTIONS", "STARTDIR", "HOME", "CONF", - "HEIGHT", "WIDTH", "BCOLOR", "FCOLOR", "FNAME", "FSZ", - "FWT", "AUTOREFRESH", "DEBUGLEVEL", "WARN", "PSEP", + "HEIGHT", "WIDTH", "BCOLOR", "FCOLOR", "FNAME", "FSZ", "FWT", + "MAXDIR", "MAXHIST", "MAXNESTING", "AUTOREFRESH", "DEBUGLEVEL", "WARN", "PSEP", "QUOTECHAR", "POLLINT", "REFRESHINT"] @@ -468,6 +471,7 @@ UI.CmdTable = {} UI.FuncKeys = ["", "", "", "", "", "", "", "", "", "", "", ""] + UI.OptionsNumeric = ["DEBUGLEVEL", "HEIGHT", "MAXDIR", "MAXHIST", "MAXNESTING", "REFRESHINT", "WIDTH"] UI.SymTable = {} linenum = 0 @@ -603,7 +607,6 @@ def ParseLine(line, num): global UI - # Get rid of trailing newline, if any if line[-1] == '\n': line = line[:-1] @@ -714,7 +717,14 @@ if val: UpdateDirMenu(val) - + # Process any option variables + + elif name in UI.OptionsNumeric: + try: + globals()[name] = int(val) # !!! Cheater's way to get to global variables. + except: + WrnMsg(wBADVAL % (num, line)) + # Process other variable types. # Distinguish between internal program variables and # user-defined variables and act accordingly. We inhibit @@ -750,6 +760,21 @@ depth = 0 while doeval: + + # Bound the number of times we can nest a definition + # to prevent self-references which give infinite nesting depth + + depth += 1 + if (depth > MAXNESTING): + doeval = FALSE + + # See if there are still unresolved variable references. + # If so, let the user know + + if REVAR.findall(cmd): + ErrMsg(eVBLTOODEEP % (num, cmd)) + sys.exit(1) + # Get a list of variable references vbls = REVAR.findall(cmd) @@ -801,20 +826,6 @@ else: doeval = FALSE - # Bound the number of times we can nest a definition - # to prevent self-references which give infinite nesting depth - - depth += 1 - if depth == MAXNESTING: - doeval = FALSE - - # See if there are still unresolved variable references. - # If so, let the user know - - if REVAR.findall(cmd): - ErrMsg(eVBLTOODEEP % (num, cmd)) - sys.exit(1) - # Add the command entry to the command table. # Prevent duplicate keys from being entered. @@ -909,37 +920,28 @@ # Setup the Command Menu - self.CmdBtn = Menubutton(self.mBar, text=COMMANDMENU, underline=0, state=DISABLED, font=(FNAME, FSZ, "bold")) - self.CmdBtn.menu = Menu(self.CmdBtn, font=("courier", FSZ, "bold")) + self.CmdBtn = Menubutton(self.mBar, text=COMMANDMENU, underline=0, state=DISABLED) + self.CmdBtn.menu = Menu(self.CmdBtn) self.CmdBtn.pack(side=LEFT, padx=MENUPADX) # Setup the Directory Menu - self.DirBtn = Menubutton(self.mBar, text=DIRMENU, underline=0, font=(FNAME, FSZ, "bold")) - self.DirBtn.menu = Menu(self.DirBtn, font=("courier", FSZ, "bold")) + self.DirBtn = Menubutton(self.mBar, text=DIRMENU, underline=0) + self.DirBtn.menu = Menu(self.DirBtn) self.DirBtn.pack(side=LEFT, padx=MENUPADX) # Setup the History Menu - self.HistBtn = Menubutton(self.mBar, text=HISTMENU, underline=0, font=(FNAME, FSZ, "bold")) - self.HistBtn.menu = Menu(self.HistBtn, font=("courier", FSZ, "bold")) + self.HistBtn = Menubutton(self.mBar, text=HISTMENU, underline=0) + self.HistBtn.menu = Menu(self.HistBtn) self.HistBtn.pack(side=LEFT, padx=MENUPADX) # Setup the Directory Listing and Scrollbars self.hSB = Scrollbar(root, orient=HORIZONTAL) self.vSB = Scrollbar(root, orient=VERTICAL) - self.DirList = Listbox(root, - foreground = FCOLOR, - background = BCOLOR, - font=(FNAME, FSZ, FWT), - selectmode=EXTENDED, - exportselection=0, - xscrollcommand=self.hSB.set, - yscrollcommand=self.vSB.set, - height = HEIGHT, - width = WIDTH, - ) + self.DirList = Listbox(root, selectmode=EXTENDED, exportselection=0, + xscrollcommand=self.hSB.set, yscrollcommand=self.vSB.set) # Make them visible by packing @@ -1329,7 +1331,7 @@ # Check for, and handle accelerator keys if event.state == AltMask: - + # Set menu button associated with accelerator # Command Menu @@ -1722,9 +1724,9 @@ # Event Handler: Run Command #### -def KeyRunCommand(event, initial="", save=TRUE): +def KeyRunCommand(event, initial=""): global UI - + # Prompt with passed initial edit string if initial: cmd = askstring(pRUNCMD, pENCMD, initialvalue=initial) @@ -1737,14 +1739,19 @@ else: cmd = askstring(pRUNCMD, pENCMD) - # Save command (if any) if we're supposed to - if save and cmd: - UI.LastCmd = cmd - - # Execute command (if any) + # Execute command (if any) - Blank entry means do nothing/return if cmd: ExecuteCommand(cmd) + # Save the command only if Command History is enable (MAXHIST > 0) + # AND one of two conditions exist: + # + # 1) No initial string was provided (The user entered a command manually). + # 2) An initial string was provided, but the user edited it. + + if (MAXHIST > 0) and ((not initial) or (cmd != initial)): + UI.LastCmd = cmd + UI.DirList.focus() # End of 'KeyRunCommand()' @@ -1883,28 +1890,37 @@ # Run the command on Win32 using filename associations if UseStartDir: - os.startfile(cmd) + try: + os.startfile(cmd) + except: + WrnMsg(wBADEXE % cmd) # Normal command execution for both Unix and Win32 else: - thread.start_new_thread(os.system, (cmd,)) + try: + thread.start_new_thread(os.system, (cmd,)) + except: + WrnMsg(wBADEXE % cmd) - # In all cases, save command history, maintaining max stack depth - if len(UI.CmdHist) == MAXHIST: - UI.CmdHist = UI.CmdHist[1:] - UI.CmdHist.append(cmd) + # If the user has Command History enabled (MAXHIST > 0), + # save command history, maintaining max stack depth - # Now update the History Menu accordingly - - UI.HistBtn.menu.delete(0,END) - for entry in UI.CmdHist: - UI.HistBtn.menu.add_command(label=entry, command=lambda cmd=entry: KeyRunCommand(None, initial=cmd, save=FALSE)) - UI.HistBtn['menu'] = UI.HistBtn.menu + if (MAXHIST > 0): + if len(UI.CmdHist) == MAXHIST: + UI.CmdHist = UI.CmdHist[1:] + UI.CmdHist.append(cmd) - # Enable the History Menu button if there is now content there - if UI.HistBtn.menu.size(): - UI.HistBtn.config(state=NORMAL) + # Now update the History Menu accordingly + + UI.HistBtn.menu.delete(0,END) + for entry in UI.CmdHist: + UI.HistBtn.menu.add_command(label=entry, command=lambda cmd=entry: KeyRunCommand(None, initial=cmd)) + UI.HistBtn['menu'] = UI.HistBtn.menu + + # Enable the History Menu button if there is now content there + if UI.HistBtn.menu.size(): + UI.HistBtn.config(state=NORMAL) # Dump Command History stack if requested @@ -2315,8 +2331,48 @@ # Program Entry Point # #----------------------------------------------------------# +##### +# Create an instance of the UI +##### + +UIroot = Tk() +UI = twanderUI(UIroot) + +# Make the Tk window the topmost in the Z stack. +# 'Gotta do this or Win32 will not return input +# focus to our program after a startup warning +# display. + +UIroot.tkraise() + +##### +# Setup global UI variables +##### + +# Setup Built-In Variables +UI.BuiltIns = {DIR:"", DSELECTION:"", DSELECTIONS:"", HASH:"", + PROMPT:"", SELECTION:"", SELECTIONS:""} + +# Prepare storage for key bindings +UI.KeyBindings = {} + +# Initialize list of all directories visited +UI.AllDirs = [] + +# Initialize directory stack +UI.LastDir = [] + +# And current location +UI.CurrentDir = "" + +# Initialize Command History data structures +ClearHistory(None) + + +##### # Command line processing - Process any options set in the # environment first, and then those given on the command line +##### OPTIONS = sys.argv[1:] envopt = os.getenv(PROGNAME.upper()) @@ -2329,13 +2385,19 @@ Usage() sys.exit(1) -# Parse command line +# Read configuration file in first - this allows +# environment variable and then command line to override its settings later for opt, val in opts: + if opt == "-c": + CONF = os.path.abspath(val) + +ParseConfFile(None) + +# Parse the rest of the command line +for opt, val in opts: if opt == "-b": BCOLOR = val - if opt == "-c": - CONF = os.path.abspath(val) if opt == "-d": DEBUGLEVEL = val if int(DEBUGLEVEL): @@ -2365,21 +2427,6 @@ if opt == "-y": HEIGHT = val -# Create an instance of the UI -UIroot = Tk() -UI = twanderUI(UIroot) - -# Make the Tk window the topmost in the Z stack. -# 'Gotta do this or Win32 will not return input -# focus to our program after a startup warning -# display. - -UIroot.tkraise() - -##### -# Setup global UI variables -##### - # Figure out where to start # Program can only have 0 or 1 arguments # Make sure any startdir argument is legit @@ -2410,31 +2457,22 @@ # Get starting directory into canonical form STARTDIR = os.path.abspath(STARTDIR) -# Setup Built-In Variables -UI.BuiltIns = {DIR:"", DSELECTION:"", DSELECTIONS:"", HASH:"", - PROMPT:"", SELECTION:"", SELECTIONS:""} +# Now that all user switches have been read, set GUI options -# Prepare storage for key bindings -UI.KeyBindings = {} +for i in (UI.CmdBtn, UI.DirBtn, UI.HistBtn): + i.config(font=(FNAME, FSZ, "bold")) + i.menu.config(font=("courier", FSZ, "bold")) -# Initialize list of all directories visited -UI.AllDirs = [] +UI.DirList.config(font=(FNAME, FSZ, FWT), + foreground=FCOLOR, background=BCOLOR, + height=HEIGHT, width=WIDTH) -# Initialize Command History data structures -ClearHistory(None) +# Setup event handlers - We have to do this here just +# in case we could not read any config file, which +# is when we normally bind the handlers. -# Parse the and store configuration file, if any -ParseConfFile(None) - -# Setup event handlers UI.BindAllHandlers() -# Initialize directory stack -UI.LastDir = [] - -# And current location -UI.CurrentDir = "" - # Need mutex to serialize on widget updates UI.DirListMutex = mutex.mutex()