[Cvsshell-devel] CVS: cvsshell/src app.py,1.7,1.8 cvs_shell.py,1.12,1.13 interactive_app.py,1.5,1.6
Status: Beta
Brought to you by:
stefanheimann
From: Stefan H. <ste...@us...> - 2002-03-11 10:09:52
|
Update of /cvsroot/cvsshell/cvsshell/src In directory usw-pr-cvs1:/tmp/cvs-serv2904/src Modified Files: app.py cvs_shell.py interactive_app.py Log Message: installation and cvsshell now works on windows! Index: app.py =================================================================== RCS file: /cvsroot/cvsshell/cvsshell/src/app.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** app.py 10 Mar 2002 23:13:59 -0000 1.7 --- app.py 11 Mar 2002 10:09:50 -0000 1.8 *************** *** 1,220 **** ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import sys, os, traceback, getopt ! from oo_tools import GetSetProvider ! ! ! _thisdir = os.path.join(os.getcwd(), sys.path[0]) ! AppError = 'AppError' ! ! ! class App(GetSetProvider): ! ! def __init__(self): ! # the directory this file is placed in ! GetSetProvider.__init__(self) ! self.THIS_DIR = _thisdir ! self.HOME = os.environ['HOME'] ! self.name = self.__class__.__name__ ! self.version = '0.1' ! self.copyright = None ! self.bugAddress = None ! self._args = sys.argv[1:] ! self._env = os.environ ! self._opts = None ! ! ! def closeApp(self): ! for x, y in [(sys.stdin, sys.__stdin__), ! (sys.stdout, sys.__stdout__), ! (sys.stderr, sys.__stderr__)]: ! try: ! if x is not y: ! x.close() ! except: pass ! sys.stdin = sys.__stdin__ ! sys.stdout = sys.__stdout__ ! sys.stderr = sys.__stderr__ ! ! ! def help(self): ! """Print the help message.""" ! print "%s, Version %s" % (self.name, self.version) ! print ! max = 0 ! for x in self._docs: ! l = len(x[0]) ! if l > max: max = l ! for x in self._docs: ! spaces = (max - len(x[0])) * ' ' ! print " %s%s %s" % (x[0],spaces,x[1]) ! print ! ! ! def initOptions(self, opts=[]): ! """ ! This method should be called once before getarg is called. ! opts is a list of key-value pairs. ! The keys are the name of the options that should be ! supported, without the leading `-'. If an ! option expects an argument, the name of the option should be ! followed by a colon. ! The values are the documentation of the option. ! """ ! str = '' ! self._docs = [] ! for x in opts: ! name = x[0] ! doc = x[1] ! str += name ! name = '-' + name ! if name[-1] == ':': name = name[:-1] + " <arg>" ! self._docs.append((name,doc)) ! try: ! self._opts = getopt.getopt(self._args,str) ! except getopt.GetoptError, msg: ! self.printErr("%s: %s" % (self.name, msg)) ! self.exit() ! ! ! ############################## ! # script environment services ! ############################## ! ! def getopt(self, tag): ! """test '-x' command arg""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using getopt!" ! for x in self._opts[0]: ! if x[0] == tag: return 1 ! return 0 ! ! def getarg(self, tag, default=None): ! """get '-x val' command arg""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using getarg!" ! for x in self._opts[0]: ! if x[0] == tag: return x[1] ! return default ! ! def restargs(self): ! """get the rest of the commandline arguments (after all options)""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using restargs!" ! return self._opts[1] ! ! def getenv(self, name, default=''): ! """get '$x' environment var""" ! try: ! return self._env[name] ! except KeyError: ! return default ! ! def setenv(self, name, value): # convenient method ! """set '$x' environment var""" ! self._env[name] = value ! ! def cd(self, dir): ! """Changes the current working directory. Returns the old directory.""" ! if not dir: return ! oldDir = os.getcwd() ! self.printVMsg('cd ' + dir) ! os.chdir(dir) ! return oldDir ! ! def isVerbose(self): ! return self.getopt('-v') or self.getopt('--verbose') ! ! def isHelpRequest(self): ! return self.getopt('-h') or self.getopt('--help') ! ! def printVMsg(self, text='', nonl=0): ! if self.isVerbose(): self.printMsg(text, nonl) ! ! def printMsg(self, text='', nonl=0): ! sys.stdout.write(str(text)) ! if not nonl: sys.stdout.write('\n') ! ! def printErr(self, text='', nonl=0): ! sys.stderr.write(str(text)) ! if not nonl: sys.stderr.write('\n') ! ! def exit(self, message='', status=1): ! if message: ! self.printMsg(message) ! sys.exit(status) ! ! def shell(self, command, fork=0, inp=''): ! if self.isVerbose(): ! self.printMsg(command) # how about ipc? ! if not fork: ! os.system(command) # run a shell cmd ! elif fork == 1: ! return os.popen(command, 'r').read() # get its output ! else: # readlines too? ! pipe = os.popen(command, 'w') ! pipe.write(inp) # send it input ! pipe.close() ! return None ! ! def main(self): ! """to run the app ! main() is the start/run/stop execution protocol""" ! if self.isHelpRequest(): ! self.help() ! return 0 ! res = None ! try: ! self.start() ! self.run() ! res = self.stop() # optional return val ! except SystemExit: # ignore if from exit() ! pass ! except: ! self.printErr('========== Traceback starts here ==========') ! self.printErr('uncaught: ' + `(sys.exc_type,sys.exc_value)`) ! traceback.print_exc() ! self.printErr(""" ! Program Version: %s ! Platform: %s ! Python version: %s""" % (self.version, sys.platform, sys.version)) ! self.printErr("========== Traceback ends here ==========\n" \ ! "You have discovered a bug in %s." % self.name) ! if self.bugAddress.find('@') > 0: ! self.printErr("Please send a bug report to \n %s," ! % self.bugAddress) ! else: ! self.printErr("Please fill out a bug report at \n %s," ! % self.bugAddress) ! self.printErr("including the traceback above.") ! self.closeApp() ! return res ! ! def start(self): ! self.printMsg("%s %s" % (self.name, self.version)) ! self.printMsg(self.copyright) ! self.printVMsg(self.name + ' started.') ! def stop(self): ! self.printVMsg(self.name + ' done.') ! def run(self): ! raise AppError, 'run must be redefined!' ! --- 1,232 ---- ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import sys, os, traceback, getopt ! from oo_tools import GetSetProvider ! ! ! _thisdir = os.path.join(os.getcwd(), sys.path[0]) ! AppError = 'AppError' ! ! ! class App(GetSetProvider): ! ! def __init__(self): ! # the directory this file is placed in ! GetSetProvider.__init__(self) ! self.onWindows = sys.platform[:3] == 'win' ! self.THIS_DIR = _thisdir ! try: ! self.HOME = os.environ['HOME'] ! except KeyError: ! if self.onWindows: ! self.HOME = 'c:\\' ! else: ! self.printErr('$HOME is not set.') ! self.HOME = os.getcwd() ! self.name = self.__class__.__name__ ! self.version = '0.1' ! self.copyright = None ! self.bugAddress = None ! try: ! self.path = os.environ['PATH'].split(os.pathsep) ! except KeyError: ! self.path = [] ! self._args = sys.argv[1:] ! self._env = os.environ ! self._opts = None ! ! ! def closeApp(self): ! for x, y in [(sys.stdin, sys.__stdin__), ! (sys.stdout, sys.__stdout__), ! (sys.stderr, sys.__stderr__)]: ! try: ! if x is not y: ! x.close() ! except: pass ! sys.stdin = sys.__stdin__ ! sys.stdout = sys.__stdout__ ! sys.stderr = sys.__stderr__ ! ! ! def help(self): ! """Print the help message.""" ! print "%s, Version %s" % (self.name, self.version) ! print ! max = 0 ! for x in self._docs: ! l = len(x[0]) ! if l > max: max = l ! for x in self._docs: ! spaces = (max - len(x[0])) * ' ' ! print " %s%s %s" % (x[0],spaces,x[1]) ! print ! ! ! def initOptions(self, opts=[]): ! """ ! This method should be called once before getarg is called. ! opts is a list of key-value pairs. ! The keys are the name of the options that should be ! supported, without the leading `-'. If an ! option expects an argument, the name of the option should be ! followed by a colon. ! The values are the documentation of the option. ! """ ! str = '' ! self._docs = [] ! for x in opts: ! name = x[0] ! doc = x[1] ! str += name ! name = '-' + name ! if name[-1] == ':': name = name[:-1] + " <arg>" ! self._docs.append((name,doc)) ! try: ! self._opts = getopt.getopt(self._args,str) ! except getopt.GetoptError, msg: ! self.printErr("%s: %s" % (self.name, msg)) ! self.exit() ! ! ! ############################## ! # script environment services ! ############################## ! ! def getopt(self, tag): ! """test '-x' command arg""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using getopt!" ! for x in self._opts[0]: ! if x[0] == tag: return 1 ! return 0 ! ! def getarg(self, tag, default=None): ! """get '-x val' command arg""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using getarg!" ! for x in self._opts[0]: ! if x[0] == tag: return x[1] ! return default ! ! def restargs(self): ! """get the rest of the commandline arguments (after all options)""" ! if self._opts == None: ! raise AppError, "You have to call init_options before using restargs!" ! return self._opts[1] ! ! def getenv(self, name, default=''): ! """get '$x' environment var""" ! try: ! return self._env[name] ! except KeyError: ! return default ! ! def setenv(self, name, value): # convenient method ! """set '$x' environment var""" ! self._env[name] = value ! ! def cd(self, dir): ! """Changes the current working directory. Returns the old directory.""" ! if not dir: return ! oldDir = os.getcwd() ! self.printVMsg('cd ' + dir) ! os.chdir(dir) ! return oldDir ! ! def isVerbose(self): ! return self.getopt('-v') or self.getopt('--verbose') ! ! def isHelpRequest(self): ! return self.getopt('-h') or self.getopt('--help') ! ! def printVMsg(self, text='', nonl=0): ! if self.isVerbose(): self.printMsg(text, nonl) ! ! def printMsg(self, text='', nonl=0): ! sys.stdout.write(str(text)) ! if not nonl: sys.stdout.write('\n') ! ! def printErr(self, text='', nonl=0): ! sys.stderr.write(str(text)) ! if not nonl: sys.stderr.write('\n') ! ! def exit(self, message='', status=1): ! if message: ! self.printMsg(message) ! sys.exit(status) ! ! def shell(self, command, fork=0, inp=''): ! if self.isVerbose(): ! self.printMsg(command) # how about ipc? ! if not fork: ! os.system(command) # run a shell cmd ! elif fork == 1: ! return os.popen(command, 'r').read() # get its output ! else: # readlines too? ! pipe = os.popen(command, 'w') ! pipe.write(inp) # send it input ! pipe.close() ! return None ! ! def main(self): ! """to run the app ! main() is the start/run/stop execution protocol""" ! if self.isHelpRequest(): ! self.help() ! return 0 ! res = None ! try: ! self.start() ! self.run() ! res = self.stop() # optional return val ! except SystemExit: # ignore if from exit() ! pass ! except: ! self.printErr('========== Traceback starts here ==========') ! self.printErr('uncaught: ' + `(sys.exc_type,sys.exc_value)`) ! traceback.print_exc() ! self.printErr(""" ! Program Version: %s ! Platform: %s ! Python version: %s""" % (self.version, sys.platform, sys.version)) ! self.printErr("========== Traceback ends here ==========\n" \ ! "You have discovered a bug in %s." % self.name) ! if self.bugAddress.find('@') > 0: ! self.printErr("Please send a bug report to \n %s," ! % self.bugAddress) ! else: ! self.printErr("Please fill out a bug report at \n %s," ! % self.bugAddress) ! self.printErr("including the traceback above.") ! self.closeApp() ! return res ! ! def start(self): ! self.printMsg("%s %s" % (self.name, self.version)) ! self.printMsg(self.copyright) ! self.printVMsg(self.name + ' started.') ! def stop(self): ! self.printVMsg(self.name + ' done.') ! def run(self): ! raise AppError, 'run must be redefined!' ! Index: cvs_shell.py =================================================================== RCS file: /cvsroot/cvsshell/cvsshell/src/cvs_shell.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** cvs_shell.py 10 Mar 2002 23:13:59 -0000 1.12 --- cvs_shell.py 11 Mar 2002 10:09:50 -0000 1.13 *************** *** 1,392 **** ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import os, sys, re, string, types, getopt, utils ! from oo_tools import GetSetProvider ! from app import AppError ! from interactive_app import InteractiveApp ! from plugable_app import PlugableApp ! ! VERSION = '0.1' ! NAME ='CvsShell' ! COPYRIGHT = 'Copyright 2002 Stefan Heimann (ma...@st...).\n' \ ! 'This software is released under the GPL.' ! BUG_ADDRESS = 'http://sourceforge.net/tracker/?group_id=48175&atid=452212' ! CvsError = 'CvsError' ! InternalCvsError = 'InternalCvsError' ! ! class CvsShell(GetSetProvider, PlugableApp, InteractiveApp): ! ! def __init__(self): ! GetSetProvider.__init__(self) ! PlugableApp.__init__(self) ! InteractiveApp.__init__(self) ! sys.stderr = sys.stdout ! self.setName(NAME) ! self.setVersion(VERSION) ! self.setCopyright(COPYRIGHT) ! self.setBugAddress(BUG_ADDRESS) ! etcDir = os.path.join(self.THIS_DIR, '..', 'etc') ! self.setConfigFile(os.path.join(self.HOME,'.cvsshellrc'), ! os.path.join(etcDir, 'default-cvsshellrc')) ! self.setCommandFile(os.path.join(etcDir, 'cvsshell.ini')) ! self.setHistoryFile(os.path.join(self.HOME,'.cvsshell_history')) ! self.initOptions([('v',"Print some extra information"), ! ('h',"Print this help message")]) ! self.CVS_ROOT_FILE = os.path.join('CVS', 'Root') ! self.cmdRegex = re.compile(r""" ! ^\s* # matches leading whitespace ! (?!\#) # line is not a comment (negative lookahead!) ! (?P<name>\S+) # matches the first word of the command ! ( ! \s+ # delimits name and options ! (?P<opts>.*) # the options of the command ! )? # options are optional ! """, re.VERBOSE) ! self.cvsChangeCmdRE = re.compile(r"""\s+ ! (ad(d)?|new|ci|com(mit)?|delete|remove) ! \s+""", re.VERBOSE) ! self.noListingErrMsg = """No listing found. ! You must run `update', `refresh' or `status' before using this command.""" ! self.cvsRoot = None ! self.cvsRootAutoUpdate = 0 ! self.showFullPrompt = 0 ! self.listing = None ! self.dirtyListing = 1 ! self.cvsRootAliases = {} ! self.configMap = {} ! self.cmdToDefOpts = {} ! self.cmdToDefGlobOpts = {} ! self.defOpts = '' # options for all commands ! self.defGlobOpts = '' # global options for all commands ! self.readCommandFile() ! self.readConfigFile() ! ! ! def readCvsRootFromFile(self): ! s = None ! if os.access(self.CVS_ROOT_FILE, os.R_OK): ! s = open(self.CVS_ROOT_FILE, 'r').readline().strip() ! return s ! ! ! def getPrompt(self): ! status = '' ! if self.cvsRootAutoUpdate: status += 'a' ! else: status += 'A' ! root = self.getCvsRoot() or '--' ! try: ! dir = os.getcwd().replace(self.HOME, '~') ! # if you delete the dir you are in, getcwd raise an error ! except OSError: ! dir = 'ERROR' ! if not self.showFullPrompt: ! # make to prompt match on one line ! extraChars = 6 ! maxLineWidth = 79 ! minLen = 15 ! ls = len(status) ! ld = len(dir) ! lr = len(root) ! width = ls + extraChars + ld + lr ! if width > maxLineWidth: ! w = max(minLen, (lr - (width - maxLineWidth + 3)) / 2) ! if lr > 2*w: root = root[:w] + '...' + root[-w:] ! lr = len(root) ! width = ls + extraChars + ld + lr ! if width > maxLineWidth: ! w = max(minLen, ld - (width - maxLineWidth + 3)) ! if ld > w: dir = '...' + dir[-w:] ! ld = len(dir) ! return "{%s} %s [%s]\n$ " % (status, ! dir, ! root) ! ! ! def evalCommand(self, cmd): ! result = self.cmdRegex.match(cmd) ! if result is None: ! return None ! cmdName = result.group('name') ! cmdOpts = result.group('opts') ! if cmdOpts == None: ! cmdOpts = '' ! cmdOpts = cmdOpts.strip() ! fun = self.getCmdFun(cmdName, CvsShell.execShellCmd) ! return fun(self, cmdName, cmdOpts) ! ! ! def run(self): # FIXME: why must I redefine run here? ! InteractiveApp.run(self) ! ! ! def execShellCmd(self, name, opts): ! if name == 'cvs' and self.cvsChangeCmdRE.search(opts): ! # command may change the status ! self.setDirtyListing(1) ! self.shell('%s %s' % (name, opts)) ! ! ! def toggle(self, oldVal, args): ! if len(args) == 0: ! return not oldVal ! else: ! if args == 'on': return 1 ! elif args == 'off': return 0 ! else: ! self.printErr("argument must be `on' or `off'") ! return oldVal ! ! ! def more(self, lines=None, s=None, numlines=22): ! if lines is not None: ! s = string.join(lines, '') ! elif s is not None: ! lines = s.split('\n') ! else: ! raise AppError, \ ! "Illegal argument: One of lines or s must be given" ! if len(lines) <= numlines or not self.configMap.has_key('pager'): ! self.printMsg(s, nonl=1) ! else: ! import tempfile ! pager = self.configMap['pager'] ! try: ! tmp = tempfile.mktemp() ! open(tmp,'w').write(s) ! self.shell(pager + ' ' + tmp) ! except (IOError,OSError), msg: ! self.printMsg(s, nonl=1) ! self.printErr("Error invoking pager `%s': %s" % (pager,str(msg))) ! ! ! ############################## ! # CVS helper methods ! ############################## ! ! def runCvsCmd(self, cmd, rootDir=None, cvsRoot=None, globOpts = '', ! args='', fork=1, getStderr=0): ! """ ! * cmd is the command to execute ! * rootDir is the directory the command should be executed in ! * globOpts are global options. These options ! override the default global options ! * args are the arguments that should be passed to the cvs command ! """ ! oldDir = None ! if rootDir is not None: ! try: ! oldDir = self.cd(rootDir) ! except OSError,msg: ! self.printErr("Could not change to the root directory of " \ ! "the current listing: %s" % str(msg)) ! raise CvsError ! # ! defGlobOpts,defOpts = self.getOptions(cmd) ! cvsRoot = cvsRoot or self.getCvsRoot() ! if cvsRoot != None: cvsRootOpt = "-d %s" % cvsRoot ! else: cvsRootOpt = '' ! # ! globOpts = "%s %s %s" % (defGlobOpts, cvsRootOpt, globOpts) ! args = "%s %s" % (defOpts, args) ! cmd = 'cvs %s %s %s' % (globOpts, cmd, args) ! if getStderr: cmd += ' 2>&1' # FIXME: does not work on win! ! self.printVMsg(cmd) ! if fork: ! f = os.popen(cmd) ! try: ! lines = f.readlines() ! except KeyboardInterrupt: ! return [] ! if f.close() is not None: # cvs command caused an error ! raise CvsError ! else: ! return lines ! else: ! r = os.system(cmd) ! if r != 0: ! raise CvsError ! if oldDir is not None: ! try: ! self.cd(oldDir) ! except OSError,msg: ! self.printErr(msg) ! return None ! ! ! def applyOnEntryList(self, opts, fun): ! """Applies fun to the entries of the current listing selected by opts. ! opts is a string that may specify some numbers ! which refers to entries in the listing. ! fun must take 2 argument as the entry and its ! filename is passed to it. Returns a ! list with the return values from fun. ! If a error occurs, an AppError is raised. If this error is ! encountered while parsing opts, a ParseError is raised.""" ! nums = utils.parseNumberStr(opts) ! if len(nums) == 0: ! raise AppError, 'No files specified.' ! if not self.listing: ! raise AppError, self.noListingErrMsg ! result = [] ! max = len(self.listing.entries) ! for x in nums: ! if x < 0 or x > max: continue ! e = self.listing.entries[x] ! name = os.path.join(e.dir, e.name) ! result.append(fun(e,name)) ! return result ! ! ! def printListing(self): ! if not self.listing: ! self.printErr(self.noListingErrMsg) ! else: ! self.listing.printEntries() ! if self.getDirtyListing(): ! self.printMsg("Listing may be out-of-date. " \ ! "Run `update', `refresh' or `status'.") ! ! ! def parseArgs(self, args, cvsCmdOpts='', cmdOpts=''): ! """Parse args and separates it like that: ! cvsCmdOpts are the options that are allowed for the cvs command ! cmdOpts are additional options (provided by cvsshell) ! global options are handled automatically ! * global cvs options (as string) ! * command specific cvs options (as string) ! * other options (as dictionary) ! * other args (as string) ! These values are returned as a 4-tupel. ! A GetoptError is raised if there are unknown options.""" ! cvsGlobOpts = 'HQqrwlntvb:T:e:d:fz:as:' ! s = cvsGlobOpts+cvsCmdOpts+cmdOpts ! (opts, rest) = getopt.getopt(utils.splitquoted(args), s) ! cvsGlobOptstr = cvsCmdOptstr = '' ! cmdOptDict = {} ! for x in opts: ! name, value = x ! # getopt prepends '-' to every option ! if name[1] in cvsGlobOpts: ! cvsGlobOptstr += ' ' + name ! if value: cvsGlobOptstr += ' ' + value ! elif name[1] in cvsCmdOpts: ! cvsCmdOptstr += ' ' + name ! if value: cvsCmdOptstr += ' ' + value ! else: ! cmdOptDict[name] = value ! if cvsGlobOptstr: cvsGlobOptstr = cvsGlobOptstr[1:] # remove trailing ' ' ! if cvsCmdOptstr: cvsCmdOptstr = cvsCmdOptstr[1:] # remove trailing ' ' ! reststr = '' ! for x in rest: ! reststr += x + ' ' ! if reststr: reststr = reststr[:-1] ! return (cvsGlobOptstr, cvsCmdOptstr, cmdOptDict, reststr) ! ! ! def getOptions(self, cmd): ! """Returns the options that should be used for cmd. The return value ! is the tupel (globalOptions, options)""" ! defGlobOpts = self.getDefGlobOpts() ! defOpts = self.getDefOpts() ! defGlobOpts += ' ' + self.getCmdToDefGlobOpts().get(cmd,'') ! defOpts += ' ' + self.getCmdToDefOpts().get(cmd,'') ! return defGlobOpts,defOpts ! ! ! ! ############################## ! # Listing ! ############################## ! ! class Listing(GetSetProvider): ! ! def __init__(self, app, rootDir, entries=[]): ! GetSetProvider.__init__(self) ! self.app = app ! self.rootDir = rootDir ! self.entries = entries ! self.sortOrder = ['dir', 'status', 'name'] ! ! def sortEntries(self): ! class __EntrySorter: ! def __init__(self, order): self.order = order ! def cmp(self,x,y): ! try: ! i = 0 ! for attr in self.order: ! i = eval("cmp(x.%s, y.%s)" % (attr,attr)) ! if i != 0: return i ! return i ! except AttributeError: ! return -1 ! self.entries.sort(__EntrySorter(self.sortOrder).cmp) ! ! def printEntries(self, verbose=1): ! if not self.entries: ! if verbose: self.app.printMsg('No entries available.') ! return ! max = 0 ! for e in self.entries: ! l = len(e.status) ! if l > max: max = l ! formatStr = " %%%dd %%-%ds %%s\n" % (len(`len(self.entries)`), max) ! oldDir = None ! id = 0 ! lines = [] ! for e in self.entries: ! newDir = e.dir ! if oldDir != newDir: ! lines.append("%s:\n" % newDir) ! lines.append(formatStr % (id, e.status, e.name)) ! id += 1 ! oldDir = newDir ! self.app.more(lines) ! ! ############################## ! # Entry ! ############################## ! ! class Entry(GetSetProvider): ! S_NEW = 'N' ! S_ADDED = 'A' ! S_CONFLICT = 'C' ! S_MODIFIED = 'M' ! S_PATCHED = 'P' ! S_REMOVED = 'R' ! S_UPDATED = 'U' ! S_UNKNOWN = '?' ! S_OK = 'OK' # file on the sandbox is in sync with repository ! S_DELETED = 'D' # file scheduled for removal has been commited ! def __init__(self, dir, name, status): ! GetSetProvider.__init__(self) ! self.dir = dir ! self.name = name ! self.status = status ! ! ! ! if __name__ == '__main__': ! app = CvsShell() ! res = app.main() ! app.exit(res) ! ! --- 1,394 ---- ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import os, sys, re, string, types, getopt, utils ! from oo_tools import GetSetProvider ! from app import AppError ! from interactive_app import InteractiveApp ! from plugable_app import PlugableApp ! ! VERSION = '0.1' ! NAME ='CvsShell' ! COPYRIGHT = 'Copyright 2002 Stefan Heimann (ma...@st...).\n' \ ! 'This software is released under the GPL.' ! BUG_ADDRESS = 'http://sourceforge.net/tracker/?group_id=48175&atid=452212' ! CvsError = 'CvsError' ! InternalCvsError = 'InternalCvsError' ! ! class CvsShell(GetSetProvider, PlugableApp, InteractiveApp): ! ! def __init__(self): ! GetSetProvider.__init__(self) ! PlugableApp.__init__(self) ! InteractiveApp.__init__(self) ! sys.stderr = sys.stdout ! self.setName(NAME) ! self.setVersion(VERSION) ! self.setCopyright(COPYRIGHT) ! self.setBugAddress(BUG_ADDRESS) ! etcDir = os.path.join(self.THIS_DIR, '..', 'etc') ! self.setConfigFile(os.path.join(self.HOME,'.cvsshellrc'), ! os.path.join(etcDir, 'default-cvsshellrc')) ! self.setCommandFile(os.path.join(etcDir, 'cvsshell.ini')) ! self.setHistoryFile(os.path.join(self.HOME,'.cvsshell_history')) ! self.initOptions([('v',"Print some extra information"), ! ('h',"Print this help message")]) ! self.CVS_ROOT_FILE = os.path.join('CVS', 'Root') ! self.cmdRegex = re.compile(r""" ! ^\s* # matches leading whitespace ! (?!\#) # line is not a comment (negative lookahead!) ! (?P<name>\S+) # matches the first word of the command ! ( ! \s+ # delimits name and options ! (?P<opts>.*) # the options of the command ! )? # options are optional ! """, re.VERBOSE) ! self.cvsChangeCmdRE = re.compile(r"""\s+ ! (ad(d)?|new|ci|com(mit)?|delete|remove) ! \s+""", re.VERBOSE) ! self.noListingErrMsg = """No listing found. ! You must run `update', `refresh' or `status' before using this command.""" ! self.cvsRoot = None ! self.cvsRootAutoUpdate = 0 ! self.showFullPrompt = 0 ! self.listing = None ! self.dirtyListing = 1 ! self.cvsRootAliases = {} ! self.configMap = {} ! self.cmdToDefOpts = {} ! self.cmdToDefGlobOpts = {} ! self.defOpts = '' # options for all commands ! self.defGlobOpts = '' # global options for all commands ! self.readCommandFile() ! self.readConfigFile() ! ! ! def readCvsRootFromFile(self): ! s = None ! if os.access(self.CVS_ROOT_FILE, os.R_OK): ! s = open(self.CVS_ROOT_FILE, 'r').readline().strip() ! return s ! ! ! def getPrompt(self): ! status = '' ! if self.cvsRootAutoUpdate: status += 'a' ! else: status += 'A' ! root = self.getCvsRoot() or '--' ! try: ! dir = os.getcwd().replace(self.HOME, '~') ! # if you delete the dir you are in, getcwd raise an error ! except OSError: ! dir = 'ERROR' ! if not self.showFullPrompt: ! # make to prompt match on one line ! extraChars = 6 ! maxLineWidth = 79 ! minLen = 15 ! ls = len(status) ! ld = len(dir) ! lr = len(root) ! width = ls + extraChars + ld + lr ! if width > maxLineWidth: ! w = max(minLen, (lr - (width - maxLineWidth + 3)) / 2) ! if lr > 2*w: root = root[:w] + '...' + root[-w:] ! lr = len(root) ! width = ls + extraChars + ld + lr ! if width > maxLineWidth: ! w = max(minLen, ld - (width - maxLineWidth + 3)) ! if ld > w: dir = '...' + dir[-w:] ! ld = len(dir) ! return "{%s} %s [%s]\n$ " % (status, ! dir, ! root) ! ! ! def evalCommand(self, cmd): ! result = self.cmdRegex.match(cmd) ! if result is None: ! return None ! cmdName = result.group('name') ! cmdOpts = result.group('opts') ! if cmdOpts == None: ! cmdOpts = '' ! cmdOpts = cmdOpts.strip() ! fun = self.getCmdFun(cmdName, CvsShell.execShellCmd) ! return fun(self, cmdName, cmdOpts) ! ! ! def run(self): # FIXME: why must I redefine run here? ! InteractiveApp.run(self) ! ! ! def execShellCmd(self, name, opts): ! if name == 'cvs' and self.cvsChangeCmdRE.search(opts): ! # command may change the status ! self.setDirtyListing(1) ! self.shell('%s %s' % (name, opts)) ! ! ! def toggle(self, oldVal, args): ! if len(args) == 0: ! return not oldVal ! else: ! if args == 'on': return 1 ! elif args == 'off': return 0 ! else: ! self.printErr("argument must be `on' or `off'") ! return oldVal ! ! ! def more(self, lines=None, s=None, numlines=22): ! if lines is not None: ! s = string.join(lines, '') ! elif s is not None: ! lines = s.split('\n') ! else: ! raise AppError, \ ! "Illegal argument: One of lines or s must be given" ! if len(lines) <= numlines or not self.configMap.has_key('pager'): ! self.printMsg(s, nonl=1) ! else: ! import tempfile ! pager = self.configMap['pager'] ! try: ! tmp = tempfile.mktemp() ! open(tmp,'w').write(s) ! self.shell(pager + ' ' + tmp) ! except (IOError,OSError), msg: ! self.printMsg(s, nonl=1) ! self.printErr("Error invoking pager `%s': %s" % (pager,str(msg))) ! ! ! ############################## ! # CVS helper methods ! ############################## ! ! def runCvsCmd(self, cmd, rootDir=None, cvsRoot=None, globOpts = '', ! args='', fork=1, getStderr=0): ! """ ! * cmd is the command to execute ! * rootDir is the directory the command should be executed in ! * globOpts are global options. These options ! override the default global options ! * args are the arguments that should be passed to the cvs command ! """ ! oldDir = None ! if rootDir is not None: ! try: ! oldDir = self.cd(rootDir) ! except OSError,msg: ! self.printErr("Could not change to the root directory of " \ ! "the current listing: %s" % str(msg)) ! raise CvsError ! # ! defGlobOpts,defOpts = self.getOptions(cmd) ! cvsRoot = cvsRoot or self.getCvsRoot() ! if cvsRoot != None: cvsRootOpt = "-d %s" % cvsRoot ! else: cvsRootOpt = '' ! # ! globOpts = "%s %s %s" % (defGlobOpts, cvsRootOpt, globOpts) ! args = "%s %s" % (defOpts, args) ! cmd = 'cvs %s %s %s' % (globOpts, cmd, args) ! # on windows, cvs seems to output all information on stdout ! if getStderr and not self.onWindows: cmd += ' 2>&1' ! self.printVMsg(cmd) ! if fork: ! f = os.popen(cmd) ! try: ! lines = f.readlines() ! except KeyboardInterrupt: ! return [] ! if f.close() is not None: # cvs command caused an error ! raise CvsError ! else: ! return lines ! else: ! r = os.system(cmd) ! if r != 0: ! raise CvsError ! if oldDir is not None: ! try: ! self.cd(oldDir) ! except OSError,msg: ! self.printErr(msg) ! return None ! ! ! def applyOnEntryList(self, opts, fun): ! """Applies fun to the entries of the current listing selected by opts. ! opts is a string that may specify some numbers ! which refers to entries in the listing. ! fun must take 2 argument as the entry and its ! filename is passed to it. Returns a ! list with the return values from fun. ! If a error occurs, an AppError is raised. If this error is ! encountered while parsing opts, a ParseError is raised.""" ! nums = utils.parseNumberStr(opts) ! if len(nums) == 0: ! raise AppError, 'No files specified.' ! if not self.listing: ! raise AppError, self.noListingErrMsg ! result = [] ! max = len(self.listing.entries) ! for x in nums: ! if x < 0 or x > max: continue ! e = self.listing.entries[x] ! name = os.path.join(e.dir, e.name) ! result.append(fun(e,name)) ! return result ! ! ! def printListing(self): ! if not self.listing: ! self.printErr(self.noListingErrMsg) ! else: ! self.listing.printEntries() ! if self.getDirtyListing(): ! self.printMsg("Listing may be out-of-date. " \ ! "Run `update', `refresh' or `status'.") ! ! ! def parseArgs(self, args, cvsCmdOpts='', cmdOpts=''): ! """Parse args and separates it like that: ! cvsCmdOpts are the options that are allowed for the cvs command ! cmdOpts are additional options (provided by cvsshell) ! global options are handled automatically ! * global cvs options (as string) ! * command specific cvs options (as string) ! * other options (as dictionary) ! * other args (as string) ! These values are returned as a 4-tupel. ! A GetoptError is raised if there are unknown options.""" ! cvsGlobOpts = 'HQqrwlntvb:T:e:d:fz:as:' ! s = cvsGlobOpts+cvsCmdOpts+cmdOpts ! (opts, rest) = getopt.getopt(utils.splitquoted(args), s) ! cvsGlobOptstr = cvsCmdOptstr = '' ! cmdOptDict = {} ! for x in opts: ! name, value = x ! # getopt prepends '-' to every option ! if name[1] in cvsGlobOpts: ! cvsGlobOptstr += ' ' + name ! if value: cvsGlobOptstr += ' ' + value ! elif name[1] in cvsCmdOpts: ! cvsCmdOptstr += ' ' + name ! if value: cvsCmdOptstr += ' ' + value ! else: ! cmdOptDict[name] = value ! if cvsGlobOptstr: cvsGlobOptstr = cvsGlobOptstr[1:] # remove trailing ' ' ! if cvsCmdOptstr: cvsCmdOptstr = cvsCmdOptstr[1:] # remove trailing ' ' ! reststr = '' ! for x in rest: ! reststr += x + ' ' ! if reststr: reststr = reststr[:-1] ! return (cvsGlobOptstr, cvsCmdOptstr, cmdOptDict, reststr) ! ! ! def getOptions(self, cmd): ! """Returns the options that should be used for cmd. The return value ! is the tupel (globalOptions, options)""" ! defGlobOpts = self.getDefGlobOpts() ! defOpts = self.getDefOpts() ! defGlobOpts += ' ' + self.getCmdToDefGlobOpts().get(cmd,'') ! defOpts += ' ' + self.getCmdToDefOpts().get(cmd,'') ! return defGlobOpts,defOpts ! ! ! ! ############################## ! # Listing ! ############################## ! ! class Listing(GetSetProvider): ! ! def __init__(self, app, rootDir, entries=[]): ! GetSetProvider.__init__(self) ! self.app = app ! self.rootDir = rootDir ! self.entries = entries ! self.sortOrder = ['dir', 'status', 'name'] ! ! def sortEntries(self): ! class __EntrySorter: ! def __init__(self, order): self.order = order ! def cmp(self,x,y): ! try: ! i = 0 ! for attr in self.order: ! i = eval("cmp(x.%s, y.%s)" % (attr,attr)) ! if i != 0: return i ! return i ! except AttributeError: ! return -1 ! self.entries.sort(__EntrySorter(self.sortOrder).cmp) ! ! def printEntries(self, verbose=1): ! if not self.entries: ! if verbose: self.app.printMsg('No entries available.') ! return ! max = 0 ! for e in self.entries: ! l = len(e.status) ! if l > max: max = l ! formatStr = " %%%dd %%-%ds %%s\n" % (len(`len(self.entries)`), max) ! oldDir = None ! id = 0 ! lines = [] ! for e in self.entries: ! newDir = e.dir ! if oldDir != newDir: ! lines.append("%s:\n" % newDir) ! lines.append(formatStr % (id, e.status, e.name)) ! id += 1 ! oldDir = newDir ! self.app.more(lines) ! ! ! ############################## ! # Entry ! ############################## ! ! class Entry(GetSetProvider): ! S_NEW = 'N' ! S_ADDED = 'A' ! S_CONFLICT = 'C' ! S_MODIFIED = 'M' ! S_PATCHED = 'P' ! S_REMOVED = 'R' ! S_UPDATED = 'U' ! S_UNKNOWN = '?' ! S_OK = 'OK' # file on the sandbox is in sync with repository ! S_DELETED = 'D' # file scheduled for removal has been commited ! def __init__(self, dir, name, status): ! GetSetProvider.__init__(self) ! self.dir = dir ! self.name = name ! self.status = status ! ! ! def main(): ! app = CvsShell() ! res = app.main() ! app.exit(res) ! ! if __name__ == '__main__': ! main() Index: interactive_app.py =================================================================== RCS file: /cvsroot/cvsshell/cvsshell/src/interactive_app.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** interactive_app.py 10 Mar 2002 23:13:59 -0000 1.5 --- interactive_app.py 11 Mar 2002 10:09:50 -0000 1.6 *************** *** 1,84 **** ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import os ! from app import App, AppError ! ! class InteractiveApp(App): ! ! def __init__(self): ! App.__init__(self) ! self.BREAK_REPL = 'BREAK_REPL' ! try: ! import readline ! except ImportError: ! self.printErr("Could not import readline. History browsing " \ ! "and tab completion will not be available.") ! return ! else: ! readline.parse_and_bind("tab: complete") ! ! def setHistoryFile(self,filename): ! self.historyFile = filename ! try: ! import readline ! except ImportError: return ! try: ! readline.read_history_file(self.historyFile) ! except IOError: ! pass ! import atexit ! atexit.register(readline.write_history_file, self.historyFile) ! ! def run(self): # define App.run here ! while 1: ! command = self.readCommand() ! if command == None: ! break ! result = self.evalCommand(command) ! if result is self.BREAK_REPL: break ! elif result is None: continue ! else: self.printMsg(result) ! ! def readCommand(self): # subclass hooks + App.start,stop ! return self.prompt(self.getPrompt()) ! ! def getPrompt(self): ! return '? ' ! ! def prompt(self, msg): ! """Displayes msg and prompts the user for input. ! If input is EOF, None is returned.""" ! try: ! a = raw_input(msg) ! except (EOFError, KeyboardInterrupt): ! self.printMsg() ! return None ! a = os.path.expandvars(a) ! a = os.path.expanduser(a) ! return a.strip() ! ! def evalCommand(self, command): ! """Subclasses should override this method. ! If BREAK_REPL is returned, the repl is terminated, ! otherwise the return-value is printed (if not None).""" ! raise AppError, 'evalCommand must be redefined!' ! --- 1,87 ---- ! ############################################################################### ! # This file is part of CvsShell ! # ! # CvsShell is free software; you can redistribute it and/or modify ! # it under the terms of the GNU General Public License as published by ! # the Free Software Foundation; either version 2 of the License, or ! # (at your option) any later version. ! # ! # CvsShell is distributed in the hope that it will be useful, ! # but WITHOUT ANY WARRANTY; without even the implied warranty of ! # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! # GNU General Public License for more details. ! # ! # You should have received a copy of the GNU General Public License ! # along with CvsShell; if not, write to the Free Software ! # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ! # ! # Copyright 2002 by Stefan Heimann ! # Website: http://cvsshell.sourceforge.net/ ! ############################################################################### ! ! import os ! from app import App, AppError ! ! class InteractiveApp(App): ! ! def __init__(self): ! App.__init__(self) ! self.BREAK_REPL = 'BREAK_REPL' ! try: ! import readline ! except ImportError: ! self.printErr("Could not import readline.\nHistory browsing " \ ! "and tab completion will not be available.") ! return ! else: ! readline.parse_and_bind("tab: complete") ! ! def setHistoryFile(self,filename): ! self.historyFile = filename ! try: ! import readline ! except ImportError: return ! try: ! readline.read_history_file(self.historyFile) ! except IOError: ! pass ! import atexit ! atexit.register(readline.write_history_file, self.historyFile) ! ! def run(self): # define App.run here ! while 1: ! command = self.readCommand() ! if command == None: ! break ! result = self.evalCommand(command) ! if result is self.BREAK_REPL: break ! elif result is None: continue ! else: self.printMsg(result) ! ! def readCommand(self): # subclass hooks + App.start,stop ! return self.prompt(self.getPrompt()) ! ! def getPrompt(self): ! return '? ' ! ! def prompt(self, msg): ! """Displayes msg and prompts the user for input. ! If input is EOF, None is returned.""" ! try: ! a = raw_input(msg) ! except EOFError: ! self.printMsg() ! return None ! except KeyboardInterrupt: ! self.printMsg() ! return None ! a = os.path.expandvars(a) ! a = os.path.expanduser(a) ! return a.strip() ! ! def evalCommand(self, command): ! """Subclasses should override this method. ! If BREAK_REPL is returned, the repl is terminated, ! otherwise the return-value is printed (if not None).""" ! raise AppError, 'evalCommand must be redefined!' ! |