1import stat 2import sys 3import os.path 4import MenuMaker 5import Prophet 6 7from Keywords import Keyword as Kw, Set as KwS 8 9import optparse 10from optparse import OptionParser as _OP 11 12from MenuMaker import msg, warn, fatal, fronts, terms 13 14# Output the generated menu to stdout instead of the menu file 15writeToStdout = False 16forceWrite = False # Overwrite existing files 17noDesktop = False # Do not scan for .desktop files 18noLegacy = False # Do not scan for the legacy apps 19noDebian = False # Exclude Debian apps database 20 21vStr = "be verbose" 22 23 24def vOpt(option, opt, value, parser): 25 v = MenuMaker.verbose 26 if v < 3: 27 v += 1 28 MenuMaker.verbose = Prophet.verbose = v 29 30 31fStr = "overwrite existing files" 32 33 34def fOpt(option, opt, value, parser): 35 global forceWrite 36 forceWrite = True 37 38cStr = "dump the menu to stdout instead of file" 39 40 41def cOpt(option, opt, value, parser): 42 global writeToStdout 43 writeToStdout = True 44 45iStr = "produce the output suitable for inclusion into the custom menu; MUST be used in conjunction with -c" 46 47 48def iOpt(option, opt, value, parser): 49 MenuMaker.writeFullMenu = False 50 51pStr = "always retain the path to executable (by default, the path is omitted whenever possible)" 52 53 54def pOpt(option, opt, value, parser): 55 Prophet.omitExecPath = False 56 57xdStr = "skip .desktop database scanning" 58 59 60def xdOpt(option, opt, value, parser): 61 global noDesktop 62 noDesktop = True 63 64xlStr = "skip legacy knowledge base scanning" 65 66 67def xlOpt(option, opt, value, parser): 68 global noLegacy 69 noLegacy = True 70 71xeStr = "skip Debian database scanning" 72 73 74def xeOpt(option, opt, value, parser): 75 global noDebian 76 noDebian = True 77 78sKwS = KwS(*Prophet.skip.keys()) 79sStr = "skip specific categories (comma-separated list)" 80 81 82def sOpt(option, opt, value, parser): 83 for x in value.split(","): 84 if not len(x): 85 continue 86 x = Kw(x) 87 try: 88 Prophet.skip[x] 89 except KeyError: 90 raise optparse.OptionValueError( 91 "wrong argument %s for option %s" % (x, opt)) 92 Prophet.skip[x] = True 93 94 95tStr = "terminal emulator to use for console applications" 96 97 98def tOpt(option, opt, value, parser): 99 global term 100 for t, n in terms: 101 if value in n: 102 term = t 103 return 104 105 raise optparse.OptionValueError( 106 "wrong argument %s for option %s" % (value, opt)) 107 108 109class OP(_OP): 110 111 def print_help(self): 112 sys.stdout.write( 113 "This is %s %s, a 100%% Python heuristics-driven menu generator\n" % 114 (MenuMaker.pkgName, MenuMaker.pkgVer)) 115 sys.stdout.write( 116 "%s %s %s <%s>\n\n" % 117 (MenuMaker.pkgHome, 118 MenuMaker.copyright, 119 MenuMaker.author, 120 MenuMaker.email)) 121 _OP.print_help(self) 122 sys.stdout.write("\nfrontends (case insensitive):\n") 123 for v in fronts.values(): 124 vars = list(v) 125 s = str(vars[0]) 126 for x in vars[1:]: 127 s += " | " + str(x) 128 sys.stdout.write(" %s\n" % s) 129 sys.stdout.write( 130 "\nterminal emulators for -t (case insensitive), in order of decreasing preference:\n") 131 for t, n in terms: 132 vars = list(n) 133 s = str(vars[0]) 134 for x in vars[1:]: 135 s += " | " + str(x) 136 sys.stdout.write(" %s\n" % s) 137 sys.stdout.write("\nskip categories for -s (case insensitive):\n") 138 for k in sKwS: 139 sys.stdout.write(" %s\n" % k) 140 141 142op = OP(usage="%prog [options] frontend", version="%s %s" % 143 (MenuMaker.pkgName, MenuMaker.pkgVer)) 144op.add_option("-v", "--verbose", action="callback", callback=vOpt, help=vStr) 145op.add_option("-f", "--force", action="callback", callback=fOpt, help=fStr) 146op.add_option("-c", "--stdout", action="callback", callback=cOpt, help=cStr) 147op.add_option("-i", action="callback", callback=iOpt, help=iStr) 148op.add_option("-t", metavar="terminal", nargs=1, type="string", 149 action="callback", callback=tOpt, help=tStr) 150op.add_option( 151 "-p", "--retain-path", action="callback", callback=pOpt, help=pStr) 152op.add_option("--no-desktop", action="callback", callback=xdOpt, help=xdStr) 153op.add_option("--no-legacy", action="callback", callback=xlOpt, help=xlStr) 154op.add_option("--no-debian", action="callback", callback=xeOpt, help=xeStr) 155op.add_option("-s", "--skip", metavar="list", nargs=1, 156 type="string", action="callback", callback=sOpt, help=sStr) 157 158opts, args = op.parse_args() 159 160if not len(args): 161 fatal("no frontend specified") 162 163found = False 164for k, v in fronts.items(): 165 if args[0] in v: 166 found = True 167 name = "MenuMaker.%s" % k 168 frontName = k 169 __import__(name) 170 front = sys.modules[name] 171 MenuMaker.setFrontend(front) 172 break 173 174 175if not found: 176 fatal("unknown frontend: %s" % args[0]) 177 178if len(args) > 1: 179 warn("multiple frontends specified; the first one (%s) will be used" % 180 frontName) 181 182try: 183 menuFile = front.menuFile 184 185except AttributeError: 186 if not writeToStdout: 187 fatal( 188 "%s doesn't support writing to a file; use -c option instead" % 189 frontName) 190 191 192if not MenuMaker.writeFullMenu and not writeToStdout: 193 fatal( 194 "-i MUST be used in conjunction with -c; this is done to prevent accidental overwriting of the custom menu") 195 196# Postpone the setup to the time when it's actually needed 197msg("* scanning") 198 199if noDesktop: 200 msg(" skipping desktop") 201 desktop = [] 202else: 203 import Prophet.Desktop 204 desktop = Prophet.Desktop.scan() 205 206if noLegacy: 207 msg(" skipping legacy") 208 legacy = [] 209else: 210 import Prophet.Legacy 211 Prophet.Legacy.setup() 212 legacy = Prophet.Legacy.scan() 213 214if noDebian: 215 msg(" skipping Debian") 216 debian = [] 217else: 218 import Prophet.Debian 219 debian = Prophet.Debian.scan() 220 221merged = Prophet.merge(legacy + desktop + debian) 222 223msg("* generating") 224 225if not Prophet.skip[Prophet.App.Console]: 226 try: 227 MenuMaker.terminal = term() 228 except Prophet.NotSet: 229 fatal("specified terminal emulator couldn't be found; try another one") 230 except NameError: 231 warn(" no terminal emulator specified; will use the default") 232 found = False 233 for tcls, tname in terms: 234 try: 235 MenuMaker.terminal = tcls() 236 found = True 237 break 238 except Prophet.NotSet: 239 pass 240 241 if not found: 242 fatal("no suitable terminal emulator found") 243 244 msg(" using %s as terminal emulator" % MenuMaker.terminal.name) 245 246menu = MenuMaker.Root() 247 248if writeToStdout: 249 msg("* writing") 250 file = sys.stdout 251 252else: 253 msg("* writing to %s" % menuFile) 254 file = os.path.expanduser(menuFile) 255 dir = os.path.dirname(file) 256 if not os.path.isdir(dir): 257 msg(" creating missing directory %s" % dir) 258 os.makedirs(dir) 259 if not forceWrite and os.path.isfile(file): 260 fatal( 261 "refuse to overwrite existing file %s; either delete it or use -f option" % 262 file) 263 file = open(file, "wt") 264 265menu.distribute(merged) 266 267menu.arrange() 268 269for x in menu.emit(0): 270 file.write(x) 271 file.write("\n") 272 273msg("* done") 274