' else: if not line[0].isspace(): if pre: print '' pre = 0 else: if not pre: print '
' pre = 1 if '/' in line or '@' in line: line = translate(line, pre) elif '<' in line or '&' in line: line = escape(line) if not pre and '*' in line: line = emphasize(line) print line if pre: print '' pre = 0 if edit: print '
' emit(ENTRY_FOOTER, self) if self.last_changed_date: emit(ENTRY_LOGINFO, self) print '
' class FaqDir: entryclass = FaqEntry __okprog = re.compile(OKFILENAME) def __init__(self, dir=os.curdir): self.__dir = dir self.__files = None def __fill(self): if self.__files is not None: return self.__files = files = [] okprog = self.__okprog for file in os.listdir(self.__dir): if self.__okprog.match(file): files.append(file) files.sort() def good(self, file): return self.__okprog.match(file) def parse(self, file): m = self.good(file) if not m: return None sec, num = m.group(1, 2) return int(sec), int(num) def list(self): # XXX Caller shouldn't modify result self.__fill() return self.__files def open(self, file): sec_num = self.parse(file) if not sec_num: raise InvalidFile(file) try: fp = open(file) except IOError, msg: raise NoSuchFile(file, msg) try: return self.entryclass(fp, file, sec_num) finally: fp.close() def show(self, file, edit=1): self.open(file).show(edit=edit) def new(self, section): if not SECTION_TITLES.has_key(section): raise NoSuchSection(section) maxnum = 0 for file in self.list(): sec, num = self.parse(file) if sec == section: maxnum = max(maxnum, num) sec_num = (section, maxnum+1) file = NEWFILENAME % sec_num return self.entryclass(None, file, sec_num) class FaqWizard: def __init__(self): self.ui = UserInput() self.dir = FaqDir() def go(self): print 'Content-type: text/html' req = self.ui.req or 'home' mname = 'do_%s' % req try: meth = getattr(self, mname) except AttributeError: self.error("Bad request type %r." % (req,)) else: try: meth() except InvalidFile, exc: self.error("Invalid entry file name %s" % exc.file) except NoSuchFile, exc: self.error("No entry with file name %s" % exc.file) except NoSuchSection, exc: self.error("No section number %s" % exc.section) self.epilogue() def error(self, message, **kw): self.prologue(T_ERROR) emit(message, kw) def prologue(self, title, entry=None, **kw): emit(PROLOGUE, entry, kwdict=kw, title=escape(title)) def epilogue(self): emit(EPILOGUE) def do_home(self): self.prologue(T_HOME) emit(HOME) def do_debug(self): self.prologue("FAQ Wizard Debugging") form = cgi.FieldStorage() cgi.print_form(form) cgi.print_environ(os.environ) cgi.print_directory() cgi.print_arguments() def do_search(self): query = self.ui.query if not query: self.error("Empty query string!") return if self.ui.querytype == 'simple': query = re.escape(query) queries = [query] elif self.ui.querytype in ('anykeywords', 'allkeywords'): words = filter(None, re.split('\W+', query)) if not words: self.error("No keywords specified!") return words = map(lambda w: r'\b%s\b' % w, words) if self.ui.querytype[:3] == 'any': queries = ['|'.join(words)] else: # Each of the individual queries must match queries = words else: # Default to regular expression queries = [query] self.prologue(T_SEARCH) progs = [] for query in queries: if self.ui.casefold == 'no': p = re.compile(query) else: p = re.compile(query, re.IGNORECASE) progs.append(p) hits = [] for file in self.dir.list(): try: entry = self.dir.open(file) except FileError: constants for p in progs: if not p.search(entry.title) and not p.search(entry.body): break else: hits.append(file) if not hits: emit(NO_HITS, self.ui, count=0) elif len(hits) <= MAXHITS: if len(hits) == 1: emit(ONE_HIT, count=1) else: emit(FEW_HITS, count=len(hits)) self.format_all(hits, headers=0) else: emit(MANY_HITS, count=len(hits)) self.format_index(hits) def do_all(self): self.prologue(T_ALL) files = self.dir.list() self.last_changed(files) self.format_index(files, localrefs=1) self.format_all(files) def do_compat(self): files = self.dir.list() emit(COMPAT) self.last_changed(files) self.format_index(files, localrefs=1) self.format_all(files, edit=0) sys.exit(0) # XXX Hack to suppress epilogue def last_changed(self, files): latest = 0 for file in files: entry = self.dir.open(file) if entry: mtime = mtime = entry.getmtime() if mtime > latest: latest = mtime print time.strftime(LAST_CHANGED, time.localtime(latest)) emit(EXPLAIN_MARKS) def format_all(self, files, edit=1, headers=1): sec = 0 for file in files: try: entry = self.dir.open(file) except NoSuchFile: continue if headers and entry.sec != sec: sec = entry.sec try: title = SECTION_TITLES[sec] except KeyError: title = "Untitled" emit("\n
\n %(sec)s. %(title)s\n", sec=sec, title=title) entry.show(edit=edit) def do_index(self): self.prologue(T_INDEX) files = self.dir.list() self.last_changed(files) self.format_index(files, add=1) def format_index(self, files, add=0, localrefs=0): sec = 0 for file in files: try: entry = self.dir.open(file) except NoSuchFile: continue if entry.sec != sec: if sec: if add: emit(INDEX_ADDSECTION, sec=sec) emit(INDEX_ENDSECTION, sec=sec) sec = entry.sec try: title = SECTION_TITLES[sec] except KeyError: title = "Untitled" emit(INDEX_SECTION, sec=sec, title=title) if localrefs: emit(LOCAL_ENTRY, entry) else: emit(INDEX_ENTRY, entry) entry.emit_marks() if sec: if add: emit(INDEX_ADDSECTION, sec=sec) emit(INDEX_ENDSECTION, sec=sec) def do_recent(self): if not self.ui.days: days = 1 else: days = float(self.ui.days) try: cutoff = now - days * 24 * 3600 except OverflowError: cutoff = 0 list = [] for file in self.dir.list(): entry = self.dir.open(file) if not entry: continue mtime = entry.getmtime() if mtime >= cutoff: list.append((mtime, file)) list.sort() list.reverse() self.prologue(T_RECENT) if days <= 1: period = "%.2g hours" % (days*24) else: period = "%.6g days" % days if not list: emit(NO_RECENT, period=period) elif len(list) == 1: emit(ONE_RECENT, period=period) else: emit(SOME_RECENT, period=period, count=len(list)) self.format_all(map(lambda (mtime, file): file, list), headers=0) emit(TAIL_RECENT) def do_roulette(self): import random files = self.dir.list() if not files: self.error("No entries.") return file = random.choice(files) self.prologue(T_ROULETTE) emit(ROULETTE) self.dir.show(file) def do_help(self): self.prologue(T_HELP) emit(HELP) def do_show(self): entry = self.dir.open(self.ui.file) self.prologue(T_SHOW) entry.show() def do_add(self): self.prologue(T_ADD) emit(ADD_HEAD) sections = SECTION_TITLES.items() sections.sort() for section, title in sections: emit(ADD_SECTION, section=section, title=title) emit(ADD_TAIL) def do_delete(self): self.prologue(T_DELETE) emit(DELETE) def do_log(self): entry = self.dir.open(self.ui.file) self.prologue(T_LOG, entry) emit(LOG, entry) self.rlog(interpolate(SH_RLOG, entry), entry) def rlog(self, command, entry=None): output = os.popen(command).read() sys.stdout.write('') athead = 0 lines = output.split('\n') while lines and not lines[-1]: del lines[-1] if lines: line = lines[-1] if line[:1] == '=' and len(line) >= 40 and \ line == line[0]*len(line): del lines[-1] headrev = None for line in lines: if entry and athead and line[:9] == 'revision ': rev = line[9:].split() mami = revparse(rev) if not mami: print line else: emit(REVISIONLINK, entry, rev=rev, line=line) if mami[1] > 1: prev = "%d.%d" % (mami[0], mami[1]-1) emit(DIFFLINK, entry, prev=prev, rev=rev) if headrev: emit(DIFFLINK, entry, prev=rev, rev=headrev) else: headrev = rev print athead = 0 else: athead = 0 if line[:1] == '-' and len(line) >= 20 and \ line == len(line) * line[0]: athead = 1 sys.stdout.write('') else: print line print '' def do_revision(self): entry = self.dir.open(self.ui.file) rev = self.ui.rev mami = revparse(rev) if not mami: self.error("Invalid revision number: %r." % (rev,)) self.prologue(T_REVISION, entry) self.shell(interpolate(SH_REVISION, entry, rev=rev)) def do_diff(self): entry = self.dir.open(self.ui.file) prev = self.ui.prev rev = self.ui.rev mami = revparse(rev) if not mami: self.error("Invalid revision number: %r." % (rev,)) if prev: if not revparse(prev): self.error("Invalid previous revision number: %r." % (prev,)) else: prev = '%d.%d' % (mami[0], mami[1]) self.prologue(T_DIFF, entry) self.shell(interpolate(SH_RDIFF, entry, rev=rev, prev=prev)) def shell(self, command): output = os.popen(command).read() sys.stdout.write('
') print escape(output) print '' def do_new(self): entry = self.dir.new(section=int(self.ui.section)) entry.version = '*new*' self.prologue(T_EDIT) emit(EDITHEAD) emit(EDITFORM1, entry, editversion=entry.version) emit(EDITFORM2, entry, load_my_cookie()) emit(EDITFORM3) entry.show(edit=0) def do_edit(self): entry = self.dir.open(self.ui.file) entry.load_version() self.prologue(T_EDIT) emit(EDITHEAD) emit(EDITFORM1, entry, editversion=entry.version) emit(EDITFORM2, entry, load_my_cookie()) emit(EDITFORM3) entry.show(edit=0) def do_review(self): send_my_cookie(self.ui) if self.ui.editversion == '*new*': sec, num = self.dir.parse(self.ui.file) entry = self.dir.new(section=sec) entry.version = "*new*" if entry.file != self.ui.file: self.error("Commit version conflict!") emit(NEWCONFLICT, self.ui, sec=sec, num=num) return else: entry = self.dir.open(self.ui.file) entry.load_version() # Check that the FAQ entry number didn't change if self.ui.title.split()[:1] != entry.title.split()[:1]: self.error("Don't change the entry number please!") return # Check that the edited version is the current version if entry.version != self.ui.editversion: self.error("Commit version conflict!") emit(VERSIONCONFLICT, entry, self.ui) return commit_ok = ((not PASSWORD or self.ui.password == PASSWORD) and self.ui.author and '@' in self.ui.email and self.ui.log) if self.ui.commit: if not commit_ok: self.cantcommit() else: self.commit(entry) return self.prologue(T_REVIEW) emit(REVIEWHEAD) entry.body = self.ui.body entry.title = self.ui.title entry.show(edit=0) emit(EDITFORM1, self.ui, entry) if commit_ok: emit(COMMIT) else: emit(NOCOMMIT_HEAD) self.errordetail() emit(NOCOMMIT_TAIL) emit(EDITFORM2, self.ui, entry, load_my_cookie()) emit(EDITFORM3) def cantcommit(self): self.prologue(T_CANTCOMMIT) print CANTCOMMIT_HEAD self.errordetail() print CANTCOMMIT_TAIL def errordetail(self): if PASSWORD and self.ui.password != PASSWORD: emit(NEED_PASSWD) if not self.ui.log: emit(NEED_LOG) if not self.ui.author: emit(NEED_AUTHOR) if not self.ui.email: emit(NEED_EMAIL) def commit(self, entry): file = entry.file # Normalize line endings in body if '\r' in self.ui.body: self.ui.body = re.sub('\r\n?', '\n', self.ui.body) # Normalize whitespace in title self.ui.title = ' '.join(self.ui.title.split()) # Check that there were any changes if self.ui.body == entry.body and self.ui.title == entry.title: self.error("You didn't make any changes!") return # need to lock here because otherwise the file exists and is not writable (on NT) command = interpolate(SH_LOCK, file=file) p = os.popen(command) output = p.read() try: os.unlink(file) except os.error: pass try: f = open(file, 'w') except IOError, why: self.error(CANTWRITE, file=file, why=why) return date = time.ctime(now) emit(FILEHEADER, self.ui, os.environ, date=date, _file=f, _quote=0) f.write('\n') f.write(self.ui.body) f.write('\n') f.close() import tempfile tf = tempfile.NamedTemporaryFile() emit(LOGHEADER, self.ui, os.environ, date=date, _file=tf) tf.flush() tf.seek(0) command = interpolate(SH_CHECKIN, file=file, tfn=tf.name) log("\n\n" + command) p = os.popen(command) output = p.read() sts = p.close() log("output: " + output) log("done: " + str(sts)) log("TempFile:\n" + tf.read() + "end") if not sts: self.prologue(T_COMMITTED) emit(COMMITTED) else: self.error(T_COMMITFAILED) emit(COMMITFAILED, sts=sts) print '
%s' % escape(output) try: os.unlink(tf.name) except os.error: pass entry = self.dir.open(file) entry.show() wiz = FaqWizard() wiz.go()
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4