1#! /usr/bin/env python 2 3# Tk man page browser -- currently only shows the Tcl/Tk man pages 4 5import sys 6import os 7import string 8import re 9from Tkinter import * 10from ManPage import ManPage 11 12MANNDIRLIST = ['/depot/sundry/man/mann','/usr/local/man/mann'] 13MAN3DIRLIST = ['/depot/sundry/man/man3','/usr/local/man/man3'] 14 15foundmanndir = 0 16for dir in MANNDIRLIST: 17 if os.path.exists(dir): 18 MANNDIR = dir 19 foundmanndir = 1 20 21foundman3dir = 0 22for dir in MAN3DIRLIST: 23 if os.path.exists(dir): 24 MAN3DIR = dir 25 foundman3dir = 1 26 27if not foundmanndir or not foundman3dir: 28 sys.stderr.write('\n') 29 if not foundmanndir: 30 msg = """\ 31Failed to find mann directory. 32Please add the correct entry to the MANNDIRLIST 33at the top of %s script.""" % \ 34sys.argv[0] 35 sys.stderr.write("%s\n\n" % msg) 36 if not foundman3dir: 37 msg = """\ 38Failed to find man3 directory. 39Please add the correct entry to the MAN3DIRLIST 40at the top of %s script.""" % \ 41sys.argv[0] 42 sys.stderr.write("%s\n\n" % msg) 43 sys.exit(1) 44 45del foundmanndir 46del foundman3dir 47 48def listmanpages(mandir): 49 files = os.listdir(mandir) 50 names = [] 51 for file in files: 52 if file[-2:-1] == '.' and (file[-1] in 'ln123456789'): 53 names.append(file[:-2]) 54 names.sort() 55 return names 56 57class SelectionBox: 58 59 def __init__(self, master=None): 60 self.choices = [] 61 62 self.frame = Frame(master, name="frame") 63 self.frame.pack(expand=1, fill=BOTH) 64 self.master = self.frame.master 65 self.subframe = Frame(self.frame, name="subframe") 66 self.subframe.pack(expand=0, fill=BOTH) 67 self.leftsubframe = Frame(self.subframe, name='leftsubframe') 68 self.leftsubframe.pack(side=LEFT, expand=1, fill=BOTH) 69 self.rightsubframe = Frame(self.subframe, name='rightsubframe') 70 self.rightsubframe.pack(side=RIGHT, expand=1, fill=BOTH) 71 self.chaptervar = StringVar(master) 72 self.chapter = Menubutton(self.rightsubframe, name='chapter', 73 text='Directory', relief=RAISED, 74 borderwidth=2) 75 self.chapter.pack(side=TOP) 76 self.chaptermenu = Menu(self.chapter, name='chaptermenu') 77 self.chaptermenu.add_radiobutton(label='C functions', 78 value=MAN3DIR, 79 variable=self.chaptervar, 80 command=self.newchapter) 81 self.chaptermenu.add_radiobutton(label='Tcl/Tk functions', 82 value=MANNDIR, 83 variable=self.chaptervar, 84 command=self.newchapter) 85 self.chapter['menu'] = self.chaptermenu 86 self.listbox = Listbox(self.rightsubframe, name='listbox', 87 relief=SUNKEN, borderwidth=2, 88 width=20, height=5) 89 self.listbox.pack(expand=1, fill=BOTH) 90 self.l1 = Button(self.leftsubframe, name='l1', 91 text='Display manual page named:', 92 command=self.entry_cb) 93 self.l1.pack(side=TOP) 94 self.entry = Entry(self.leftsubframe, name='entry', 95 relief=SUNKEN, borderwidth=2, 96 width=20) 97 self.entry.pack(expand=0, fill=X) 98 self.l2frame = Frame(self.leftsubframe, name='l2frame') 99 self.l2frame.pack(expand=0, fill=NONE) 100 self.l2 = Button(self.l2frame, name='l2', 101 text='Search regexp:', 102 command=self.search_cb) 103 self.l2.pack(side=LEFT) 104 self.casevar = BooleanVar() 105 self.casesense = Checkbutton(self.l2frame, name='casesense', 106 text='Case sensitive', 107 variable=self.casevar, 108 relief=FLAT) 109 self.casesense.pack(side=LEFT) 110 self.search = Entry(self.leftsubframe, name='search', 111 relief=SUNKEN, borderwidth=2, 112 width=20) 113 self.search.pack(expand=0, fill=X) 114 self.title = Label(self.leftsubframe, name='title', 115 text='(none)') 116 self.title.pack(side=BOTTOM) 117 self.text = ManPage(self.frame, name='text', 118 relief=SUNKEN, borderwidth=2, 119 wrap=NONE, width=72, 120 selectbackground='pink') 121 self.text.pack(expand=1, fill=BOTH) 122 123 self.entry.bind('<Return>', self.entry_cb) 124 self.search.bind('<Return>', self.search_cb) 125 self.listbox.bind('<Double-1>', self.listbox_cb) 126 127 self.entry.bind('<Tab>', self.entry_tab) 128 self.search.bind('<Tab>', self.search_tab) 129 self.text.bind('<Tab>', self.text_tab) 130 131 self.entry.focus_set() 132 133 self.chaptervar.set(MANNDIR) 134 self.newchapter() 135 136 def newchapter(self): 137 mandir = self.chaptervar.get() 138 self.choices = [] 139 self.addlist(listmanpages(mandir)) 140 141 def addchoice(self, choice): 142 if choice not in self.choices: 143 self.choices.append(choice) 144 self.choices.sort() 145 self.update() 146 147 def addlist(self, list): 148 self.choices[len(self.choices):] = list 149 self.choices.sort() 150 self.update() 151 152 def entry_cb(self, *e): 153 self.update() 154 155 def listbox_cb(self, e): 156 selection = self.listbox.curselection() 157 if selection and len(selection) == 1: 158 name = self.listbox.get(selection[0]) 159 self.show_page(name) 160 161 def search_cb(self, *e): 162 self.search_string(self.search.get()) 163 164 def entry_tab(self, e): 165 self.search.focus_set() 166 167 def search_tab(self, e): 168 self.entry.focus_set() 169 170 def text_tab(self, e): 171 self.entry.focus_set() 172 173 def updatelist(self): 174 key = self.entry.get() 175 ok = filter(lambda name, key=key, n=len(key): name[:n]==key, 176 self.choices) 177 if not ok: 178 self.frame.bell() 179 self.listbox.delete(0, AtEnd()) 180 exactmatch = 0 181 for item in ok: 182 if item == key: exactmatch = 1 183 self.listbox.insert(AtEnd(), item) 184 if exactmatch: 185 return key 186 n = self.listbox.size() 187 if n == 1: 188 return self.listbox.get(0) 189 # Else return None, meaning not a unique selection 190 191 def update(self): 192 name = self.updatelist() 193 if name: 194 self.show_page(name) 195 self.entry.delete(0, AtEnd()) 196 self.updatelist() 197 198 def show_page(self, name): 199 file = '%s/%s.?' % (self.chaptervar.get(), name) 200 fp = os.popen('nroff -man %s | ul -i' % file, 'r') 201 self.text.kill() 202 self.title['text'] = name 203 self.text.parsefile(fp) 204 205 def search_string(self, search): 206 if not search: 207 self.frame.bell() 208 print 'Empty search string' 209 return 210 if not self.casevar.get(): 211 map = re.IGNORECASE 212 else: 213 map = None 214 try: 215 if map: 216 prog = re.compile(search, map) 217 else: 218 prog = re.compile(search) 219 except re.error, msg: 220 self.frame.bell() 221 print 'Regex error:', msg 222 return 223 here = self.text.index(AtInsert()) 224 lineno = string.atoi(here[:string.find(here, '.')]) 225 end = self.text.index(AtEnd()) 226 endlineno = string.atoi(end[:string.find(end, '.')]) 227 wraplineno = lineno 228 found = 0 229 while 1: 230 lineno = lineno + 1 231 if lineno > endlineno: 232 if wraplineno <= 0: 233 break 234 endlineno = wraplineno 235 lineno = 0 236 wraplineno = 0 237 line = self.text.get('%d.0 linestart' % lineno, 238 '%d.0 lineend' % lineno) 239 i = prog.search(line) 240 if i >= 0: 241 found = 1 242 n = max(1, len(prog.group(0))) 243 try: 244 self.text.tag_remove('sel', 245 AtSelFirst(), 246 AtSelLast()) 247 except TclError: 248 pass 249 self.text.tag_add('sel', 250 '%d.%d' % (lineno, i), 251 '%d.%d' % (lineno, i+n)) 252 self.text.mark_set(AtInsert(), 253 '%d.%d' % (lineno, i)) 254 self.text.yview_pickplace(AtInsert()) 255 break 256 if not found: 257 self.frame.bell() 258 259def main(): 260 root = Tk() 261 sb = SelectionBox(root) 262 if sys.argv[1:]: 263 sb.show_page(sys.argv[1]) 264 root.minsize(1, 1) 265 root.mainloop() 266 267main() 268