symbian-qemu-0.9.1-12/python-2.6.1/Tools/faqwiz/faqwiz.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 """Generic FAQ Wizard.
       
     2 
       
     3 This is a CGI program that maintains a user-editable FAQ.  It uses RCS
       
     4 to keep track of changes to individual FAQ entries.  It is fully
       
     5 configurable; everything you might want to change when using this
       
     6 program to maintain some other FAQ than the Python FAQ is contained in
       
     7 the configuration module, faqconf.py.
       
     8 
       
     9 Note that this is not an executable script; it's an importable module.
       
    10 The actual script to place in cgi-bin is faqw.py.
       
    11 
       
    12 """
       
    13 
       
    14 import sys, time, os, stat, re, cgi, faqconf
       
    15 from faqconf import *                   # This imports all uppercase names
       
    16 now = time.time()
       
    17 
       
    18 class FileError:
       
    19     def __init__(self, file):
       
    20         self.file = file
       
    21 
       
    22 class InvalidFile(FileError):
       
    23     pass
       
    24 
       
    25 class NoSuchSection(FileError):
       
    26     def __init__(self, section):
       
    27         FileError.__init__(self, NEWFILENAME %(section, 1))
       
    28         self.section = section
       
    29 
       
    30 class NoSuchFile(FileError):
       
    31     def __init__(self, file, why=None):
       
    32         FileError.__init__(self, file)
       
    33         self.why = why
       
    34 
       
    35 def escape(s):
       
    36     s = s.replace('&', '&')
       
    37     s = s.replace('<', '&lt;')
       
    38     s = s.replace('>', '&gt;')
       
    39     return s
       
    40 
       
    41 def escapeq(s):
       
    42     s = escape(s)
       
    43     s = s.replace('"', '&quot;')
       
    44     return s
       
    45 
       
    46 def _interpolate(format, args, kw):
       
    47     try:
       
    48         quote = kw['_quote']
       
    49     except KeyError:
       
    50         quote = 1
       
    51     d = (kw,) + args + (faqconf.__dict__,)
       
    52     m = MagicDict(d, quote)
       
    53     return format % m
       
    54 
       
    55 def interpolate(format, *args, **kw):
       
    56     return _interpolate(format, args, kw)
       
    57 
       
    58 def emit(format, *args, **kw):
       
    59     try:
       
    60         f = kw['_file']
       
    61     except KeyError:
       
    62         f = sys.stdout
       
    63     f.write(_interpolate(format, args, kw))
       
    64 
       
    65 translate_prog = None
       
    66 
       
    67 def translate(text, pre=0):
       
    68     global translate_prog
       
    69     if not translate_prog:
       
    70         translate_prog = prog = re.compile(
       
    71             r'\b(http|ftp|https)://\S+(\b|/)|\b[-.\w]+@[-.\w]+')
       
    72     else:
       
    73         prog = translate_prog
       
    74     i = 0
       
    75     list = []
       
    76     while 1:
       
    77         m = prog.search(text, i)
       
    78         if not m:
       
    79             break
       
    80         j = m.start()
       
    81         list.append(escape(text[i:j]))
       
    82         i = j
       
    83         url = m.group(0)
       
    84         while url[-1] in '();:,.?\'"<>':
       
    85             url = url[:-1]
       
    86         i = i + len(url)
       
    87         url = escape(url)
       
    88         if not pre or (pre and PROCESS_PREFORMAT):
       
    89             if ':' in url:
       
    90                 repl = '<A HREF="%s">%s</A>' % (url, url)
       
    91             else:
       
    92                 repl = '<A HREF="mailto:%s">%s</A>' % (url, url)
       
    93         else:
       
    94             repl = url
       
    95         list.append(repl)
       
    96     j = len(text)
       
    97     list.append(escape(text[i:j]))
       
    98     return ''.join(list)
       
    99 
       
   100 def emphasize(line):
       
   101     return re.sub(r'\*([a-zA-Z]+)\*', r'<I>\1</I>', line)
       
   102 
       
   103 revparse_prog = None
       
   104 
       
   105 def revparse(rev):
       
   106     global revparse_prog
       
   107     if not revparse_prog:
       
   108         revparse_prog = re.compile(r'^(\d{1,3})\.(\d{1,4})$')
       
   109     m = revparse_prog.match(rev)
       
   110     if not m:
       
   111         return None
       
   112     [major, minor] = map(int, m.group(1, 2))
       
   113     return major, minor
       
   114 
       
   115 logon = 0
       
   116 def log(text):
       
   117     if logon:
       
   118         logfile = open("logfile", "a")
       
   119         logfile.write(text + "\n")
       
   120         logfile.close()
       
   121 
       
   122 def load_cookies():
       
   123     if not os.environ.has_key('HTTP_COOKIE'):
       
   124         return {}
       
   125     raw = os.environ['HTTP_COOKIE']
       
   126     words = [s.strip() for s in raw.split(';')]
       
   127     cookies = {}
       
   128     for word in words:
       
   129         i = word.find('=')
       
   130         if i >= 0:
       
   131             key, value = word[:i], word[i+1:]
       
   132             cookies[key] = value
       
   133     return cookies
       
   134 
       
   135 def load_my_cookie():
       
   136     cookies = load_cookies()
       
   137     try:
       
   138         value = cookies[COOKIE_NAME]
       
   139     except KeyError:
       
   140         return {}
       
   141     import urllib
       
   142     value = urllib.unquote(value)
       
   143     words = value.split('/')
       
   144     while len(words) < 3:
       
   145         words.append('')
       
   146     author = '/'.join(words[:-2])
       
   147     email = words[-2]
       
   148     password = words[-1]
       
   149     return {'author': author,
       
   150             'email': email,
       
   151             'password': password}
       
   152 
       
   153 def send_my_cookie(ui):
       
   154     name = COOKIE_NAME
       
   155     value = "%s/%s/%s" % (ui.author, ui.email, ui.password)
       
   156     import urllib
       
   157     value = urllib.quote(value)
       
   158     then = now + COOKIE_LIFETIME
       
   159     gmt = time.gmtime(then)
       
   160     path = os.environ.get('SCRIPT_NAME', '/cgi-bin/')
       
   161     print "Set-Cookie: %s=%s; path=%s;" % (name, value, path),
       
   162     print time.strftime("expires=%a, %d-%b-%y %X GMT", gmt)
       
   163 
       
   164 class MagicDict:
       
   165 
       
   166     def __init__(self, d, quote):
       
   167         self.__d = d
       
   168         self.__quote = quote
       
   169 
       
   170     def __getitem__(self, key):
       
   171         for d in self.__d:
       
   172             try:
       
   173                 value = d[key]
       
   174                 if value:
       
   175                     value = str(value)
       
   176                     if self.__quote:
       
   177                         value = escapeq(value)
       
   178                     return value
       
   179             except KeyError:
       
   180                 pass
       
   181         return ''
       
   182 
       
   183 class UserInput:
       
   184 
       
   185     def __init__(self):
       
   186         self.__form = cgi.FieldStorage()
       
   187         #log("\n\nbody: " + self.body)
       
   188 
       
   189     def __getattr__(self, name):
       
   190         if name[0] == '_':
       
   191             raise AttributeError
       
   192         try:
       
   193             value = self.__form[name].value
       
   194         except (TypeError, KeyError):
       
   195             value = ''
       
   196         else:
       
   197             value = value.strip()
       
   198         setattr(self, name, value)
       
   199         return value
       
   200 
       
   201     def __getitem__(self, key):
       
   202         return getattr(self, key)
       
   203 
       
   204 class FaqEntry:
       
   205 
       
   206     def __init__(self, fp, file, sec_num):
       
   207         self.file = file
       
   208         self.sec, self.num = sec_num
       
   209         if fp:
       
   210             import rfc822
       
   211             self.__headers = rfc822.Message(fp)
       
   212             self.body = fp.read().strip()
       
   213         else:
       
   214             self.__headers = {'title': "%d.%d. " % sec_num}
       
   215             self.body = ''
       
   216 
       
   217     def __getattr__(self, name):
       
   218         if name[0] == '_':
       
   219             raise AttributeError
       
   220         key = '-'.join(name.split('_'))
       
   221         try:
       
   222             value = self.__headers[key]
       
   223         except KeyError:
       
   224             value = ''
       
   225         setattr(self, name, value)
       
   226         return value
       
   227 
       
   228     def __getitem__(self, key):
       
   229         return getattr(self, key)
       
   230 
       
   231     def load_version(self):
       
   232         command = interpolate(SH_RLOG_H, self)
       
   233         p = os.popen(command)
       
   234         version = ''
       
   235         while 1:
       
   236             line = p.readline()
       
   237             if not line:
       
   238                 break
       
   239             if line[:5] == 'head:':
       
   240                 version = line[5:].strip()
       
   241         p.close()
       
   242         self.version = version
       
   243 
       
   244     def getmtime(self):
       
   245         if not self.last_changed_date:
       
   246             return 0
       
   247         try:
       
   248             return os.stat(self.file)[stat.ST_MTIME]
       
   249         except os.error:
       
   250             return 0
       
   251 
       
   252     def emit_marks(self):
       
   253         mtime = self.getmtime()
       
   254         if mtime >= now - DT_VERY_RECENT:
       
   255             emit(MARK_VERY_RECENT, self)
       
   256         elif mtime >= now - DT_RECENT:
       
   257             emit(MARK_RECENT, self)
       
   258 
       
   259     def show(self, edit=1):
       
   260         emit(ENTRY_HEADER1, self)
       
   261         self.emit_marks()
       
   262         emit(ENTRY_HEADER2, self)
       
   263         pre = 0
       
   264         raw = 0
       
   265         for line in self.body.split('\n'):
       
   266             # Allow the user to insert raw html into a FAQ answer
       
   267             # (Skip Montanaro, with changes by Guido)
       
   268             tag = line.rstrip().lower()
       
   269             if tag == '<html>':
       
   270                 raw = 1
       
   271                 continue
       
   272             if tag == '</html>':
       
   273                 raw = 0
       
   274                 continue
       
   275             if raw:
       
   276                 print line
       
   277                 continue
       
   278             if not line.strip():
       
   279                 if pre:
       
   280                     print '</PRE>'
       
   281                     pre = 0
       
   282                 else:
       
   283                     print '<P>'
       
   284             else:
       
   285                 if not line[0].isspace():
       
   286                     if pre:
       
   287                         print '</PRE>'
       
   288                         pre = 0
       
   289                 else:
       
   290                     if not pre:
       
   291                         print '<PRE>'
       
   292                         pre = 1
       
   293                 if '/' in line or '@' in line:
       
   294                     line = translate(line, pre)
       
   295                 elif '<' in line or '&' in line:
       
   296                     line = escape(line)
       
   297                 if not pre and '*' in line:
       
   298                     line = emphasize(line)
       
   299                 print line
       
   300         if pre:
       
   301             print '</PRE>'
       
   302             pre = 0
       
   303         if edit:
       
   304             print '<P>'
       
   305             emit(ENTRY_FOOTER, self)
       
   306             if self.last_changed_date:
       
   307                 emit(ENTRY_LOGINFO, self)
       
   308         print '<P>'
       
   309 
       
   310 class FaqDir:
       
   311 
       
   312     entryclass = FaqEntry
       
   313 
       
   314     __okprog = re.compile(OKFILENAME)
       
   315 
       
   316     def __init__(self, dir=os.curdir):
       
   317         self.__dir = dir
       
   318         self.__files = None
       
   319 
       
   320     def __fill(self):
       
   321         if self.__files is not None:
       
   322             return
       
   323         self.__files = files = []
       
   324         okprog = self.__okprog
       
   325         for file in os.listdir(self.__dir):
       
   326             if self.__okprog.match(file):
       
   327                 files.append(file)
       
   328         files.sort()
       
   329 
       
   330     def good(self, file):
       
   331         return self.__okprog.match(file)
       
   332 
       
   333     def parse(self, file):
       
   334         m = self.good(file)
       
   335         if not m:
       
   336             return None
       
   337         sec, num = m.group(1, 2)
       
   338         return int(sec), int(num)
       
   339 
       
   340     def list(self):
       
   341         # XXX Caller shouldn't modify result
       
   342         self.__fill()
       
   343         return self.__files
       
   344 
       
   345     def open(self, file):
       
   346         sec_num = self.parse(file)
       
   347         if not sec_num:
       
   348             raise InvalidFile(file)
       
   349         try:
       
   350             fp = open(file)
       
   351         except IOError, msg:
       
   352             raise NoSuchFile(file, msg)
       
   353         try:
       
   354             return self.entryclass(fp, file, sec_num)
       
   355         finally:
       
   356             fp.close()
       
   357 
       
   358     def show(self, file, edit=1):
       
   359         self.open(file).show(edit=edit)
       
   360 
       
   361     def new(self, section):
       
   362         if not SECTION_TITLES.has_key(section):
       
   363             raise NoSuchSection(section)
       
   364         maxnum = 0
       
   365         for file in self.list():
       
   366             sec, num = self.parse(file)
       
   367             if sec == section:
       
   368                 maxnum = max(maxnum, num)
       
   369         sec_num = (section, maxnum+1)
       
   370         file = NEWFILENAME % sec_num
       
   371         return self.entryclass(None, file, sec_num)
       
   372 
       
   373 class FaqWizard:
       
   374 
       
   375     def __init__(self):
       
   376         self.ui = UserInput()
       
   377         self.dir = FaqDir()
       
   378 
       
   379     def go(self):
       
   380         print 'Content-type: text/html'
       
   381         req = self.ui.req or 'home'
       
   382         mname = 'do_%s' % req
       
   383         try:
       
   384             meth = getattr(self, mname)
       
   385         except AttributeError:
       
   386             self.error("Bad request type %r." % (req,))
       
   387         else:
       
   388             try:
       
   389                 meth()
       
   390             except InvalidFile, exc:
       
   391                 self.error("Invalid entry file name %s" % exc.file)
       
   392             except NoSuchFile, exc:
       
   393                 self.error("No entry with file name %s" % exc.file)
       
   394             except NoSuchSection, exc:
       
   395                 self.error("No section number %s" % exc.section)
       
   396         self.epilogue()
       
   397 
       
   398     def error(self, message, **kw):
       
   399         self.prologue(T_ERROR)
       
   400         emit(message, kw)
       
   401 
       
   402     def prologue(self, title, entry=None, **kw):
       
   403         emit(PROLOGUE, entry, kwdict=kw, title=escape(title))
       
   404 
       
   405     def epilogue(self):
       
   406         emit(EPILOGUE)
       
   407 
       
   408     def do_home(self):
       
   409         self.prologue(T_HOME)
       
   410         emit(HOME)
       
   411 
       
   412     def do_debug(self):
       
   413         self.prologue("FAQ Wizard Debugging")
       
   414         form = cgi.FieldStorage()
       
   415         cgi.print_form(form)
       
   416         cgi.print_environ(os.environ)
       
   417         cgi.print_directory()
       
   418         cgi.print_arguments()
       
   419 
       
   420     def do_search(self):
       
   421         query = self.ui.query
       
   422         if not query:
       
   423             self.error("Empty query string!")
       
   424             return
       
   425         if self.ui.querytype == 'simple':
       
   426             query = re.escape(query)
       
   427             queries = [query]
       
   428         elif self.ui.querytype in ('anykeywords', 'allkeywords'):
       
   429             words = filter(None, re.split('\W+', query))
       
   430             if not words:
       
   431                 self.error("No keywords specified!")
       
   432                 return
       
   433             words = map(lambda w: r'\b%s\b' % w, words)
       
   434             if self.ui.querytype[:3] == 'any':
       
   435                 queries = ['|'.join(words)]
       
   436             else:
       
   437                 # Each of the individual queries must match
       
   438                 queries = words
       
   439         else:
       
   440             # Default to regular expression
       
   441             queries = [query]
       
   442         self.prologue(T_SEARCH)
       
   443         progs = []
       
   444         for query in queries:
       
   445             if self.ui.casefold == 'no':
       
   446                 p = re.compile(query)
       
   447             else:
       
   448                 p = re.compile(query, re.IGNORECASE)
       
   449             progs.append(p)
       
   450         hits = []
       
   451         for file in self.dir.list():
       
   452             try:
       
   453                 entry = self.dir.open(file)
       
   454             except FileError:
       
   455                 constants
       
   456             for p in progs:
       
   457                 if not p.search(entry.title) and not p.search(entry.body):
       
   458                     break
       
   459             else:
       
   460                 hits.append(file)
       
   461         if not hits:
       
   462             emit(NO_HITS, self.ui, count=0)
       
   463         elif len(hits) <= MAXHITS:
       
   464             if len(hits) == 1:
       
   465                 emit(ONE_HIT, count=1)
       
   466             else:
       
   467                 emit(FEW_HITS, count=len(hits))
       
   468             self.format_all(hits, headers=0)
       
   469         else:
       
   470             emit(MANY_HITS, count=len(hits))
       
   471             self.format_index(hits)
       
   472 
       
   473     def do_all(self):
       
   474         self.prologue(T_ALL)
       
   475         files = self.dir.list()
       
   476         self.last_changed(files)
       
   477         self.format_index(files, localrefs=1)
       
   478         self.format_all(files)
       
   479 
       
   480     def do_compat(self):
       
   481         files = self.dir.list()
       
   482         emit(COMPAT)
       
   483         self.last_changed(files)
       
   484         self.format_index(files, localrefs=1)
       
   485         self.format_all(files, edit=0)
       
   486         sys.exit(0)                     # XXX Hack to suppress epilogue
       
   487 
       
   488     def last_changed(self, files):
       
   489         latest = 0
       
   490         for file in files:
       
   491             entry = self.dir.open(file)
       
   492             if entry:
       
   493                 mtime = mtime = entry.getmtime()
       
   494                 if mtime > latest:
       
   495                     latest = mtime
       
   496         print time.strftime(LAST_CHANGED, time.localtime(latest))
       
   497         emit(EXPLAIN_MARKS)
       
   498 
       
   499     def format_all(self, files, edit=1, headers=1):
       
   500         sec = 0
       
   501         for file in files:
       
   502             try:
       
   503                 entry = self.dir.open(file)
       
   504             except NoSuchFile:
       
   505                 continue
       
   506             if headers and entry.sec != sec:
       
   507                 sec = entry.sec
       
   508                 try:
       
   509                     title = SECTION_TITLES[sec]
       
   510                 except KeyError:
       
   511                     title = "Untitled"
       
   512                 emit("\n<HR>\n<H1>%(sec)s. %(title)s</H1>\n",
       
   513                      sec=sec, title=title)
       
   514             entry.show(edit=edit)
       
   515 
       
   516     def do_index(self):
       
   517         self.prologue(T_INDEX)
       
   518         files = self.dir.list()
       
   519         self.last_changed(files)
       
   520         self.format_index(files, add=1)
       
   521 
       
   522     def format_index(self, files, add=0, localrefs=0):
       
   523         sec = 0
       
   524         for file in files:
       
   525             try:
       
   526                 entry = self.dir.open(file)
       
   527             except NoSuchFile:
       
   528                 continue
       
   529             if entry.sec != sec:
       
   530                 if sec:
       
   531                     if add:
       
   532                         emit(INDEX_ADDSECTION, sec=sec)
       
   533                     emit(INDEX_ENDSECTION, sec=sec)
       
   534                 sec = entry.sec
       
   535                 try:
       
   536                     title = SECTION_TITLES[sec]
       
   537                 except KeyError:
       
   538                     title = "Untitled"
       
   539                 emit(INDEX_SECTION, sec=sec, title=title)
       
   540             if localrefs:
       
   541                 emit(LOCAL_ENTRY, entry)
       
   542             else:
       
   543                 emit(INDEX_ENTRY, entry)
       
   544             entry.emit_marks()
       
   545         if sec:
       
   546             if add:
       
   547                 emit(INDEX_ADDSECTION, sec=sec)
       
   548             emit(INDEX_ENDSECTION, sec=sec)
       
   549 
       
   550     def do_recent(self):
       
   551         if not self.ui.days:
       
   552             days = 1
       
   553         else:
       
   554             days = float(self.ui.days)
       
   555         try:
       
   556             cutoff = now - days * 24 * 3600
       
   557         except OverflowError:
       
   558             cutoff = 0
       
   559         list = []
       
   560         for file in self.dir.list():
       
   561             entry = self.dir.open(file)
       
   562             if not entry:
       
   563                 continue
       
   564             mtime = entry.getmtime()
       
   565             if mtime >= cutoff:
       
   566                 list.append((mtime, file))
       
   567         list.sort()
       
   568         list.reverse()
       
   569         self.prologue(T_RECENT)
       
   570         if days <= 1:
       
   571             period = "%.2g hours" % (days*24)
       
   572         else:
       
   573             period = "%.6g days" % days
       
   574         if not list:
       
   575             emit(NO_RECENT, period=period)
       
   576         elif len(list) == 1:
       
   577             emit(ONE_RECENT, period=period)
       
   578         else:
       
   579             emit(SOME_RECENT, period=period, count=len(list))
       
   580         self.format_all(map(lambda (mtime, file): file, list), headers=0)
       
   581         emit(TAIL_RECENT)
       
   582 
       
   583     def do_roulette(self):
       
   584         import random
       
   585         files = self.dir.list()
       
   586         if not files:
       
   587             self.error("No entries.")
       
   588             return
       
   589         file = random.choice(files)
       
   590         self.prologue(T_ROULETTE)
       
   591         emit(ROULETTE)
       
   592         self.dir.show(file)
       
   593 
       
   594     def do_help(self):
       
   595         self.prologue(T_HELP)
       
   596         emit(HELP)
       
   597 
       
   598     def do_show(self):
       
   599         entry = self.dir.open(self.ui.file)
       
   600         self.prologue(T_SHOW)
       
   601         entry.show()
       
   602 
       
   603     def do_add(self):
       
   604         self.prologue(T_ADD)
       
   605         emit(ADD_HEAD)
       
   606         sections = SECTION_TITLES.items()
       
   607         sections.sort()
       
   608         for section, title in sections:
       
   609             emit(ADD_SECTION, section=section, title=title)
       
   610         emit(ADD_TAIL)
       
   611 
       
   612     def do_delete(self):
       
   613         self.prologue(T_DELETE)
       
   614         emit(DELETE)
       
   615 
       
   616     def do_log(self):
       
   617         entry = self.dir.open(self.ui.file)
       
   618         self.prologue(T_LOG, entry)
       
   619         emit(LOG, entry)
       
   620         self.rlog(interpolate(SH_RLOG, entry), entry)
       
   621 
       
   622     def rlog(self, command, entry=None):
       
   623         output = os.popen(command).read()
       
   624         sys.stdout.write('<PRE>')
       
   625         athead = 0
       
   626         lines = output.split('\n')
       
   627         while lines and not lines[-1]:
       
   628             del lines[-1]
       
   629         if lines:
       
   630             line = lines[-1]
       
   631             if line[:1] == '=' and len(line) >= 40 and \
       
   632                line == line[0]*len(line):
       
   633                 del lines[-1]
       
   634         headrev = None
       
   635         for line in lines:
       
   636             if entry and athead and line[:9] == 'revision ':
       
   637                 rev = line[9:].split()
       
   638                 mami = revparse(rev)
       
   639                 if not mami:
       
   640                     print line
       
   641                 else:
       
   642                     emit(REVISIONLINK, entry, rev=rev, line=line)
       
   643                     if mami[1] > 1:
       
   644                         prev = "%d.%d" % (mami[0], mami[1]-1)
       
   645                         emit(DIFFLINK, entry, prev=prev, rev=rev)
       
   646                     if headrev:
       
   647                         emit(DIFFLINK, entry, prev=rev, rev=headrev)
       
   648                     else:
       
   649                         headrev = rev
       
   650                     print
       
   651                 athead = 0
       
   652             else:
       
   653                 athead = 0
       
   654                 if line[:1] == '-' and len(line) >= 20 and \
       
   655                    line == len(line) * line[0]:
       
   656                     athead = 1
       
   657                     sys.stdout.write('<HR>')
       
   658                 else:
       
   659                     print line
       
   660         print '</PRE>'
       
   661 
       
   662     def do_revision(self):
       
   663         entry = self.dir.open(self.ui.file)
       
   664         rev = self.ui.rev
       
   665         mami = revparse(rev)
       
   666         if not mami:
       
   667             self.error("Invalid revision number: %r." % (rev,))
       
   668         self.prologue(T_REVISION, entry)
       
   669         self.shell(interpolate(SH_REVISION, entry, rev=rev))
       
   670 
       
   671     def do_diff(self):
       
   672         entry = self.dir.open(self.ui.file)
       
   673         prev = self.ui.prev
       
   674         rev = self.ui.rev
       
   675         mami = revparse(rev)
       
   676         if not mami:
       
   677             self.error("Invalid revision number: %r." % (rev,))
       
   678         if prev:
       
   679             if not revparse(prev):
       
   680                 self.error("Invalid previous revision number: %r." % (prev,))
       
   681         else:
       
   682             prev = '%d.%d' % (mami[0], mami[1])
       
   683         self.prologue(T_DIFF, entry)
       
   684         self.shell(interpolate(SH_RDIFF, entry, rev=rev, prev=prev))
       
   685 
       
   686     def shell(self, command):
       
   687         output = os.popen(command).read()
       
   688         sys.stdout.write('<PRE>')
       
   689         print escape(output)
       
   690         print '</PRE>'
       
   691 
       
   692     def do_new(self):
       
   693         entry = self.dir.new(section=int(self.ui.section))
       
   694         entry.version = '*new*'
       
   695         self.prologue(T_EDIT)
       
   696         emit(EDITHEAD)
       
   697         emit(EDITFORM1, entry, editversion=entry.version)
       
   698         emit(EDITFORM2, entry, load_my_cookie())
       
   699         emit(EDITFORM3)
       
   700         entry.show(edit=0)
       
   701 
       
   702     def do_edit(self):
       
   703         entry = self.dir.open(self.ui.file)
       
   704         entry.load_version()
       
   705         self.prologue(T_EDIT)
       
   706         emit(EDITHEAD)
       
   707         emit(EDITFORM1, entry, editversion=entry.version)
       
   708         emit(EDITFORM2, entry, load_my_cookie())
       
   709         emit(EDITFORM3)
       
   710         entry.show(edit=0)
       
   711 
       
   712     def do_review(self):
       
   713         send_my_cookie(self.ui)
       
   714         if self.ui.editversion == '*new*':
       
   715             sec, num = self.dir.parse(self.ui.file)
       
   716             entry = self.dir.new(section=sec)
       
   717             entry.version = "*new*"
       
   718             if entry.file != self.ui.file:
       
   719                 self.error("Commit version conflict!")
       
   720                 emit(NEWCONFLICT, self.ui, sec=sec, num=num)
       
   721                 return
       
   722         else:
       
   723             entry = self.dir.open(self.ui.file)
       
   724             entry.load_version()
       
   725         # Check that the FAQ entry number didn't change
       
   726         if self.ui.title.split()[:1] != entry.title.split()[:1]:
       
   727             self.error("Don't change the entry number please!")
       
   728             return
       
   729         # Check that the edited version is the current version
       
   730         if entry.version != self.ui.editversion:
       
   731             self.error("Commit version conflict!")
       
   732             emit(VERSIONCONFLICT, entry, self.ui)
       
   733             return
       
   734         commit_ok = ((not PASSWORD
       
   735                       or self.ui.password == PASSWORD)
       
   736                      and self.ui.author
       
   737                      and '@' in self.ui.email
       
   738                      and self.ui.log)
       
   739         if self.ui.commit:
       
   740             if not commit_ok:
       
   741                 self.cantcommit()
       
   742             else:
       
   743                 self.commit(entry)
       
   744             return
       
   745         self.prologue(T_REVIEW)
       
   746         emit(REVIEWHEAD)
       
   747         entry.body = self.ui.body
       
   748         entry.title = self.ui.title
       
   749         entry.show(edit=0)
       
   750         emit(EDITFORM1, self.ui, entry)
       
   751         if commit_ok:
       
   752             emit(COMMIT)
       
   753         else:
       
   754             emit(NOCOMMIT_HEAD)
       
   755             self.errordetail()
       
   756             emit(NOCOMMIT_TAIL)
       
   757         emit(EDITFORM2, self.ui, entry, load_my_cookie())
       
   758         emit(EDITFORM3)
       
   759 
       
   760     def cantcommit(self):
       
   761         self.prologue(T_CANTCOMMIT)
       
   762         print CANTCOMMIT_HEAD
       
   763         self.errordetail()
       
   764         print CANTCOMMIT_TAIL
       
   765 
       
   766     def errordetail(self):
       
   767         if PASSWORD and self.ui.password != PASSWORD:
       
   768             emit(NEED_PASSWD)
       
   769         if not self.ui.log:
       
   770             emit(NEED_LOG)
       
   771         if not self.ui.author:
       
   772             emit(NEED_AUTHOR)
       
   773         if not self.ui.email:
       
   774             emit(NEED_EMAIL)
       
   775 
       
   776     def commit(self, entry):
       
   777         file = entry.file
       
   778         # Normalize line endings in body
       
   779         if '\r' in self.ui.body:
       
   780             self.ui.body = re.sub('\r\n?', '\n', self.ui.body)
       
   781         # Normalize whitespace in title
       
   782         self.ui.title = ' '.join(self.ui.title.split())
       
   783         # Check that there were any changes
       
   784         if self.ui.body == entry.body and self.ui.title == entry.title:
       
   785             self.error("You didn't make any changes!")
       
   786             return
       
   787 
       
   788         # need to lock here because otherwise the file exists and is not writable (on NT)
       
   789         command = interpolate(SH_LOCK, file=file)
       
   790         p = os.popen(command)
       
   791         output = p.read()
       
   792 
       
   793         try:
       
   794             os.unlink(file)
       
   795         except os.error:
       
   796             pass
       
   797         try:
       
   798             f = open(file, 'w')
       
   799         except IOError, why:
       
   800             self.error(CANTWRITE, file=file, why=why)
       
   801             return
       
   802         date = time.ctime(now)
       
   803         emit(FILEHEADER, self.ui, os.environ, date=date, _file=f, _quote=0)
       
   804         f.write('\n')
       
   805         f.write(self.ui.body)
       
   806         f.write('\n')
       
   807         f.close()
       
   808 
       
   809         import tempfile
       
   810         tf = tempfile.NamedTemporaryFile()
       
   811         emit(LOGHEADER, self.ui, os.environ, date=date, _file=tf)
       
   812         tf.flush()
       
   813         tf.seek(0)
       
   814 
       
   815         command = interpolate(SH_CHECKIN, file=file, tfn=tf.name)
       
   816         log("\n\n" + command)
       
   817         p = os.popen(command)
       
   818         output = p.read()
       
   819         sts = p.close()
       
   820         log("output: " + output)
       
   821         log("done: " + str(sts))
       
   822         log("TempFile:\n" + tf.read() + "end")
       
   823 
       
   824         if not sts:
       
   825             self.prologue(T_COMMITTED)
       
   826             emit(COMMITTED)
       
   827         else:
       
   828             self.error(T_COMMITFAILED)
       
   829             emit(COMMITFAILED, sts=sts)
       
   830         print '<PRE>%s</PRE>' % escape(output)
       
   831 
       
   832         try:
       
   833             os.unlink(tf.name)
       
   834         except os.error:
       
   835             pass
       
   836 
       
   837         entry = self.dir.open(file)
       
   838         entry.show()
       
   839 
       
   840 wiz = FaqWizard()
       
   841 wiz.go()