1*cda5da8dSAndroid Build Coastguard Worker"""A generic class to build line-oriented command interpreters. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerInterpreters constructed with this class obey the following conventions: 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard Worker1. End of file on input is processed as the command 'EOF'. 6*cda5da8dSAndroid Build Coastguard Worker2. A command is parsed out of each line by collecting the prefix composed 7*cda5da8dSAndroid Build Coastguard Worker of characters in the identchars member. 8*cda5da8dSAndroid Build Coastguard Worker3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method 9*cda5da8dSAndroid Build Coastguard Worker is passed a single argument consisting of the remainder of the line. 10*cda5da8dSAndroid Build Coastguard Worker4. Typing an empty line repeats the last command. (Actually, it calls the 11*cda5da8dSAndroid Build Coastguard Worker method `emptyline', which may be overridden in a subclass.) 12*cda5da8dSAndroid Build Coastguard Worker5. There is a predefined `help' method. Given an argument `topic', it 13*cda5da8dSAndroid Build Coastguard Worker calls the command `help_topic'. With no arguments, it lists all topics 14*cda5da8dSAndroid Build Coastguard Worker with defined help_ functions, broken into up to three topics; documented 15*cda5da8dSAndroid Build Coastguard Worker commands, miscellaneous help topics, and undocumented commands. 16*cda5da8dSAndroid Build Coastguard Worker6. The command '?' is a synonym for `help'. The command '!' is a synonym 17*cda5da8dSAndroid Build Coastguard Worker for `shell', if a do_shell method exists. 18*cda5da8dSAndroid Build Coastguard Worker7. If completion is enabled, completing commands will be done automatically, 19*cda5da8dSAndroid Build Coastguard Worker and completing of commands args is done by calling complete_foo() with 20*cda5da8dSAndroid Build Coastguard Worker arguments text, line, begidx, endidx. text is string we are matching 21*cda5da8dSAndroid Build Coastguard Worker against, all returned matches must begin with it. line is the current 22*cda5da8dSAndroid Build Coastguard Worker input line (lstripped), begidx and endidx are the beginning and end 23*cda5da8dSAndroid Build Coastguard Worker indexes of the text being matched, which could be used to provide 24*cda5da8dSAndroid Build Coastguard Worker different completion depending upon which position the argument is in. 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard WorkerThe `default' method may be overridden to intercept commands for which there 27*cda5da8dSAndroid Build Coastguard Workeris no do_ method. 28*cda5da8dSAndroid Build Coastguard Worker 29*cda5da8dSAndroid Build Coastguard WorkerThe `completedefault' method may be overridden to intercept completions for 30*cda5da8dSAndroid Build Coastguard Workercommands that have no complete_ method. 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard WorkerThe data member `self.ruler' sets the character used to draw separator lines 33*cda5da8dSAndroid Build Coastguard Workerin the help messages. If empty, no ruler line is drawn. It defaults to "=". 34*cda5da8dSAndroid Build Coastguard Worker 35*cda5da8dSAndroid Build Coastguard WorkerIf the value of `self.intro' is nonempty when the cmdloop method is called, 36*cda5da8dSAndroid Build Coastguard Workerit is printed out on interpreter startup. This value may be overridden 37*cda5da8dSAndroid Build Coastguard Workervia an optional argument to the cmdloop() method. 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard WorkerThe data members `self.doc_header', `self.misc_header', and 40*cda5da8dSAndroid Build Coastguard Worker`self.undoc_header' set the headers used for the help function's 41*cda5da8dSAndroid Build Coastguard Workerlistings of documented functions, miscellaneous topics, and undocumented 42*cda5da8dSAndroid Build Coastguard Workerfunctions respectively. 43*cda5da8dSAndroid Build Coastguard Worker""" 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Workerimport string, sys 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker__all__ = ["Cmd"] 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard WorkerPROMPT = '(Cmd) ' 50*cda5da8dSAndroid Build Coastguard WorkerIDENTCHARS = string.ascii_letters + string.digits + '_' 51*cda5da8dSAndroid Build Coastguard Worker 52*cda5da8dSAndroid Build Coastguard Workerclass Cmd: 53*cda5da8dSAndroid Build Coastguard Worker """A simple framework for writing line-oriented command interpreters. 54*cda5da8dSAndroid Build Coastguard Worker 55*cda5da8dSAndroid Build Coastguard Worker These are often useful for test harnesses, administrative tools, and 56*cda5da8dSAndroid Build Coastguard Worker prototypes that will later be wrapped in a more sophisticated interface. 57*cda5da8dSAndroid Build Coastguard Worker 58*cda5da8dSAndroid Build Coastguard Worker A Cmd instance or subclass instance is a line-oriented interpreter 59*cda5da8dSAndroid Build Coastguard Worker framework. There is no good reason to instantiate Cmd itself; rather, 60*cda5da8dSAndroid Build Coastguard Worker it's useful as a superclass of an interpreter class you define yourself 61*cda5da8dSAndroid Build Coastguard Worker in order to inherit Cmd's methods and encapsulate action methods. 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker """ 64*cda5da8dSAndroid Build Coastguard Worker prompt = PROMPT 65*cda5da8dSAndroid Build Coastguard Worker identchars = IDENTCHARS 66*cda5da8dSAndroid Build Coastguard Worker ruler = '=' 67*cda5da8dSAndroid Build Coastguard Worker lastcmd = '' 68*cda5da8dSAndroid Build Coastguard Worker intro = None 69*cda5da8dSAndroid Build Coastguard Worker doc_leader = "" 70*cda5da8dSAndroid Build Coastguard Worker doc_header = "Documented commands (type help <topic>):" 71*cda5da8dSAndroid Build Coastguard Worker misc_header = "Miscellaneous help topics:" 72*cda5da8dSAndroid Build Coastguard Worker undoc_header = "Undocumented commands:" 73*cda5da8dSAndroid Build Coastguard Worker nohelp = "*** No help on %s" 74*cda5da8dSAndroid Build Coastguard Worker use_rawinput = 1 75*cda5da8dSAndroid Build Coastguard Worker 76*cda5da8dSAndroid Build Coastguard Worker def __init__(self, completekey='tab', stdin=None, stdout=None): 77*cda5da8dSAndroid Build Coastguard Worker """Instantiate a line-oriented interpreter framework. 78*cda5da8dSAndroid Build Coastguard Worker 79*cda5da8dSAndroid Build Coastguard Worker The optional argument 'completekey' is the readline name of a 80*cda5da8dSAndroid Build Coastguard Worker completion key; it defaults to the Tab key. If completekey is 81*cda5da8dSAndroid Build Coastguard Worker not None and the readline module is available, command completion 82*cda5da8dSAndroid Build Coastguard Worker is done automatically. The optional arguments stdin and stdout 83*cda5da8dSAndroid Build Coastguard Worker specify alternate input and output file objects; if not specified, 84*cda5da8dSAndroid Build Coastguard Worker sys.stdin and sys.stdout are used. 85*cda5da8dSAndroid Build Coastguard Worker 86*cda5da8dSAndroid Build Coastguard Worker """ 87*cda5da8dSAndroid Build Coastguard Worker if stdin is not None: 88*cda5da8dSAndroid Build Coastguard Worker self.stdin = stdin 89*cda5da8dSAndroid Build Coastguard Worker else: 90*cda5da8dSAndroid Build Coastguard Worker self.stdin = sys.stdin 91*cda5da8dSAndroid Build Coastguard Worker if stdout is not None: 92*cda5da8dSAndroid Build Coastguard Worker self.stdout = stdout 93*cda5da8dSAndroid Build Coastguard Worker else: 94*cda5da8dSAndroid Build Coastguard Worker self.stdout = sys.stdout 95*cda5da8dSAndroid Build Coastguard Worker self.cmdqueue = [] 96*cda5da8dSAndroid Build Coastguard Worker self.completekey = completekey 97*cda5da8dSAndroid Build Coastguard Worker 98*cda5da8dSAndroid Build Coastguard Worker def cmdloop(self, intro=None): 99*cda5da8dSAndroid Build Coastguard Worker """Repeatedly issue a prompt, accept input, parse an initial prefix 100*cda5da8dSAndroid Build Coastguard Worker off the received input, and dispatch to action methods, passing them 101*cda5da8dSAndroid Build Coastguard Worker the remainder of the line as argument. 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker """ 104*cda5da8dSAndroid Build Coastguard Worker 105*cda5da8dSAndroid Build Coastguard Worker self.preloop() 106*cda5da8dSAndroid Build Coastguard Worker if self.use_rawinput and self.completekey: 107*cda5da8dSAndroid Build Coastguard Worker try: 108*cda5da8dSAndroid Build Coastguard Worker import readline 109*cda5da8dSAndroid Build Coastguard Worker self.old_completer = readline.get_completer() 110*cda5da8dSAndroid Build Coastguard Worker readline.set_completer(self.complete) 111*cda5da8dSAndroid Build Coastguard Worker readline.parse_and_bind(self.completekey+": complete") 112*cda5da8dSAndroid Build Coastguard Worker except ImportError: 113*cda5da8dSAndroid Build Coastguard Worker pass 114*cda5da8dSAndroid Build Coastguard Worker try: 115*cda5da8dSAndroid Build Coastguard Worker if intro is not None: 116*cda5da8dSAndroid Build Coastguard Worker self.intro = intro 117*cda5da8dSAndroid Build Coastguard Worker if self.intro: 118*cda5da8dSAndroid Build Coastguard Worker self.stdout.write(str(self.intro)+"\n") 119*cda5da8dSAndroid Build Coastguard Worker stop = None 120*cda5da8dSAndroid Build Coastguard Worker while not stop: 121*cda5da8dSAndroid Build Coastguard Worker if self.cmdqueue: 122*cda5da8dSAndroid Build Coastguard Worker line = self.cmdqueue.pop(0) 123*cda5da8dSAndroid Build Coastguard Worker else: 124*cda5da8dSAndroid Build Coastguard Worker if self.use_rawinput: 125*cda5da8dSAndroid Build Coastguard Worker try: 126*cda5da8dSAndroid Build Coastguard Worker line = input(self.prompt) 127*cda5da8dSAndroid Build Coastguard Worker except EOFError: 128*cda5da8dSAndroid Build Coastguard Worker line = 'EOF' 129*cda5da8dSAndroid Build Coastguard Worker else: 130*cda5da8dSAndroid Build Coastguard Worker self.stdout.write(self.prompt) 131*cda5da8dSAndroid Build Coastguard Worker self.stdout.flush() 132*cda5da8dSAndroid Build Coastguard Worker line = self.stdin.readline() 133*cda5da8dSAndroid Build Coastguard Worker if not len(line): 134*cda5da8dSAndroid Build Coastguard Worker line = 'EOF' 135*cda5da8dSAndroid Build Coastguard Worker else: 136*cda5da8dSAndroid Build Coastguard Worker line = line.rstrip('\r\n') 137*cda5da8dSAndroid Build Coastguard Worker line = self.precmd(line) 138*cda5da8dSAndroid Build Coastguard Worker stop = self.onecmd(line) 139*cda5da8dSAndroid Build Coastguard Worker stop = self.postcmd(stop, line) 140*cda5da8dSAndroid Build Coastguard Worker self.postloop() 141*cda5da8dSAndroid Build Coastguard Worker finally: 142*cda5da8dSAndroid Build Coastguard Worker if self.use_rawinput and self.completekey: 143*cda5da8dSAndroid Build Coastguard Worker try: 144*cda5da8dSAndroid Build Coastguard Worker import readline 145*cda5da8dSAndroid Build Coastguard Worker readline.set_completer(self.old_completer) 146*cda5da8dSAndroid Build Coastguard Worker except ImportError: 147*cda5da8dSAndroid Build Coastguard Worker pass 148*cda5da8dSAndroid Build Coastguard Worker 149*cda5da8dSAndroid Build Coastguard Worker 150*cda5da8dSAndroid Build Coastguard Worker def precmd(self, line): 151*cda5da8dSAndroid Build Coastguard Worker """Hook method executed just before the command line is 152*cda5da8dSAndroid Build Coastguard Worker interpreted, but after the input prompt is generated and issued. 153*cda5da8dSAndroid Build Coastguard Worker 154*cda5da8dSAndroid Build Coastguard Worker """ 155*cda5da8dSAndroid Build Coastguard Worker return line 156*cda5da8dSAndroid Build Coastguard Worker 157*cda5da8dSAndroid Build Coastguard Worker def postcmd(self, stop, line): 158*cda5da8dSAndroid Build Coastguard Worker """Hook method executed just after a command dispatch is finished.""" 159*cda5da8dSAndroid Build Coastguard Worker return stop 160*cda5da8dSAndroid Build Coastguard Worker 161*cda5da8dSAndroid Build Coastguard Worker def preloop(self): 162*cda5da8dSAndroid Build Coastguard Worker """Hook method executed once when the cmdloop() method is called.""" 163*cda5da8dSAndroid Build Coastguard Worker pass 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker def postloop(self): 166*cda5da8dSAndroid Build Coastguard Worker """Hook method executed once when the cmdloop() method is about to 167*cda5da8dSAndroid Build Coastguard Worker return. 168*cda5da8dSAndroid Build Coastguard Worker 169*cda5da8dSAndroid Build Coastguard Worker """ 170*cda5da8dSAndroid Build Coastguard Worker pass 171*cda5da8dSAndroid Build Coastguard Worker 172*cda5da8dSAndroid Build Coastguard Worker def parseline(self, line): 173*cda5da8dSAndroid Build Coastguard Worker """Parse the line into a command name and a string containing 174*cda5da8dSAndroid Build Coastguard Worker the arguments. Returns a tuple containing (command, args, line). 175*cda5da8dSAndroid Build Coastguard Worker 'command' and 'args' may be None if the line couldn't be parsed. 176*cda5da8dSAndroid Build Coastguard Worker """ 177*cda5da8dSAndroid Build Coastguard Worker line = line.strip() 178*cda5da8dSAndroid Build Coastguard Worker if not line: 179*cda5da8dSAndroid Build Coastguard Worker return None, None, line 180*cda5da8dSAndroid Build Coastguard Worker elif line[0] == '?': 181*cda5da8dSAndroid Build Coastguard Worker line = 'help ' + line[1:] 182*cda5da8dSAndroid Build Coastguard Worker elif line[0] == '!': 183*cda5da8dSAndroid Build Coastguard Worker if hasattr(self, 'do_shell'): 184*cda5da8dSAndroid Build Coastguard Worker line = 'shell ' + line[1:] 185*cda5da8dSAndroid Build Coastguard Worker else: 186*cda5da8dSAndroid Build Coastguard Worker return None, None, line 187*cda5da8dSAndroid Build Coastguard Worker i, n = 0, len(line) 188*cda5da8dSAndroid Build Coastguard Worker while i < n and line[i] in self.identchars: i = i+1 189*cda5da8dSAndroid Build Coastguard Worker cmd, arg = line[:i], line[i:].strip() 190*cda5da8dSAndroid Build Coastguard Worker return cmd, arg, line 191*cda5da8dSAndroid Build Coastguard Worker 192*cda5da8dSAndroid Build Coastguard Worker def onecmd(self, line): 193*cda5da8dSAndroid Build Coastguard Worker """Interpret the argument as though it had been typed in response 194*cda5da8dSAndroid Build Coastguard Worker to the prompt. 195*cda5da8dSAndroid Build Coastguard Worker 196*cda5da8dSAndroid Build Coastguard Worker This may be overridden, but should not normally need to be; 197*cda5da8dSAndroid Build Coastguard Worker see the precmd() and postcmd() methods for useful execution hooks. 198*cda5da8dSAndroid Build Coastguard Worker The return value is a flag indicating whether interpretation of 199*cda5da8dSAndroid Build Coastguard Worker commands by the interpreter should stop. 200*cda5da8dSAndroid Build Coastguard Worker 201*cda5da8dSAndroid Build Coastguard Worker """ 202*cda5da8dSAndroid Build Coastguard Worker cmd, arg, line = self.parseline(line) 203*cda5da8dSAndroid Build Coastguard Worker if not line: 204*cda5da8dSAndroid Build Coastguard Worker return self.emptyline() 205*cda5da8dSAndroid Build Coastguard Worker if cmd is None: 206*cda5da8dSAndroid Build Coastguard Worker return self.default(line) 207*cda5da8dSAndroid Build Coastguard Worker self.lastcmd = line 208*cda5da8dSAndroid Build Coastguard Worker if line == 'EOF' : 209*cda5da8dSAndroid Build Coastguard Worker self.lastcmd = '' 210*cda5da8dSAndroid Build Coastguard Worker if cmd == '': 211*cda5da8dSAndroid Build Coastguard Worker return self.default(line) 212*cda5da8dSAndroid Build Coastguard Worker else: 213*cda5da8dSAndroid Build Coastguard Worker try: 214*cda5da8dSAndroid Build Coastguard Worker func = getattr(self, 'do_' + cmd) 215*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 216*cda5da8dSAndroid Build Coastguard Worker return self.default(line) 217*cda5da8dSAndroid Build Coastguard Worker return func(arg) 218*cda5da8dSAndroid Build Coastguard Worker 219*cda5da8dSAndroid Build Coastguard Worker def emptyline(self): 220*cda5da8dSAndroid Build Coastguard Worker """Called when an empty line is entered in response to the prompt. 221*cda5da8dSAndroid Build Coastguard Worker 222*cda5da8dSAndroid Build Coastguard Worker If this method is not overridden, it repeats the last nonempty 223*cda5da8dSAndroid Build Coastguard Worker command entered. 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker """ 226*cda5da8dSAndroid Build Coastguard Worker if self.lastcmd: 227*cda5da8dSAndroid Build Coastguard Worker return self.onecmd(self.lastcmd) 228*cda5da8dSAndroid Build Coastguard Worker 229*cda5da8dSAndroid Build Coastguard Worker def default(self, line): 230*cda5da8dSAndroid Build Coastguard Worker """Called on an input line when the command prefix is not recognized. 231*cda5da8dSAndroid Build Coastguard Worker 232*cda5da8dSAndroid Build Coastguard Worker If this method is not overridden, it prints an error message and 233*cda5da8dSAndroid Build Coastguard Worker returns. 234*cda5da8dSAndroid Build Coastguard Worker 235*cda5da8dSAndroid Build Coastguard Worker """ 236*cda5da8dSAndroid Build Coastguard Worker self.stdout.write('*** Unknown syntax: %s\n'%line) 237*cda5da8dSAndroid Build Coastguard Worker 238*cda5da8dSAndroid Build Coastguard Worker def completedefault(self, *ignored): 239*cda5da8dSAndroid Build Coastguard Worker """Method called to complete an input line when no command-specific 240*cda5da8dSAndroid Build Coastguard Worker complete_*() method is available. 241*cda5da8dSAndroid Build Coastguard Worker 242*cda5da8dSAndroid Build Coastguard Worker By default, it returns an empty list. 243*cda5da8dSAndroid Build Coastguard Worker 244*cda5da8dSAndroid Build Coastguard Worker """ 245*cda5da8dSAndroid Build Coastguard Worker return [] 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker def completenames(self, text, *ignored): 248*cda5da8dSAndroid Build Coastguard Worker dotext = 'do_'+text 249*cda5da8dSAndroid Build Coastguard Worker return [a[3:] for a in self.get_names() if a.startswith(dotext)] 250*cda5da8dSAndroid Build Coastguard Worker 251*cda5da8dSAndroid Build Coastguard Worker def complete(self, text, state): 252*cda5da8dSAndroid Build Coastguard Worker """Return the next possible completion for 'text'. 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Worker If a command has not been entered, then complete against command list. 255*cda5da8dSAndroid Build Coastguard Worker Otherwise try to call complete_<command> to get list of completions. 256*cda5da8dSAndroid Build Coastguard Worker """ 257*cda5da8dSAndroid Build Coastguard Worker if state == 0: 258*cda5da8dSAndroid Build Coastguard Worker import readline 259*cda5da8dSAndroid Build Coastguard Worker origline = readline.get_line_buffer() 260*cda5da8dSAndroid Build Coastguard Worker line = origline.lstrip() 261*cda5da8dSAndroid Build Coastguard Worker stripped = len(origline) - len(line) 262*cda5da8dSAndroid Build Coastguard Worker begidx = readline.get_begidx() - stripped 263*cda5da8dSAndroid Build Coastguard Worker endidx = readline.get_endidx() - stripped 264*cda5da8dSAndroid Build Coastguard Worker if begidx>0: 265*cda5da8dSAndroid Build Coastguard Worker cmd, args, foo = self.parseline(line) 266*cda5da8dSAndroid Build Coastguard Worker if cmd == '': 267*cda5da8dSAndroid Build Coastguard Worker compfunc = self.completedefault 268*cda5da8dSAndroid Build Coastguard Worker else: 269*cda5da8dSAndroid Build Coastguard Worker try: 270*cda5da8dSAndroid Build Coastguard Worker compfunc = getattr(self, 'complete_' + cmd) 271*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 272*cda5da8dSAndroid Build Coastguard Worker compfunc = self.completedefault 273*cda5da8dSAndroid Build Coastguard Worker else: 274*cda5da8dSAndroid Build Coastguard Worker compfunc = self.completenames 275*cda5da8dSAndroid Build Coastguard Worker self.completion_matches = compfunc(text, line, begidx, endidx) 276*cda5da8dSAndroid Build Coastguard Worker try: 277*cda5da8dSAndroid Build Coastguard Worker return self.completion_matches[state] 278*cda5da8dSAndroid Build Coastguard Worker except IndexError: 279*cda5da8dSAndroid Build Coastguard Worker return None 280*cda5da8dSAndroid Build Coastguard Worker 281*cda5da8dSAndroid Build Coastguard Worker def get_names(self): 282*cda5da8dSAndroid Build Coastguard Worker # This method used to pull in base class attributes 283*cda5da8dSAndroid Build Coastguard Worker # at a time dir() didn't do it yet. 284*cda5da8dSAndroid Build Coastguard Worker return dir(self.__class__) 285*cda5da8dSAndroid Build Coastguard Worker 286*cda5da8dSAndroid Build Coastguard Worker def complete_help(self, *args): 287*cda5da8dSAndroid Build Coastguard Worker commands = set(self.completenames(*args)) 288*cda5da8dSAndroid Build Coastguard Worker topics = set(a[5:] for a in self.get_names() 289*cda5da8dSAndroid Build Coastguard Worker if a.startswith('help_' + args[0])) 290*cda5da8dSAndroid Build Coastguard Worker return list(commands | topics) 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker def do_help(self, arg): 293*cda5da8dSAndroid Build Coastguard Worker 'List available commands with "help" or detailed help with "help cmd".' 294*cda5da8dSAndroid Build Coastguard Worker if arg: 295*cda5da8dSAndroid Build Coastguard Worker # XXX check arg syntax 296*cda5da8dSAndroid Build Coastguard Worker try: 297*cda5da8dSAndroid Build Coastguard Worker func = getattr(self, 'help_' + arg) 298*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 299*cda5da8dSAndroid Build Coastguard Worker try: 300*cda5da8dSAndroid Build Coastguard Worker doc=getattr(self, 'do_' + arg).__doc__ 301*cda5da8dSAndroid Build Coastguard Worker if doc: 302*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(doc)) 303*cda5da8dSAndroid Build Coastguard Worker return 304*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 305*cda5da8dSAndroid Build Coastguard Worker pass 306*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(self.nohelp % (arg,))) 307*cda5da8dSAndroid Build Coastguard Worker return 308*cda5da8dSAndroid Build Coastguard Worker func() 309*cda5da8dSAndroid Build Coastguard Worker else: 310*cda5da8dSAndroid Build Coastguard Worker names = self.get_names() 311*cda5da8dSAndroid Build Coastguard Worker cmds_doc = [] 312*cda5da8dSAndroid Build Coastguard Worker cmds_undoc = [] 313*cda5da8dSAndroid Build Coastguard Worker topics = set() 314*cda5da8dSAndroid Build Coastguard Worker for name in names: 315*cda5da8dSAndroid Build Coastguard Worker if name[:5] == 'help_': 316*cda5da8dSAndroid Build Coastguard Worker topics.add(name[5:]) 317*cda5da8dSAndroid Build Coastguard Worker names.sort() 318*cda5da8dSAndroid Build Coastguard Worker # There can be duplicates if routines overridden 319*cda5da8dSAndroid Build Coastguard Worker prevname = '' 320*cda5da8dSAndroid Build Coastguard Worker for name in names: 321*cda5da8dSAndroid Build Coastguard Worker if name[:3] == 'do_': 322*cda5da8dSAndroid Build Coastguard Worker if name == prevname: 323*cda5da8dSAndroid Build Coastguard Worker continue 324*cda5da8dSAndroid Build Coastguard Worker prevname = name 325*cda5da8dSAndroid Build Coastguard Worker cmd=name[3:] 326*cda5da8dSAndroid Build Coastguard Worker if cmd in topics: 327*cda5da8dSAndroid Build Coastguard Worker cmds_doc.append(cmd) 328*cda5da8dSAndroid Build Coastguard Worker topics.remove(cmd) 329*cda5da8dSAndroid Build Coastguard Worker elif getattr(self, name).__doc__: 330*cda5da8dSAndroid Build Coastguard Worker cmds_doc.append(cmd) 331*cda5da8dSAndroid Build Coastguard Worker else: 332*cda5da8dSAndroid Build Coastguard Worker cmds_undoc.append(cmd) 333*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(self.doc_leader)) 334*cda5da8dSAndroid Build Coastguard Worker self.print_topics(self.doc_header, cmds_doc, 15,80) 335*cda5da8dSAndroid Build Coastguard Worker self.print_topics(self.misc_header, sorted(topics),15,80) 336*cda5da8dSAndroid Build Coastguard Worker self.print_topics(self.undoc_header, cmds_undoc, 15,80) 337*cda5da8dSAndroid Build Coastguard Worker 338*cda5da8dSAndroid Build Coastguard Worker def print_topics(self, header, cmds, cmdlen, maxcol): 339*cda5da8dSAndroid Build Coastguard Worker if cmds: 340*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(header)) 341*cda5da8dSAndroid Build Coastguard Worker if self.ruler: 342*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(self.ruler * len(header))) 343*cda5da8dSAndroid Build Coastguard Worker self.columnize(cmds, maxcol-1) 344*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("\n") 345*cda5da8dSAndroid Build Coastguard Worker 346*cda5da8dSAndroid Build Coastguard Worker def columnize(self, list, displaywidth=80): 347*cda5da8dSAndroid Build Coastguard Worker """Display a list of strings as a compact set of columns. 348*cda5da8dSAndroid Build Coastguard Worker 349*cda5da8dSAndroid Build Coastguard Worker Each column is only as wide as necessary. 350*cda5da8dSAndroid Build Coastguard Worker Columns are separated by two spaces (one was not legible enough). 351*cda5da8dSAndroid Build Coastguard Worker """ 352*cda5da8dSAndroid Build Coastguard Worker if not list: 353*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("<empty>\n") 354*cda5da8dSAndroid Build Coastguard Worker return 355*cda5da8dSAndroid Build Coastguard Worker 356*cda5da8dSAndroid Build Coastguard Worker nonstrings = [i for i in range(len(list)) 357*cda5da8dSAndroid Build Coastguard Worker if not isinstance(list[i], str)] 358*cda5da8dSAndroid Build Coastguard Worker if nonstrings: 359*cda5da8dSAndroid Build Coastguard Worker raise TypeError("list[i] not a string for i in %s" 360*cda5da8dSAndroid Build Coastguard Worker % ", ".join(map(str, nonstrings))) 361*cda5da8dSAndroid Build Coastguard Worker size = len(list) 362*cda5da8dSAndroid Build Coastguard Worker if size == 1: 363*cda5da8dSAndroid Build Coastguard Worker self.stdout.write('%s\n'%str(list[0])) 364*cda5da8dSAndroid Build Coastguard Worker return 365*cda5da8dSAndroid Build Coastguard Worker # Try every row count from 1 upwards 366*cda5da8dSAndroid Build Coastguard Worker for nrows in range(1, len(list)): 367*cda5da8dSAndroid Build Coastguard Worker ncols = (size+nrows-1) // nrows 368*cda5da8dSAndroid Build Coastguard Worker colwidths = [] 369*cda5da8dSAndroid Build Coastguard Worker totwidth = -2 370*cda5da8dSAndroid Build Coastguard Worker for col in range(ncols): 371*cda5da8dSAndroid Build Coastguard Worker colwidth = 0 372*cda5da8dSAndroid Build Coastguard Worker for row in range(nrows): 373*cda5da8dSAndroid Build Coastguard Worker i = row + nrows*col 374*cda5da8dSAndroid Build Coastguard Worker if i >= size: 375*cda5da8dSAndroid Build Coastguard Worker break 376*cda5da8dSAndroid Build Coastguard Worker x = list[i] 377*cda5da8dSAndroid Build Coastguard Worker colwidth = max(colwidth, len(x)) 378*cda5da8dSAndroid Build Coastguard Worker colwidths.append(colwidth) 379*cda5da8dSAndroid Build Coastguard Worker totwidth += colwidth + 2 380*cda5da8dSAndroid Build Coastguard Worker if totwidth > displaywidth: 381*cda5da8dSAndroid Build Coastguard Worker break 382*cda5da8dSAndroid Build Coastguard Worker if totwidth <= displaywidth: 383*cda5da8dSAndroid Build Coastguard Worker break 384*cda5da8dSAndroid Build Coastguard Worker else: 385*cda5da8dSAndroid Build Coastguard Worker nrows = len(list) 386*cda5da8dSAndroid Build Coastguard Worker ncols = 1 387*cda5da8dSAndroid Build Coastguard Worker colwidths = [0] 388*cda5da8dSAndroid Build Coastguard Worker for row in range(nrows): 389*cda5da8dSAndroid Build Coastguard Worker texts = [] 390*cda5da8dSAndroid Build Coastguard Worker for col in range(ncols): 391*cda5da8dSAndroid Build Coastguard Worker i = row + nrows*col 392*cda5da8dSAndroid Build Coastguard Worker if i >= size: 393*cda5da8dSAndroid Build Coastguard Worker x = "" 394*cda5da8dSAndroid Build Coastguard Worker else: 395*cda5da8dSAndroid Build Coastguard Worker x = list[i] 396*cda5da8dSAndroid Build Coastguard Worker texts.append(x) 397*cda5da8dSAndroid Build Coastguard Worker while texts and not texts[-1]: 398*cda5da8dSAndroid Build Coastguard Worker del texts[-1] 399*cda5da8dSAndroid Build Coastguard Worker for col in range(len(texts)): 400*cda5da8dSAndroid Build Coastguard Worker texts[col] = texts[col].ljust(colwidths[col]) 401*cda5da8dSAndroid Build Coastguard Worker self.stdout.write("%s\n"%str(" ".join(texts))) 402