Commit 80e57fb2 authored by Guido van Rossum's avatar Guido van Rossum

Converted to use re instead of regex; version 0.9.0.

parent 9897f0f8
...@@ -2,8 +2,8 @@ FAQ Wizard ...@@ -2,8 +2,8 @@ FAQ Wizard
---------- ----------
Author: Guido van Rossum <guido@python.org> Author: Guido van Rossum <guido@python.org>
Version: 0.8.4 Version: 0.9.0
Date: 16 December 1997 Date: 21 December 1997
This is a CGI program that maintains a user-editable FAQ. It uses RCS This is a CGI program that maintains a user-editable FAQ. It uses RCS
...@@ -22,6 +22,17 @@ faqwiz.py main module, lives in same directory as FAQ entry files ...@@ -22,6 +22,17 @@ faqwiz.py main module, lives in same directory as FAQ entry files
faqconf.py main configuration module faqconf.py main configuration module
faqcust.py additional local customization module (optional) faqcust.py additional local customization module (optional)
What's New?
-----------
Version 0.9.0 uses the re module (Perl style regular expressions) for
all its regular expression needs, instead of the regex and regsub
modules (Emacs style). This affects the syntax for regular
expressions entered by the user as search strings (with "regular
expression" checked), hence the version number jump.
Setup Information Setup Information
----------------- -----------------
...@@ -76,6 +87,7 @@ file faq01.001.htp,v in the RCS subdirectory. You can now exercise ...@@ -76,6 +87,7 @@ file faq01.001.htp,v in the RCS subdirectory. You can now exercise
the other FAQ wizard features (search, index, whole FAQ, what's new, the other FAQ wizard features (search, index, whole FAQ, what's new,
roulette, and so on). roulette, and so on).
Maintaining Multiple FAQs Maintaining Multiple FAQs
------------------------- -------------------------
......
...@@ -49,7 +49,7 @@ entries marked with * were changed within the last 7 days.) ...@@ -49,7 +49,7 @@ entries marked with * were changed within the last 7 days.)
# Version -- don't change unless you edit faqwiz.py # Version -- don't change unless you edit faqwiz.py
WIZVERSION = "0.8.4" # FAQ Wizard version WIZVERSION = "0.9.0" # FAQ Wizard version
# This parameter is normally overwritten with a dynamic value # This parameter is normally overwritten with a dynamic value
...@@ -58,12 +58,12 @@ import os, sys ...@@ -58,12 +58,12 @@ import os, sys
FAQCGI = os.path.basename(sys.argv[0]) or FAQCGI FAQCGI = os.path.basename(sys.argv[0]) or FAQCGI
del os, sys del os, sys
# Regular expression to recognize FAQ entry files: group(1) should be # Perl (re module) style regular expression to recognize FAQ entry
# the section number, group(2) should be the question number. Both # files: group(1) should be the section number, group(2) should be the
# should be fixed width so simple-minded sorting yields the right # question number. Both should be fixed width so simple-minded
# order. # sorting yields the right order.
OKFILENAME = "^faq\([0-9][0-9]\)\.\([0-9][0-9][0-9]\)\.htp$" OKFILENAME = r"^faq(\d\d)\.(\d\d\d)\.htp$"
# Format to construct a FAQ entry file name # Format to construct a FAQ entry file name
......
...@@ -11,7 +11,7 @@ The actual script to place in cgi-bin is faqw.py. ...@@ -11,7 +11,7 @@ The actual script to place in cgi-bin is faqw.py.
""" """
import sys, string, time, os, stat, regex, cgi, faqconf import sys, string, time, os, stat, re, cgi, faqconf
from faqconf import * # This imports all uppercase names from faqconf import * # This imports all uppercase names
now = time.time() now = time.time()
...@@ -32,21 +32,15 @@ class NoSuchFile(FileError): ...@@ -32,21 +32,15 @@ class NoSuchFile(FileError):
FileError.__init__(self, file) FileError.__init__(self, file)
self.why = why self.why = why
def replace(s, old, new):
try:
return string.replace(s, old, new)
except AttributeError:
return string.join(string.split(s, old), new)
def escape(s): def escape(s):
s = replace(s, '&', '&amp;') s = string.replace(s, '&', '&amp;')
s = replace(s, '<', '&lt;') s = string.replace(s, '<', '&lt;')
s = replace(s, '>', '&gt;') s = string.replace(s, '>', '&gt;')
return s return s
def escapeq(s): def escapeq(s):
s = escape(s) s = escape(s)
s = replace(s, '"', '&quot;') s = string.replace(s, '"', '&quot;')
return s return s
def _interpolate(format, args, kw): def _interpolate(format, args, kw):
...@@ -73,20 +67,20 @@ translate_prog = None ...@@ -73,20 +67,20 @@ translate_prog = None
def translate(text, pre=0): def translate(text, pre=0):
global translate_prog global translate_prog
if not translate_prog: if not translate_prog:
url = '\(http\|ftp\|https\)://[^ \t\r\n]*' translate_prog = prog = re.compile(
email = '\<[-a-zA-Z0-9._]+@[-a-zA-Z0-9._]+' r'\b(http|ftp|https)://\S+(\b|/)|\b[-.\w]+@[-.\w]+')
translate_prog = prog = regex.compile(url + '\|' + email)
else: else:
prog = translate_prog prog = translate_prog
i = 0 i = 0
list = [] list = []
while 1: while 1:
j = prog.search(text, i) m = prog.search(text, i)
if j < 0: if not m:
break break
j = m.start()
list.append(escape(text[i:j])) list.append(escape(text[i:j]))
i = j i = j
url = prog.group(0) url = m.group(0)
while url[-1] in '();:,.?\'"<>': while url[-1] in '();:,.?\'"<>':
url = url[:-1] url = url[:-1]
i = i + len(url) i = i + len(url)
...@@ -103,26 +97,19 @@ def translate(text, pre=0): ...@@ -103,26 +97,19 @@ def translate(text, pre=0):
list.append(escape(text[i:j])) list.append(escape(text[i:j]))
return string.join(list, '') return string.join(list, '')
emphasize_prog = None
def emphasize(line): def emphasize(line):
global emphasize_prog return re.sub(r'\*([a-zA-Z]+)\*', r'<I>\1</I>', line)
import regsub
if not emphasize_prog:
pat = '\*\([a-zA-Z]+\)\*'
emphasize_prog = regex.compile(pat)
return regsub.gsub(emphasize_prog, '<I>\\1</I>', line)
revparse_prog = None revparse_prog = None
def revparse(rev): def revparse(rev):
global revparse_prog global revparse_prog
if not revparse_prog: if not revparse_prog:
revparse_prog = regex.compile( revparse_prog = re.compile(r'^(\d{1,3})\.(\d{1-4})$')
'^\([1-9][0-9]?[0-9]?\)\.\([1-9][0-9]?[0-9]?[0-9]?\)$') m = revparse_prog.match(rev)
if revparse_prog.match(rev) < 0: if not m:
return None return None
[major, minor] = map(string.atoi, revparse_prog.group(1, 2)) [major, minor] = map(string.atoi, m.group(1, 2))
return major, minor return major, minor
def load_cookies(): def load_cookies():
...@@ -315,7 +302,7 @@ class FaqDir: ...@@ -315,7 +302,7 @@ class FaqDir:
entryclass = FaqEntry entryclass = FaqEntry
__okprog = regex.compile(OKFILENAME) __okprog = re.compile(OKFILENAME)
def __init__(self, dir=os.curdir): def __init__(self, dir=os.curdir):
self.__dir = dir self.__dir = dir
...@@ -327,17 +314,18 @@ class FaqDir: ...@@ -327,17 +314,18 @@ class FaqDir:
self.__files = files = [] self.__files = files = []
okprog = self.__okprog okprog = self.__okprog
for file in os.listdir(self.__dir): for file in os.listdir(self.__dir):
if okprog.match(file) >= 0: if self.__okprog.match(file):
files.append(file) files.append(file)
files.sort() files.sort()
def good(self, file): def good(self, file):
return self.__okprog.match(file) >= 0 return self.__okprog.match(file)
def parse(self, file): def parse(self, file):
if not self.good(file): m = self.good(file)
if not m:
return None return None
sec, num = self.__okprog.group(1, 2) sec, num = m.group(1, 2)
return string.atoi(sec), string.atoi(num) return string.atoi(sec), string.atoi(num)
def list(self): def list(self):
...@@ -426,31 +414,29 @@ class FaqWizard: ...@@ -426,31 +414,29 @@ class FaqWizard:
self.error("Empty query string!") self.error("Empty query string!")
return return
if self.ui.querytype == 'simple': if self.ui.querytype == 'simple':
for c in '\\.[]?+^$*': query = re.escape(query)
if c in query:
query = replace(query, c, '\\'+c)
queries = [query] queries = [query]
elif self.ui.querytype in ('anykeywords', 'allkeywords'): elif self.ui.querytype in ('anykeywords', 'allkeywords'):
import regsub words = filter(None, re.split('\W+', query))
words = string.split(regsub.gsub('[^a-zA-Z0-9]+', ' ', query))
if not words: if not words:
self.error("No keywords specified!") self.error("No keywords specified!")
return return
words = map(lambda w: '\<%s\>' % w, words) words = map(lambda w: r'\b%s\b' % w, words)
if self.ui.querytype[:3] == 'any': if self.ui.querytype[:3] == 'any':
queries = [string.join(words, '\|')] queries = [string.join(words, '|')]
else: else:
# Each of the individual queries must match
queries = words queries = words
else: else:
# Default to regex # Default to regular expression
queries = [query] queries = [query]
self.prologue(T_SEARCH) self.prologue(T_SEARCH)
progs = [] progs = []
for query in queries: for query in queries:
if self.ui.casefold == 'no': if self.ui.casefold == 'no':
p = regex.compile(query) p = re.compile(query)
else: else:
p = regex.compile(query, regex.casefold) p = re.compile(query, re.IGNORECASE)
progs.append(p) progs.append(p)
hits = [] hits = []
for file in self.dir.list(): for file in self.dir.list():
...@@ -459,7 +445,7 @@ class FaqWizard: ...@@ -459,7 +445,7 @@ class FaqWizard:
except FileError: except FileError:
constants constants
for p in progs: for p in progs:
if p.search(entry.title) < 0 and p.search(entry.body) < 0: if not p.search(entry.title) and not p.search(entry.body):
break break
else: else:
hits.append(file) hits.append(file)
...@@ -777,8 +763,7 @@ class FaqWizard: ...@@ -777,8 +763,7 @@ class FaqWizard:
file = entry.file file = entry.file
# Normalize line endings in body # Normalize line endings in body
if '\r' in self.ui.body: if '\r' in self.ui.body:
import regsub self.ui.body = re.sub('\r\n?', '\n', self.ui.body)
self.ui.body = regsub.gsub('\r\n?', '\n', self.ui.body)
# Normalize whitespace in title # Normalize whitespace in title
self.ui.title = string.join(string.split(self.ui.title)) self.ui.title = string.join(string.split(self.ui.title))
# Check that there were any changes # Check that there were any changes
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment