pyrite-publisher-2.1.1/0040755000175000000620000000000007514642022013472 5ustar robstaffpyrite-publisher-2.1.1/PyritePublisher/0040755000175000000620000000000007514642022016624 5ustar robstaffpyrite-publisher-2.1.1/PyritePublisher/_Doc.c0100644000175000000620000001376507260723000017637 0ustar robstaff/* * _Doc.c - Python module to do DOC e-text (de)compression * * The original version of this algorithm is found in makedoc7.cpp by * Harold Bamford, Rick Bram, and Pat Bierne. * * Rewritten in C and pythonified by Rob Tillotson . * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee or royalty is * hereby granted, provided that the above copyright notice appear in * all copies and that both the copyright notice and this permission * notice appear in supporting documentation or portions thereof, * including modifications, that you you make. * * THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! * * $Id: _Doc.c,v 1.1 2001/03/29 21:15:44 rob Exp $ */ #include "stdio.h" #include "stdlib.h" #include "Python.h" #define DISP_BITS 11 #define COUNT_BITS 3 #define BUFSIZE 6000 #define MAXTEXTSIZE 4096 /* The original code uses a C++ class to handle the compressed stream. */ typedef struct { unsigned char *buf; int len; int bSpace; } Buffer; static unsigned char* memfind(unsigned char* t, int t_len, unsigned char* m, int m_len) { int i; for (i = t_len - m_len + 1 ; i>0; i--, t++) if (t[0]==m[0] && memcmp(t,m,m_len)==0) return t; return 0; } static unsigned int issue(Buffer *b, unsigned char src) { int iDest = b->len; unsigned char *dest = b->buf; if (b->bSpace) { if (src >= 0x40 && src <= 0x7f) dest[iDest++] = src ^ 0x80; else { dest[iDest++] = ' '; if (src < 0x80 && (src == 0 || src > 8)) dest[iDest++] = src; else { dest[iDest++] = 1; dest[iDest++] = src; } } b->bSpace = 0; } else { if (src == ' ') b->bSpace = 1; else { if (src < 0x80 && (src == 0 || src > 8)) dest[iDest++] = src; else { dest[iDest++] = 1; dest[iDest++] = src; } } } b->len = iDest; return iDest; } static unsigned int doc_compress(Buffer *b) { int i, j; unsigned char *pBuffer; unsigned char *pHit; unsigned char *pPrevHit; unsigned char *pTestHead; unsigned char *pTestTail; unsigned char *pEnd; unsigned int dist, compound, k; pHit = pPrevHit = pTestHead = pBuffer = b->buf; pTestTail = pTestHead+1; pEnd = b->buf + b->len; b->buf = malloc(6000); b->len = 0; for (; pTestHead != pEnd; pTestTail++) { if (pTestHead - pPrevHit > ((1 << DISP_BITS)-1)) pPrevHit = pTestHead - ((1 << DISP_BITS)-1); pHit = memfind(pPrevHit, pTestTail - pPrevHit, pTestHead, pTestTail - pTestHead); if (pHit == 0) fprintf(stderr, "!!bug!!"); if (pHit == 0 || pHit == pTestHead || pTestTail-pTestHead > (1 << COUNT_BITS)+2 || pTestTail == pEnd) { if (pTestTail-pTestHead < 4) { issue(b, pTestHead[0]); pTestHead++; } else { if (b->bSpace) { b->buf[b->len++] = ' '; b->bSpace = 0; } dist = pTestHead - pPrevHit; compound = (dist << COUNT_BITS) + pTestTail-pTestHead-4; b->buf[b->len++] = 0x80 + (compound >> 8); b->buf[b->len++] = compound & 0xff; pTestHead = pTestTail - 1; } pPrevHit = pBuffer; } else { pPrevHit = pHit; } if (pTestTail == pEnd) pTestTail--; } if (b->bSpace) b->buf[b->len++] = ' '; for (i=k=0; i < b->len; i++, k++) { b->buf[k] = b->buf[i]; if (b->buf[k] >= 0x80 && b->buf[k] < 0xc0) b->buf[++k] = b->buf[++i]; else if (b->buf[k] == 1) { b->buf[k+1] = b->buf[i+1]; while (i + 2 < b->len && b->buf[i+2] == 1 && b->buf[k] < 8) { b->buf[k]++; b->buf[k+b->buf[k]] = b->buf[i+3]; i += 2; } k += b->buf[k]; i++; } } free(pBuffer); b->len = k; return k; } static unsigned int doc_uncompress(Buffer *b) { unsigned char *pOut; unsigned char *in_buf; unsigned char *out_buf; int i, j, m, n; unsigned int c; pOut = malloc(6000); in_buf = b->buf; out_buf = pOut; for (j=i=0; j < b->len; ) { c = in_buf[j++]; if (c > 0 && c < 9) while (c--) out_buf[i++] = in_buf[j++]; else if (c < 0x80) out_buf[i++] = c; else if (c >= 0xc0) { out_buf[i++] = ' '; out_buf[i++] = c ^ 0x80; } else { c <<= 8; c += in_buf[j++]; m = (c & 0x3fff) >> COUNT_BITS; n = c & ((1 << COUNT_BITS)-1); n += 3; while (n--) { out_buf[i] = out_buf[i-m]; i++; } } } free(b->buf); b->buf = pOut; b->len = i; return i; } static PyObject *compress_str(PyObject *self, PyObject *args) { char *str; int len; Buffer b; PyObject *o; if (!PyArg_ParseTuple(args, "s#", &str, &len)) return NULL; if (len > MAXTEXTSIZE || len < 1) { PyErr_SetString(PyExc_ValueError, "string must be 1 to 4096 bytes long"); return NULL; } b.buf = malloc(BUFSIZE); memcpy(b.buf, str, len); b.len = len; b.bSpace = 0; doc_compress(&b); o = Py_BuildValue("s#", b.buf, b.len); free(b.buf); return o; } static PyObject *uncompress_str(PyObject *self, PyObject *args) { char *str; int len; Buffer b; PyObject *o; if (!PyArg_ParseTuple(args, "s#", &str, &len)) return NULL; if (len > MAXTEXTSIZE || len < 1) { PyErr_SetString(PyExc_ValueError, "string must be 1 to 4096 bytes long"); return NULL; } b.buf = malloc(BUFSIZE); memcpy(b.buf, str, len); b.len = len; b.bSpace = 0; doc_uncompress(&b); o = Py_BuildValue("s#", b.buf, b.len); free(b.buf); return o; } static PyMethodDef _DocMethods[] = { { "compress", compress_str, 1 }, { "uncompress", uncompress_str, 1 }, { NULL, NULL } }; void init_Doc() { (void) Py_InitModule("_Doc", _DocMethods); } pyrite-publisher-2.1.1/PyritePublisher/__init__.py0100644000175000000620000000000007260723000020712 0ustar robstaffpyrite-publisher-2.1.1/PyritePublisher/config.py0100644000175000000620000001203607450521062020441 0ustar robstaff# # $Id: config.py,v 1.3 2002/03/28 04:55:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: config.py,v 1.3 2002/03/28 04:55:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import shlex, os, sys # config file parser... commands end with a ; or eof, calls a # function (or really a callable object, probably) with each one class Error(Exception): pass def read_config(filename, cmd_proc): lex = shlex.shlex(open(filename), filename) while 1: # collect a command cmd = [] while 1: tok = lex.get_token() if not tok or tok == ';': break cmd.append(tok) try: if cmd: cmd_proc(cmd) except Error, s: sys.stderr.write(lex.error_leader()) sys.stderr.write(str(s)) sys.stderr.write('\n') sys.stderr.flush() if not tok: break class CommandProcessor: def __call__(self, argv): cmd = argv[0] m = 'cmd_%s' % cmd if hasattr(self, m): getattr(self, m)(argv) # ------------------------------------------------------------ # Pyrite Publisher specific stuff starts here # ------------------------------------------------------------ def maybe_unquote(s): if len(s) > 1 and s[0] in '"\'' and s[0] == s[-1]: return s[1:-1] else: return s class PPConfigProcessor(CommandProcessor): def __init__(self, plugins={}, instance=None): self.plugins = plugins self.vars = {} self.instance = instance def load(self, fn): if os.path.isfile(fn): read_config(fn, self) def has_key(self, k): return self.vars.has_key(k) def get(self, k, d=None): return self.vars.get(k,d) def __getitem__(self, k): return self.vars[k] def cmd_set(self, argv): argv = argv[1:] if len(argv) == 5: # set . = if argv[1] != '.' or argv[3] != '=': raise Error, 'set syntax error' pname = maybe_unquote(argv[0]) if not self.plugins.has_key(pname): raise Error, 'plugin %s not found' % pname p = self.plugins[pname] propname = maybe_unquote(argv[2]) if not p.has_property(propname): raise Error, 'plugin %s has no property %s' % (pname, propname) pd = p.getPropertyInfo(propname) if pd.read_only: raise Error, 'property %s.%s is read-only' % (pname, propname) value = eval(argv[4], p.__dict__.copy()) if pd.indexed: v = getattr(p, propname) if type(v) != type([]): raise Error, 'huh? property %s.%s is indexed, but is not a list?' % (pname, propname) v.append(value) else: setattr(p, propname, value) elif len(argv) == 3: # set = if argv[1] != '=': raise Error, 'set syntax error' self.vars[maybe_unquote(argv[0])] = eval(argv[2]) else: raise Error, 'set syntax error' def cmd_priority(self, argv): argv = argv[1:] if len(argv) < 2 or len(argv) > 4: raise Error, 'priority syntax error' pname = maybe_unquote(argv[0]) if not self.plugins.has_key(pname): raise Error, 'plugin %s not found' % pname plugin = self.plugins[pname] argv = argv[1:] pri = eval(argv[0]) if type(pri) != type(1): raise Error, 'priority is not an integer' argv = argv[1:] inp = outp = None if argv: inp = maybe_unquote(argv[0]) argv = argv[1:] if argv: outp = maybe_unquote(argv[0]) plugin.set_priority(pri, inp, outp) def cmd_inputfilter(self, argv): argv = argv[1:] if len(argv) < 3: raise Error, 'inputfilter syntax error' intype = maybe_unquote(argv[0]) outtype = maybe_unquote(argv[1]) cmd = maybe_unquote(argv[2]) self.instance.input_filters[(intype,outtype)] = cmd pyrite-publisher-2.1.1/PyritePublisher/dbprotocol.py0100644000175000000620000001757707450067746021400 0ustar robstaff# # $Id: dbprotocol.py,v 1.1 2002/03/26 12:56:06 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """Pyrite Publisher Database Protocol Support This module provides support for the Pyrite Publisher protocol 'columnar-database', which is used for simple flat-file databases like those found in CSV files and Palm applications like JFile, MobileDB, and Handbase. Primarily, it provides classes for handling metadata about the columns in the database. The field types supported by this module, their identifiers (defined in this module as constants), and their internal representations are as follows: FLD_STRING - a string Represented internally by a string. FLD_INT - an integer Represented internally by an integer. FLD_FLOAT - a floating-point number Represented internally by a float. FLD_BOOLEAN - a true or false value Represented internally by an integer 0 or 1. FLD_DATE - a date (without time) Represented internally by a string. FLD_TIME - a time (without date) Represented internally by a string. Objects implementing the columnar-database protocol have the following methods: define_fields(speclist) REQUIRED. Called by the upstream plugin before any records are fed downstream. The parameter is a list of FieldSpec objects which describe the columns in the database. feed_record(list) REQUIRED. Called by the upstream plugin once for each record. The parameter is a list of values contained in that record, in their internal representation. """ __version__ = '$Id: dbprotocol.py,v 1.1 2002/03/26 12:56:06 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import os import config FLD_STRING = 'string' FLD_INT = 'int' FLD_FLOAT = 'float' FLD_BOOLEAN = 'boolean' FLD_DATE = 'date' FLD_TIME = 'time' class FieldSpec: """Columnar database field specification. """ def __init__(self, name, type=FLD_STRING, **kw): self.name = name self.type = type self.max_length = 0 for k,v in kw.items(): setattr(self, k, v) def __str__(self): s = '' return s def __repr__(self): return self.__str__() def setFromConfig(self, k, v): setattr(self, k, v) def __getattr__(self, k, v): return None # field "name" type ; # values "name" ... ; _type_names = { 'string' : FLD_STRING, 'str': FLD_STRING, 'int': FLD_INT, 'integer': FLD_INT, 'float': FLD_FLOAT, 'bool': FLD_BOOLEAN, 'boolean': FLD_BOOLEAN, 'checkbox': FLD_BOOLEAN, 'check': FLD_BOOLEAN, 'toggle': FLD_BOOLEAN, } class DBSpecCmdProc(config.CommandProcessor): def __init__(self): self.fields = {} self.field_order = [] def load(self, fn): if os.path.isfile(fn): config.read_config(fn, self) def cmd_field(self, argv): argv = argv[1:] if len(argv) < 1: raise config.Error, 'field syntax error' name = config.maybe_unquote(argv[0]) argv = argv[1:] fspec = FieldSpec(name) if not argv: fspec.setFromConfig("type", FLD_STRING) else: t = config.maybe_unquote(argv[0]) if not _type_names.has_key(t): raise config.Error, 'unknown field type %s' % t fspec.setFromConfig("type", _type_names[t]) argv = argv[1:] while argv: a = config.maybe_unquote(argv[0]) argv = argv[1:] if not argv: fspec.setFromConfig(a, 1) else: v = config.maybe_unquote(argv[0]) argv = argv[1:] fspec.setFromConfig(a, v) self.fields[name] = fspec self.field_order.append(fspec) def cmd_values(self, argv): # values for popup list argv = argv[1:] if not argv: raise config.Error, "not enough arguments for 'values'" try: fnum = int(argv[0]) fspec = self.field_order[fnum] except: fspec = self.fields[config.maybe_unquote(argv[0])] fspec.setFromConfig('values', map(config.maybe_unquote, argv[1:])) class TypeGuesser: """Type guesser for the columnar database protocol. This class implements a simple type-guesser which takes a series of records with string values and tries to guess what type the values *should* be. To accomplish this, it uses a set of regular expressions which match the string representations of other data types, and a set of type-inclusion rules. (These rules are used to handle situations where more than one regular expression might match a particular value -- for example, an integer is also a valid floating point value.) As records are fed to it, it updates its guesses as to the type of each column. If the values in a column don't consistently match any particular type, it will be declared to be a string. At the same time, it keeps track of field lengths to determine the largest required field size, and will in the future be expanded to collect even more information about the data passing through it. """ def __init__(self, fields=[], rx_types=[], included_types={}, converters={}): self.fieldspecs = fields self.rx_types = rx_types self.included_types = included_types self.converters = converters def guess(self, s): for rx, typ in self.rx_types: if rx.match(s): return typ return FLD_STRING def __call__(self, fields): if not self.fieldspecs: self.fieldspecs = FieldSpec('', None) # now try to guess types for x in range(len(fields)): f = fields[x] t = self.fieldspecs[x] if t.type != FLD_STRING: t0 = self.guess(f) if t.type is None: print t, t0, f t.type = t0 elif t.type != t0: print t, t0, f if self.included_types.has_key((t.type,t0)): t.type = t0 else: t.type = FLD_STRING if len(f) > self.fieldspecs[x].max_length: self.fieldspecs[x].max_length = len(f) def convert_rec(self, rec): ret = [] for x in range(len(rec)): conv = self.converters.get(self.fieldspecs[x].type) if conv: ret.append(conv(rec[x])) else: ret.append(rec[x]) return ret if __name__ == '__main__': import sys from pprint import pprint c = DBSpecCmdProc() c.load(sys.argv[1]) pprint(c.field_order) pyrite-publisher-2.1.1/PyritePublisher/doc_compress.py0100644000175000000620000001045007260723000021645 0ustar robstaff# # $Id: doc_compress.py,v 1.1 2001/03/29 21:15:44 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """Doc compression in pure Python. """ __version__ = '$Id: doc_compress.py,v 1.1 2001/03/29 21:15:44 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import string COUNT_BITS = 3 DISP_BITS = 11 def compress(s): # optimizations # this cut off about 0.1 sec/call _find = string.find out = [] space = 0 # first phase: sliding window sstart = 0 i = 0 imax = len(s) while 1: if i >= imax: break if (i - sstart) > 2047: sstart = i - 2047 #ts = s[i:i+10] # substring to search for #while len(ts) >= 3: # f = _find(s, ts, sstart, i) # if f >= 0: break # ts = ts[:-1] # see that code above? it's the obvious way, but it's slow. # what it does is basically do string.find on the data ahead # of the current position, on an ever-shrinking buffer. With # that code, this function took around 1.6 seconds per call # (on a Cyrix M2-266 [207 MHz/83 MHz bus/1MB cache]). # # The below is a bit different: basically, it takes advantage # of the fact that if we don't have a length 3 substring, we # can't possibly have a length 4 substring, and so on. Thus, # it first looks for a length 3 substring (we don't care about # anything shorter). If it finds one, it then attempts to # find a length 4 substring between that position and the # current location, and so on. # # I suspect the primary benefit of this is to avoid 6 out of 7 # calls to string.find every time we aren't looking at a # compressible string. At any rate, changing this code cuts # the time to about 0.38 seconds per call. e = 3 ns = sstart ts = s[i:i+e] while (imax - i) >= e and e <= 10: f = _find(s, ts, ns, i) if f < 0: break e = e + 1 ts = s[i:i+e] ns = f e = e - 1 #if len(ts) >= 3: #we have a match, f is the location and len(ts) is the length # l = len(ts) # ns = f if e >= 3: dist = i - ns byte = (dist << 3) | (e-3) if space: out.append(32) space = 0 out.append(0x80 | (byte >> 8)) out.append(byte & 0xff) i = i + e else: c = ord(s[i]) i = i + 1 if space: if c >= 0x40 and c <= 0x7f: out.append(c | 0x80) else: out.append(32) if c < 0x80 and (c == 0 or c > 8): out.append(c) else: out.append(1) out.append(c) space = 0 else: if c == 32: space = 1 else: if c < 0x80 and (c == 0 or c > 8): out.append(c) else: out.append(1) out.append(c) if space: out.append(32) # second phase: look for repetitions of '1 ' and combine up to 8 of them. # interestingly enough, in regular text this hardly makes a difference. return string.join(map(chr, out),'') def uncompress(s): s = map(ord, s) x = 0 o = [] try: while 1: c = s[x] x = x + 1 if c > 0 and c < 9: # just copy that many bytes for y in range(0, c): o.append(s[x]) x = x + 1 elif c < 128: # a regular ascii character o.append(c) elif c >= 0xc0: # a regular ascii character with a space before it o.append(32) o.append(c & 0x7f) else: # a compressed sequence c = c << 8 c = c | s[x] x = x + 1 m = (c & 0x3fff) >> COUNT_BITS n = (c & ((1 << COUNT_BITS)-1)) + 3 for y in range(0, n): o.append(o[len(o)-m]) except IndexError: pass return string.join(map(chr, o), '') pyrite-publisher-2.1.1/PyritePublisher/doc_database.py0100644000175000000620000001531107450164551021572 0ustar robstaff# # $Id: doc_database.py,v 1.6 2002/03/26 21:34:33 rob Exp $ # # Copyright 1998-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: doc_database.py,v 1.6 2002/03/26 21:34:33 rob Exp $' __copyright__ = 'Copyright 1998-2001 Rob Tillotson ' import string, operator, struct, time, sys import prc try: import _Doc _compress = _Doc.compress _uncompress = _Doc.uncompress except: import doc_compress _compress = doc_compress.compress _uncompress = doc_compress.uncompress def header_pack(version, spare, storylen, textrecs, recsize, position, sizes=[]): raw = struct.pack('>hhlhhl', version, spare, storylen, textrecs, recsize, position) for s in sizes: raw = raw + struct.pack('>h', s) return raw def header_unpack(raw): fld = struct.unpack('>hhlhhl', raw[:16]) raw = raw[16:] sizes = [] while raw: r = raw[:2] raw = raw[2:] if len(r) != 2: break sizes.append(struct.unpack('>h',r)[0]) return fld + (sizes,) def bookmark_pack(text, pos): return struct.pack('>16sl', text[:15]+'\0', pos) def bookmark_unpack(s): text, pos = struct.unpack('>16sl', s[:20]) text = string.split(text,'\0',1)[0] return text, pos class DocWriteStream: def __init__(self, filename, name, creator=None, type=None, flags=None, version=None, category=0, compress=1, **kw): self.filename = filename self.name = name self.creator = creator self.type = type self.flags = flags self.version = version self.compress = compress self.category = category self.create_kw = kw self.appinfo = '' self.bookmarks = [] self.records = [] self.opened = 1 self.buf = '' self.len = 0 def has_feature(self, f): return None def set_appinfo(self, a=''): self.appinfo = a def bookmark(self, title, pos=None): if pos is None: pos = self.len self.bookmarks.append((title,pos)) def annotate(self, text, title='', pos=None): pass def __output(self): while len(self.buf) >= 4096: b = self.buf[:4096] self.buf = self.buf[4096:] if self.compress: self.records.append(_compress(b)) else: self.records.append(b) def write(self, data): self.buf = self.buf + data self.len = self.len + len(data) self.__output() def writelines(self, list): for l in list: self.write(l) def close(self): self.__output() if self.buf: if self.compress: self.records.append(_compress(self.buf)) else: self.records.append(self.buf) if type(self.filename) == type(''): db = prc.File(self.filename, read=0, write=1, info={'name': self.name, 'creator': self.creator, 'type': self.type, 'version': self.version, # flags? }) else: db = self.filename # assume it is a prc object if not a string db.updateDBInfo({'name': self.name, 'creator': self.creator, 'type': self.type, 'version': self.version, }) # header db.setRecord(0x40, 0x6f8000, self.category, header_pack(2, 0, self.len, len(self.records), 4096, 0)) uid = 0x6f8001 for r in self.records: db.setRecord(0x40, uid, 0, r) uid = uid + 1 if len(self.bookmarks): for t, p in self.bookmarks: db.setRecord(0x40, uid, 0, bookmark_pack(t,p)) uid = uid + 1 if self.appinfo: db.setAppBlock(self.appinfo) self.opened = 0 db.close() class DocReadStream: def __init__(self, file=None, db=None): if db is not None: self.db = db else: self.db = prc.File(file,read=1,write=0) self.rec = 0 self.buf = '' raw, idx, id, attr, category = self.db.getRecord(0) self.category = category (self.version, self.spare, self.storylen, self.textrecs, self.recsize, self.position, self.sizes) = header_unpack(raw) def __next(self): if self.rec >= self.textrecs: return None else: self.rec = self.rec + 1 raw, idx, id, attr, category = self.db.getRecord(self.rec) if self.version >= 2: r = _uncompress(raw) else: r = raw self.buf = self.buf + r return r def read(self, nbytes=0): if not self.buf: if self.__next() is None: return '' if nbytes: e = self.buf[:nbytes] self.buf = self.buf[nbytes:] return e else: e = self.buf self.buf = '' return e def readline(self): while not '\n' in self.buf: if self.__next() is None: b = self.buf self.buf = '' return b j = string.find(self.buf, '\n') e = self.buf[:j+1] self.buf = self.buf[j+1:] return e def readlines(self): l = [] while 1: m = self.readline() if not m: break l.append(m) return l def tell(self): return (self.rec * self.recsize) - len(self.buf) def seek(self, pos, whence=0): if whence == 1: pos = self.tell() + pos elif whence == 2: pos = self.storylen + pos if pos >= self.storylen: pos = self.storylen self.rec = int(pos / self.recsize) + 1 p = pos % self.recsize raw, idx, id, attr, category = self.db.getRecord(self.rec) if self.version >= 2: self.buf = _uncompress(raw) else: self.buf = raw def close(self): self.db.close() self.rec = 0 self.buf = '' def has_feature(self, f): return None def get_bookmarks(self): l = [] r = self.textrecs+1 end = self.db.getRecords() for x in range(r, end): raw, idx, id, attr, category = self.db.getRecord(x) text, pos = bookmark_unpack(raw) l.append((pos, text)) l.sort() return l def __getattr__(self, k): if k == 'title': return self.db.getDBInfo()['name'] elif k == 'bookmarks': return self.get_bookmarks() else: raise AttributeError, k pyrite-publisher-2.1.1/PyritePublisher/dtkmain.py0100644000175000000620000005005707514640514020635 0ustar robstaff# # $Id: dtkmain.py,v 1.13 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: dtkmain.py,v 1.13 2002/07/15 21:40:28 rob Exp $' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys, os, string from pprint import pprint VERSION = '2.1.0' DATE = "2001/12/26" IDENT = "Pyrite Publisher v%s (%s) by Rob Tillotson " % (VERSION, DATE) import plugin, dtkplugins, config from plugin import LOG_DEBUG, LOG_WARNING, LOG_NORMAL, LOG_ERROR _option = plugin.CLIOptionDescriptor main_options = [ _option('help', 'h', 'help', 'Get help', boolean=1), _option('list_plugins', 'l', 'list-plugins', 'List available plugins', boolean=1), _option('force_plugins', 'P', None, 'Force a specific filter plugin', vtype='NAME', multiple=1), _option('list_properties', None, 'list-properties', 'List plugin properties', boolean=1), _option('list_tasks', None, 'list-tasks', 'List tasks', boolean=1), _option('task', 'T', 'task', 'Select a task', vtype='NAME'), ] class ConversionError(Exception): pass class Placeholder: pass # The way plugin autoselection works is more or less as follows: # # - input plugins are chosen based on their ability to handle the # filename presented to them (normally this should be done # automatically) # # the input plugin, after being given the filename, will return # the name of a mimetype for the file. (This is, in effect, an # interface name, for purposes of the chaining described below.) # # - parsers and assemblers are chained according to pairs of # interfaces. They can report to the main program which pairs # of (input, output) they can handle, and a priority level # which can be used to select an interface when there are more # than one choice. # # - output plugins can report which input interfaces they can # handle. # # To autoselect, first an input plugin is selected, and the mimetype # of the file determined. Then, the program will determine all # possible chains of parser/assembler/output plugins from that mimetype. # If there is more than one it will pick the "best" one. # # Since this architecture supports a variable chain of plugins (except # for the input, and the fact that it must end with an output) the # method of "stacking" them will now be more uniform... # # Plugins will communicate via "events" which are just method calls on # an object. Calls will propagate from input to output; the "driver" # will be wedged between the input and the first plugin, feeding data # into the first plugin and letting everything cascade from there. # # The way this will work is that at the beginning of processing, each # plugin will have its "open" method called with the chain as argument, # and this should return an API object which will be handed to the # *previous* plugin in the chain once all plugins are open. # Path finding algorithm from http://www.python.org/doc/essays/graphs.html def find_all_paths(graph, start, end, path=[]): path = path + [start] if start == end: return [path] if not graph.has_key(start): return [] paths = [] for node in graph[start]: if node not in path: newpaths = find_all_paths(graph, node, end, path) for newpath in newpaths: paths.append(newpath) return paths class Chain: def __init__(self, plugins): self.plugins = plugins self.collect_path_info() self.path = [] def debug(self): pprint(self.__f_links) print def pathfind(self, start, end): _inputs = {} _outputs = {} for plugin in self.plugins: for pri, inp, outp in plugin.get_supported_links(): if not _inputs.has_key(inp): _inputs[inp] = [] if plugin not in _inputs[inp]: _inputs[inp].append((plugin,pri)) if not _outputs.has_key(outp): _outputs[outp] = [] if plugin not in _outputs[outp]: _outputs[outp].append(plugin) # now find pairs _pairs = {} for outp in _outputs.keys(): for outplug in _outputs[outp]: for inplug,pri in _inputs.get(outp,[]): if inplug != outplug: _pairs[(outplug,inplug)] = (pri, outp) # add terminating nodes for outplug in _outputs[None]: _pairs[(outplug,None)] = (0,None) for inp in _inputs.keys(): for inplug,pri in _inputs[inp]: _pairs[(inp,inplug)] = (pri, inp) # now we have the edges of a directed graph in which the vertices are # plugins... now we need to find all paths between the start and end _graph = {} for inp, outp in _pairs.keys(): if not _graph.has_key(inp): _graph[inp] = [] if outp not in _graph[inp]: _graph[inp].append(outp) paths = find_all_paths(_graph, start, end) # now figure out priorities and sort them ret = [] for path in paths: _p = path[:] pri = -len(path) # prioritize by length too for x in range(len(path)-1): p,t = _pairs[(_p[x],_p[x+1])] pri += p if t is not None: path[x+1] = (path[x+1],t) ret.append((pri, filter(lambda x: x is not None and type(x) != type(''), path))) ret.sort() return ret def collect_path_info(self): """Collects information about possible chains from plugins. Every plugin must have a 'get_supported_links' function which returns a list of valid chainlinks for this plugin, in the form: [ ( input, output, priority), ... ] Note that input plugins are special, because they can return different mimetypes, but all other plugins use this interface. Also note that the link specifications must be tuples, because they will be used as dictionary keys. If the output parameter is None this is an output plugin. """ links = {} r_links = {} for plugin in self.plugins: lks = plugin.get_supported_links() for pri, inp, outp in lks: if not links.has_key(inp): links[inp] = {} if not links[inp].has_key(outp): links[inp][outp] = [] links[inp][outp].append((pri, plugin)) if not r_links.has_key(outp): r_links[outp] = {} if not r_links[outp].has_key(inp): r_links[outp][inp] = [] r_links[outp][inp].append((pri, plugin)) self.__f_links = links self.__r_links = r_links def open(self, path, *a, **kw): self.path = path[:] path = path[:] path.reverse() last = None for plugin, protocol in path: last = apply(plugin.open, (self, last, protocol)+a, kw) return last def close(self): for plugin, protocol in self.path: if hasattr(plugin, 'close') and callable(plugin.close): plugin.close() self.path = [] def check_plugin(self, namelist, plugin, meth="handles_interface"): """Tests whether a plugin supports one of the named interfaces. """ l = filter(lambda x, p=plugin, m=meth: getattr(p,m) is not None, map(lambda x: x[1], namelist)) if not l: raise ConversionError, ("plugin '%s' does not support any of " + \ string.join(map(lambda x: x[1], namelist), ',')) \ % repr(plugin) else: return l[0] def choose_best_plugin(self, namelist, plugins, meth="handles_interface"): """Choose the best plugin to handle one of several named interfaces. """ nl = namelist[:] nl.sort() nl.reverse() for mpri, mtype in namelist: l = map(lambda x, t=mtype, m=meth: (getattr(x,m)(t), x), plugins) l.sort() if l: return (l[-1][1], mtype) raise ConversionError, ("can't find a match for " + string.join(map(lambda x: x[1], namelist), ',')) # A "task" is like a user interface "macro". It contains a list # of plugins to force and properties to set; the idea is that # the user can select a task and have a bunch of other options # set appropriately. Tasks are registered by plugins; the validate() # method is used to check whether the task is possible to do # (so a plugin can register tasks which rely on optional plugins). # Note that the plugin that registers the task isn't forced by # default... of course it can put its own name in the force list. class TaskDefinition: def __init__(self, api, title, plugins=[], properties={}): self.api = api self.title = title self.plugins = plugins self.properties = properties def validate(self): # return false if this task can't be done # (probably because all the needed plugins aren't there) return 1 class PPInstance: def __init__(self): if '--DEBUG' in sys.argv: self.loglevel = 3 sys.argv.remove('--DEBUG') else: self.loglevel = 1 self.wildcards = {} self.tasks = {} self.plugins = dtkplugins.load_all_plugins(self) self.input_filters = {} self.conf = config.PPConfigProcessor(self.plugins,self) self.conf.load(os.path.expanduser('~/.pyrpubrc')) self.chain = Chain(self.plugins.values()) self.preferred_installer = '' # API. def log(self, level, name, string): if level <= self.loglevel: sys.stdout.write("%s: %s\n" % (name, string)) def has_plugin(self, n): return self.plugins.has_key(n) def get_plugin(self, n): return self.plugins[k] def find_installer(self): # figure out what installer the user wants to use, or return none. inst_plugins = filter(lambda x: hasattr(x, 'installable_check'), self.plugins.values()) # first see if the user specified a preference if self.preferred_installer: pref = string.lower(self.preferred_installer) else: pref = string.lower(self.conf.get('installer', '')) if pref: for p in inst_plugins: if string.lower(p.installer_name) == pref or \ string.lower(p.name) == pref: if not p.installable_check(): self.log(plugin.LOG_ERROR, 'main', 'cannot install using preferred method "%s"' % pref) else: return p break # if we still haven't picked one, sort by priority and choose the best one inst_plugins = filter(lambda x: x.installable_check(), inst_plugins) if not inst_plugins: return None inst_plugins.sort(lambda x,y: cmp(x.installer_priority, y.installer_priority)) #print "chose installer", inst_plugins[-1].name return inst_plugins[-1] def register_task(self, key, *a, **kw): t = TaskDefinition(self, *a, **kw) if t.validate(): self.tasks[key] = t def register_wildcard(self, wildcard, description): # GUI support function; registers wildcards for file selector self.wildcards[wildcard] = description def find_plugins_with_link(self, inp, outp=None): return [ p for p in self.plugins.values() if p.has_link(inp, outp) ] def set_property_all(self, prop, value): for p in self.plugins.values(): if p.has_property(prop): setattr(p, prop, value) # /API def find_input_plugin(self, fn): """Find an appropriate input plugin. Finds the plugin which will handle the given filename, and returns it, or None if no plugin can handle the given file. """ l = filter(lambda x: x is not None and x[0] is not None, map(lambda x, n=fn: hasattr(x,"handles_filename") and (x.handles_filename(n),x) or None, self.plugins.values())) l.sort() if l: return l[-1][1] else: return None def find_paths(self, mimetypes, forced_plugins=[]): """Find conversion paths. Finds possible conversion paths beginning with one of the specified mimetypes, which include all the forced plugins. Returns the pathset from the first matching mimetype. """ # XXX need to come up with some way to handle multiple mimetypes # XXX in case a gui wants to prompt a user to choose a path paths = None for mimetype in mimetypes: paths = self.chain.pathfind(mimetype, None) # filter out paths which don't contain forced plugins for pi in forced_plugins: paths = [ p for p in paths if pi in [x for x,y in p[1]] ] if paths: break paths.sort() return (mimetype, paths) def find_best_path(self, mimetypes, forced_plugins=[]): mimetype, paths = self.find_paths(mimetypes, forced_plugins) if paths: return (mimetype, list(paths[-1][1])) else: return (None, None) def convert_file(self, fn, forced_plugins=[]): # first, pick an input plugin iplug = self.find_input_plugin(fn) if iplug is None: raise ConversionError, "no plugin knows how to handle '%s'" % fn mimetypes, basename = iplug.open_input(fn) # the input plugin can return a list of mimetypes, which will be # tried in order to find a path. mimetype, path = self.find_best_path(mimetypes, forced_plugins) if path is None: mt = mimetypes[:] if len(mt) > 1: mt.insert(len(mt)-1, 'or') raise ConversionError, "couldn't find a way to convert from %s" % string.join(mt) # DEBUG print "("+string.join(map(lambda x: x.name, (iplug,)+tuple([x[0] for x in path])),',')+")", sys.stdout.flush() # /DEBUG head = self.chain.open(path, basename) input = iplug.open(self.chain, head, "INPUT") input.go(mimetype) self.chain.close() input.close_input() class PPCLI(PPInstance): def __call__(self, argv): progname = argv[0] argv = argv[1:] #print argv options = Placeholder() oparser = plugin.CLIOptionParser(main_options, options, self.plugins.values()) options.help = 0 options.list_plugins = 0 options.list_properties = 0 options.list_tasks = 0 options.force_plugins = [] # pick a task using argv[0] if self.tasks.has_key(os.path.basename(progname)): options.task = os.path.basename(progname) else: options.task = '' argv = oparser(argv) # no longer need partial=1 forced_plugins = [] # -T option if options.task: if not self.tasks.has_key(options.task): print 'error: no task called "%s". (use the --list-tasks option to see them)' return task = self.tasks[options.task] print "Selected task: %s" % task.title forced_plugins = [self.plugins.get(name,None) for name in task.plugins[:]] if None in forced_plugins: print 'error: all plugins needed for the selected task are not available' return for k, v in task.properties.items(): self.set_property_all(k, v) # it would be nice if this didn't override command line options # and config file variables, but that seems like it will be # a lot of work. # -P option if options.force_plugins: for arg in options.force_plugins: for pname in map(string.strip, string.split(arg,',')): if not self.plugins.has_key(pname): print self.plugins.keys() print "error: no such plugin '%s'" % pname else: forced_plugins.append(self.plugins[pname]) if options.list_plugins: self.cmd_list_plugins() return if options.help: self.cmd_help(oparser) return if options.list_properties: self.cmd_list_properties() return if options.list_tasks: self.cmd_list_tasks() return for fn in argv: print "Converting %s..." % fn, sys.stdout.flush() try: self.convert_file(fn, forced_plugins) except ConversionError, err: print "error: %s" % err continue except IOError, err: print "i/o error: %s" % err continue def cmd_list_tasks(self): print IDENT print if not self.tasks: print "No tasks are defined." return print "Task Description" print "---- -----------" keys = self.tasks.keys() keys.sort() for key, task in [ (key, self.tasks[key]) for key in keys ]: print "%-15s %s" % (key, task.title[:62]) def cmd_list_plugins(self): ky = self.plugins.keys() ky.sort() fplugins = [ self.plugins[k] for k in ky if self.plugins[k].get_supported_links() ] oplugins = [ self.plugins[k] for k in ky if not self.plugins[k].get_supported_links() ] print IDENT print if not fplugins and not oplugins: print "No plugins installed?" if fplugins: print "Filter plugins:" print print "Plugin Description" print "------ -----------" for p in fplugins: print "%-15s %-59s" % (p.name, string.split(p.description, '\n')[0][:60]) print if oplugins: print "Other plugins:" print print "Plugin Description" print "------ -----------" for p in oplugins: print "%-15s %-59s" % (p.name, string.split(p.description, '\n')[0][:60]) print return def cmd_help(self, oparser): print IDENT print print oparser.help(main_options) print ky = self.plugins.keys() ky.sort() for k in ky: ip = self.plugins[k] if ip.getCLIOptionDescriptors(): print "Plugin: %s %s by %s <%s>" % (ip.name, ip.version, ip.author, ip.author_email) if ip.description: print string.split(ip.description,'\n')[0] print print oparser.help(ip.getCLIOptionDescriptors()) print return def cmd_list_properties(self): print IDENT print print "! = boolean property, # = multiple values allowed" print ky = self.plugins.keys() ky.sort() for k in ky: ip = self.plugins[k] props = filter(lambda x: not x.read_only, ip.getPropertyDescriptors()) if props: props.sort() print "Plugin: %s %s by %s <%s>" % (ip.name, ip.version, ip.author, ip.author_email) print for prop in props: if prop.indexed: ty = '#' elif prop.boolean: ty = '!' else: ty = ' ' d = string.split(prop.description,'\n')[0][:54] print " %-20s %s %s" % (prop.name, ty, d) print return pyrite-publisher-2.1.1/PyritePublisher/dtkplugins.py0100644000175000000620000002032607514640514021366 0ustar robstaff# # $Id: dtkplugins.py,v 1.14 2002/07/15 21:40:28 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """base types of plugins, etc. """ __version__ = '$Id: dtkplugins.py,v 1.14 2002/07/15 21:40:28 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import sys, os, urlparse, mimetypes, re import dtkmain from doc_database import DocWriteStream import urllib import plugin from plugin import LOG_ERROR, LOG_WARNING, LOG_DEBUG, LOG_NORMAL # ******************************************************************* # Callback/API object for plugins # ******************************************************************* #class DTKCallback(plugin.Callback): # def __init__(self, *a, **kw): # apply(plugin.Callback.__init__, (self,)+a, kw) # ******************************************************************* # Loader stuff # ******************************************************************* #PLUGINS = [ # 'plugin_HTML', 'plugin_TaggedText', 'plugin_Text', # 'plugin_RichReader', 'plugin_TealDoc', # 'plugin_ztxtoutput', # 'plugin_pdbinput', 'plugin_debugoutput', # ] def load_all_plugins(callback=None): if globals().has_key('__file__') and (__file__[-3:] == '.py' or __file__[-4:] in ('.pyc','.pyo')): path = os.path.split(__file__)[0] if path not in sys.path: sys.path.append(path) # figure out what plugins are here if not path: path = '.' # for listdir p = {} for fn, ext in map(os.path.splitext, os.listdir(path)): if ext in ['.py', '.pyc', '.pyo'] and fn[:7] == 'plugin_' and not p.has_key(fn): p[fn] = fn PLUGINS = p.keys() else: # if we weren't loaded from a file, use metadata previously gathered # instead. try: import pp_meta PLUGINS = ['PyritePublisher.'+x for x in pp_meta.plugin_modules] except: PLUGINS = [] path = '' pl = [ RawParserPlugin(api=callback) ] l = {} for p in pl: l[p.name] = p ldr = plugin.CallableLoader(callback) for p in ldr.load_plugins(PLUGINS, 'Plugin', api=callback): if p.usable(): # ignore plugins that claim not to work l[p.name] = p if path and path in sys.path: sys.path.remove(path) return l # ******************************************************************* # Input plugin base class and raw plugin # ******************************************************************* class DTKPlugin(plugin.PropertyMixin, plugin.CLIOptionMixin): links = [] features = [] def __init__(self, type='plugin', api=None): plugin.PropertyMixin.__init__(self,'Pyrite.Publisher.%s.%s' % (type, self.name)) plugin.CLIOptionMixin.__init__(self, []) self._add_property('name', 'The name of this plugin', read_only=1) self._add_property('version', 'The version of this plugin', read_only=1) self._add_property('author', 'The author of this plugin', read_only=1) self._add_property('author_email', "The author's email address", read_only=1) self._add_property('description', 'A description of this plugin', read_only=1) self.chain = None self.next = None self.api = api def usable(self): return 1 def log(self, s, level=plugin.LOG_NORMAL): self.api.log(level, self.name, s) def has_feature(self, f): return f in self.features def get_supported_links(self): return self.links def has_link(self, input, output=''): for pri, inp, outp in self.links: if (inp == input and outp == output) or \ (inp == input and not output): return 1 def set_priority(self, npri, input=None, output=None): l = [] for pri, inp, outp in self.links: if inp == input and outp == output: pri = npri elif inp == input and output == None: pri = npri elif input == None and output == None: pri = npri l.append((pri, inp, outp)) l.sort() self.links = l def open(self, chain, next, protocol, *a, **kw): self.chain = chain self.next = next self.protocol = protocol return self def close(self): self.chain = None self.next = None class ParserPlugin(DTKPlugin): name = '' version = dtkmain.VERSION author = 'Rob Tillotson' author_email = 'rob@pyrite.org' description = '' def __init__(self, *a, **kw): DTKPlugin.__init__(self, 'Parser', **kw) def feed(self, data): pass def eof(self): pass class RawParserPlugin(ParserPlugin): name = 'RawText' description = 'Processes raw input.' links = [ (0, "application/octet-stream", "doc-assembler") ] def feed(self, data): self.next.send_literal_data(data) # ******************************************************************* # Output plugins base class and basic doc output plugin # ******************************************************************* class AssemblerPlugin(DTKPlugin): name = '' version = dtkmain.VERSION author = 'Rob Tillotson' author_email = 'rob@pyrite.org' description = '' def __init__(self, *a, **kw): DTKPlugin.__init__(self, 'Assembler', **kw) # # input. # class InputPlugin(DTKPlugin): name = '' version = dtkmain.VERSION author = "Rob Tillotson" author_email = "rob@pyrite.org" description = "" def __init__(self, *a, **kw): DTKPlugin.__init__(self, "Input", **kw) def handles_filename(self, fn): return None def open_input(self, fn): """Open a data source. """ return (None, None) def close_input(self): pass class OutputPlugin(DTKPlugin): name = '' version = dtkmain.VERSION author = "Rob Tillotson" author_email = "rob@pyrite.org" description = "" installable = 0 def __init__(self, *a, **kw): DTKPlugin.__init__(self, "Output", **kw) # direct install helper support if self.installable: self._add_property('install', 'Install on handheld', boolean=1) self._add_cli_option('install', 'i', None, 'Install on handheld', boolean=1) self.install = 0 def do_install(self, filenames): if not self.installable: return if self.install: installer = self.api.find_installer() if installer is None: self.log("can't figure out how to install", LOG_ERROR) installer.install(filenames) class InstallerPlugin(DTKPlugin): name = '' version = dtkmain.VERSION author = "Rob Tillotson" author_email = "rob@pyrite.org" installer_name = '' installer_can_remove = 1 installer_priority = 0 def __init__(self, *a, **kw): DTKPlugin.__init__(self, "Installer", **kw) if self.installer_can_remove: self._add_property('keep_installed_files', 'Keep installed files', boolean=1) self._add_cli_option('keep_installed_files', None, 'keep', 'Keep installed files', boolean=1) self.keep_installed_files = 0 def installable_check(self): return None def install(self, filenames): pass pyrite-publisher-2.1.1/PyritePublisher/entitydefs.py0100644000175000000620000000565307260723000021354 0ustar robstaff# # $Id: entitydefs.py,v 1.1 2001/03/29 21:15:44 rob Exp $ # # Copyright 1998-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: entitydefs.py,v 1.1 2001/03/29 21:15:44 rob Exp $' __copyright__ = 'Copyright 1998-2001 Rob Tillotson ' import htmlentitydefs palm_entitydefs = { # PalmOS fills the unused portion of the character set with an # eclectic mix of characters from various ISO standards. # Entity names from HTML 4. 'OElig': chr(0x8c), # OE ligature 'oelig': chr(0x9c), # oe ligature 'Scaron': chr(0x8a), # S with caron 'scaron': chr(0x9a), # s with caron 'Yuml': chr(0x9f), # Y with umlaut 'circ': chr(0x88), # circumflex 'tilde': chr(0x98), # small tilde 'ndash': chr(0x96), # en dash 'mdash': chr(0x97), # em dash 'lsquo': chr(0x91), # left single quotation mark 'rsquo': chr(0x92), # right single quotation mark 'ldquo': chr(0x93), # left double quotation mark 'rdquo': chr(0x94), # right double quotation mark 'dagger': chr(0x86), # dagger 'Dagger': chr(0x87), # double dagger 'permil': chr(0x89), # per mille sign (wierd percent sign?) 'lsaquo': chr(0x8b), # left single angle quotation mark 'rsaquo': chr(0x9b), # right single angle quotation mark 'fnof': chr(0x83), # latin small f with hook (function def) 'bull': chr(0x95), # bullet 'hellip': chr(0x85), # horizontal ellipsis 'trade': chr(0x99), # trademark sign 'spades': chr(0x90), # spade suit 'clubs': chr(0x8e), # club suit 'hearts': chr(0x8f), # heart suit 'diams': chr(0x8d), # diamond suit # Characters I can't find reference to anywhere else. 'dcomma': chr(0x84), # double comma # PalmOS-specific characters. 'grafcmd': chr(0x9d), # graffiti command stroke 'grafsc': chr(0x9e), # graffiti shortcut stroke # RichReader supports this 'euro': chr(0x80), } entitydefs = {} entitydefs.update(htmlentitydefs.entitydefs) entitydefs.update(palm_entitydefs) pyrite-publisher-2.1.1/PyritePublisher/gui_wxwin.py0100644000175000000620000003465307450521062021225 0ustar robstaff# # $Id: gui_wxwin.py,v 1.2 2002/03/28 04:55:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: gui_wxwin.py,v 1.2 2002/03/28 04:55:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' from wxPython.wx import * import sys import dtkmain class PPPluginBrowser(wxDialog): ID_LISTBOX = 201 ID_PROPERTY_GRID = 202 def __init__(self, parent, instance=None): wxDialog.__init__(self, parent, -1, "Plugins", wxDefaultPosition, wxSize(500,300))#, wxSYSTEM_MENU|wxCAPTION) self.instance = instance # layout: # vertical box sizer # horizontal sizer # listbox control # notebook sizer # notebook # info panel # various static text boxes # property panel # dropdown box # edit field # horizontal sizer # close button # top_sizer = wxBoxSizer(wxVERTICAL) main_sizer = wxBoxSizer(wxHORIZONTAL) # main window self.listbox = wxListBox(self, self.ID_LISTBOX) main_sizer.Add(self.listbox, 1, wxEXPAND|wxRIGHT, 5) pnames = self.instance.plugins.keys() pnames.sort() plugin = self.instance.plugins[pnames[0]] self.listbox.InsertItems(pnames,0) EVT_LISTBOX(self, self.ID_LISTBOX, self.listbox_select) notebook = wxNotebook(self, -1) notebook_sizer = wxNotebookSizer(notebook) # notebook info page info_panel = wxPanel(notebook, -1) notebook.AddPage(info_panel, "Info") sz0 = wxBoxSizer(wxHORIZONTAL) sz = wxFlexGridSizer(0,2,2,2) sz.Add(wxStaticText(info_panel, -1, "Name:")) self.l_name = wxStaticText(info_panel, -1, plugin.name) sz.Add(self.l_name) sz.Add(wxStaticText(info_panel, -1, "Version:")) self.l_version=wxStaticText(info_panel, -1, plugin.version) sz.Add(self.l_version) sz.Add(wxStaticText(info_panel, -1, "Author:")) self.l_author = wxStaticText(info_panel, -1, "%s <%s>" % (plugin.author, plugin.author_email)) sz.Add(self.l_author) sz.Add(wxStaticText(info_panel, -1, "Description:")) #self.l_description = wxTextCtrl(self.info_panel, -1, "Plugin Description", # style = wxTE_READONLY|wxTE_MULTILINE) self.l_description = wxStaticText(info_panel, -1, plugin.description) sz.Add(self.l_description) sz0.Add(sz, 1, wxEXPAND|wxALL, 5) info_panel.SetAutoLayout(true) info_panel.SetSizer(sz0) main_sizer.Add(notebook_sizer, 2, wxEXPAND) top_sizer.Add(main_sizer, 1, wxEXPAND|wxALL, 5) # button box button_sizer = wxBoxSizer(wxHORIZONTAL) button_sizer.Add(wxButton(self, wxID_CANCEL, "Close"), 0, wxALIGN_RIGHT) top_sizer.Add(button_sizer, 0, wxALIGN_RIGHT|wxALL, 5) self.SetAutoLayout(true) self.SetSizer(top_sizer) top_sizer.SetMinSize(wxSize(500,300)) top_sizer.Fit(self) def listbox_select(self, event): p = self.listbox.GetStringSelection() plugin = self.instance.plugins[p] self.l_name.SetLabel(plugin.name) self.l_version.SetLabel(plugin.version) self.l_author.SetLabel("%s <%s>" % (plugin.author, plugin.author_email)) self.l_description.SetLabel(plugin.description) class PPMainFrame(wxFrame): ID_INFILENAME = 201 ID_INFILEBUTTON = 202 ID_GOBUTTON = 203 ID_OUTFILEBUTTON = 204 ID_OUTFILENAME = 205 ID_PICK_TASK = 206 M_ID_ABOUT = 101 M_ID_EXIT = 102 M_ID_PLUGINS = 103 def __init__(self, parent, ID, title, instance=None): wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(500,300)) self.instance = instance self.CreateStatusBar() help_menu = wxMenu() help_menu.Append(self.M_ID_ABOUT, "&About", "More information about Pyrite Publisher") EVT_MENU(self, self.M_ID_ABOUT, self.menu_about) file_menu = wxMenu() file_menu.Append(self.M_ID_PLUGINS, "&Plugins...", "Plugin information and properties") EVT_MENU(self, self.M_ID_PLUGINS, self.menu_plugins) file_menu.AppendSeparator() file_menu.Append(self.M_ID_EXIT, "E&xit", "Exit Pyrite Publisher") EVT_MENU(self, self.M_ID_EXIT, self.menu_exit) menuBar = wxMenuBar() menuBar.Append(file_menu, "&File") menuBar.Append(help_menu, "&Help") self.SetMenuBar(menuBar) mainsizer = wxBoxSizer(wxVERTICAL) sz = wxFlexGridSizer(0,3,5,5) sz.AddGrowableCol(1) # where is this documented??? sz.Add(wxStaticText(self, -1, "Input File:"), 0, wxALL|wxALIGN_RIGHT, 5) self.ctrl_infile = wxTextCtrl(self, self.ID_INFILENAME) sz.Add(self.ctrl_infile, 1, wxEXPAND) sz.Add(wxButton(self, self.ID_INFILEBUTTON, "Choose...", wxDefaultPosition),0) EVT_BUTTON(self, self.ID_INFILEBUTTON, self.button_chooseinput) sz.Add(wxStaticText(self, -1, "Output File:"), 0, wxALL|wxALIGN_RIGHT, 5) self.ctrl_outfile = wxTextCtrl(self, self.ID_OUTFILENAME) sz.Add(self.ctrl_outfile, 1, wxEXPAND) sz.Add(wxButton(self, self.ID_OUTFILEBUTTON, "Choose...", wxDefaultPosition),0) EVT_BUTTON(self, self.ID_OUTFILEBUTTON, self.button_chooseoutput) # task combobox sz.Add(wxStaticText(self, -1, "Task:"), 0, wxALL|wxALIGN_RIGHT, 5) self.tasks = self.instance.tasks.items() self.tasks.sort() self.pick_task = wxChoice(self, self.ID_PICK_TASK, wxDefaultPosition, wxDefaultSize, ["Automatic conversion"]+\ [t.title for k,t in self.tasks]) self.pick_task.SetSelection(0) sz.Add(self.pick_task, 1, wxEXPAND) sz.Add(wxStaticText(self, -1, ""), 0, wxALL|wxALIGN_RIGHT, 5) mainsizer.Add(sz, 0, wxEXPAND|wxALL, 5) sz0 = self.init_advanced_options() mainsizer.Add(sz0,2,wxALL|wxEXPAND,5) # button box sz = wxBoxSizer(wxHORIZONTAL) sz.Add(wxButton(self, self.ID_GOBUTTON, "Convert"), 0) EVT_BUTTON(self, self.ID_GOBUTTON, self.button_convert) mainsizer.Add(sz,0,wxALL|wxALIGN_RIGHT,5) self.SetAutoLayout(true) self.SetSizer(mainsizer) # # ADVANCED OPTIONS # A lot of these are more restricted versions of what the plugins can do. # Oh well, such is life when you insist on a GUI instead of using the # shell like a real man :) # ID_CHECK_INSTALL = 300 ID_PICK_DOC_OUTPUT = 301 ID_PICK_TEXT_PARSER = 302 ID_CHECK_COPYDOC = 303 ID_PICK_DOC_ASSEMBLER = 304 def init_advanced_options(self): # advanced options opts = wxStaticBox(self, -1, "Advanced Options") sz0 = wxStaticBoxSizer(opts, wxVERTICAL) sz = wxFlexGridSizer(0,2,5,5) sz.AddGrowableCol(0) sz.AddGrowableCol(1) # ** Queue for installation ; sets the "install" property on every plugin with it # and disables the output file box if present if self.instance.find_installer() is not None: self.check_install = wxCheckBox(self, self.ID_CHECK_INSTALL, "Queue for installation") sz.Add(self.check_install) else: self.check_install = None #EVT_CHECKBOX(self, self.ID_CHECK_INSTALL, self.toggle_install) # ** Default doc output method: looks for plugins with doc-output # and lets you pick one, and forces it #z = wxBoxSizer(wxHORIZONTAL) #pnames = [ p.name for p in self.instance.find_plugins_with_link('doc-output') ] #pnames.insert(0,"") #z.Add(wxStaticText(self, -1, "Doc Output:"), 0, wxALIGN_CENTER_VERTICAL) #self.pick_docoutput = wxChoice(self, self.ID_PICK_DOC_OUTPUT, wxDefaultPosition, # wxDefaultSize, # pnames) #z.Add(self.pick_docoutput, 1) #self.pick_docoutput.SetSelection(0) #sz.Add(z, 1, wxEXPAND) # ** Copy docs directly: Forces the CopyDoc plugin self.check_copydoc = wxCheckBox(self, self.ID_CHECK_COPYDOC, "Copy Docs directly") sz.Add(self.check_copydoc) # ** Doc format: lets you pick a doc-assembler #z = wxBoxSizer(wxHORIZONTAL) #pnames = [ p.name for p in self.instance.find_plugins_with_link('doc-assembler') ] #pnames.insert(0, "") #z.Add(wxStaticText(self, -1, "Doc Format:"), 0, wxALIGN_CENTER_VERTICAL) #self.pick_docassembler = wxChoice(self, self.ID_PICK_DOC_ASSEMBLER, wxDefaultPosition, # wxDefaultSize, # pnames) #z.Add(self.pick_docassembler, 1) #self.pick_docassembler.SetSelection(0) #sz.Add(z, 1, wxEXPAND) sz.Add(wxStaticText(self, -1, "")) # empty # ** Default text conversion method: looks for plugins with text/plain and # lets you pick one #z = wxBoxSizer(wxHORIZONTAL) #pnames = [ p.name for p in self.instance.find_plugins_with_link('text/plain') ] #pnames.insert(0, "") #z.Add(wxStaticText(self, -1, "Text Parser:"), 0, wxALIGN_CENTER_VERTICAL) #self.pick_textparser = wxChoice(self, self.ID_PICK_TEXT_PARSER, wxDefaultPosition, # wxDefaultSize, # pnames) #z.Add(self.pick_textparser, 1) #self.pick_textparser.SetSelection(0) #sz.Add(z, 1, wxEXPAND) sz0.Add(sz,1,wxEXPAND) return sz0 def do_options(self): # properties if self.check_install is not None and self.check_install.GetValue(): self.instance.set_property_all('install',1) else: self.instance.set_property_all('install',0) self.instance.set_property_all('output_filename',self.ctrl_outfile.GetValue()) # task (if any) n = self.pick_task.GetSelection() print n if n: k,task = self.tasks[n-1] forced_plugins = task.plugins[:] for k,v in task.properties.items(): print "Task property:", k, "=", v self.set_property_all(k,v) else: forced_plugins = [] # forced plugins if self.check_copydoc.GetValue(): forced_plugins.append('CopyDoc') #p.append(self.pick_docoutput.GetStringSelection()) #p.append(self.pick_docassembler.GetStringSelection()) #p.append(self.pick_textparser.GetStringSelection()) fp = [self.instance.plugins[x] for x in forced_plugins] return fp ################################################# def menu_exit(self, event): self.Close(true) def menu_about(self, event): dlg = wxMessageDialog(self, "Pyrite Publisher %s\nby Rob Tillotson \nhttp://www.pyrite.org/" % dtkmain.VERSION, "About Pyrite Publisher", wxOK) dlg.ShowModal() dlg.Destroy() def menu_plugins(self, event): dlg = PPPluginBrowser(self, self.instance) dlg.Show(true) def button_chooseinput(self, event): wc = self.instance.wildcards.items() wc.sort(lambda x,y: cmp(x[1],y[1])) wc.insert(0, ('*.*', 'All files')) wct = string.join(map(lambda p: '%s (%s)|%s' % (p[1],p[0],p[0]), wc), '|') dlg = wxFileDialog(self, "Choose a file to convert", wildcard = wct, style=wxOPEN) if dlg.ShowModal() == wxID_OK: ifn = dlg.GetPath() self.ctrl_infile.SetValue(ifn) self.ctrl_outfile.SetValue('') dlg.Destroy() def button_chooseoutput(self, event): dlg = wxFileDialog(self, "Choose an output filename", wildcard="All files (*.*)|*.*", style=wxSAVE) if dlg.ShowModal() == wxID_OK: ofn = dlg.GetPath() self.ctrl_outfile.SetValue(ofn) dlg.Destroy() def button_convert(self, event): fname = self.ctrl_infile.GetValue() self.SetStatusText("Converting %s..." % fname) forced = self.do_options() try: self.instance.convert_file(fname, forced) except dtkmain.ConversionError, err: dlg = wxMessageDialog(self, str(err), "Conversion Error", wxOK|wxICON_EXCLAMATION) dlg.ShowModal() dlg.Destroy() except: t, v, tb = sys.exc_info() dlg = wxMessageDialog(self, "%s: %s" % (t,v), "Error", wxOK|wxICON_EXCLAMATION) dlg.ShowModal() dlg.Destroy() self.SetStatusText("Done.") class PPApp(wxApp,dtkmain.PPInstance): def __init__(self, *a, **kw): dtkmain.PPInstance.__init__(self) wxApp.__init__(self, *a, **kw) def OnInit(self): frame = PPMainFrame(NULL, -1, "Pyrite Publisher", instance=self) frame.Show(true) self.SetTopWindow(frame) return true if __name__ == '__main__': app = PPApp(0) app.MainLoop() pyrite-publisher-2.1.1/PyritePublisher/metrics.py0100644000175000000620000001520707260723000020640 0ustar robstaff# # $Id: metrics.py,v 1.1 2001/03/29 21:15:44 rob Exp $ # # Copyright 1998-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: metrics.py,v 1.1 2001/03/29 21:15:44 rob Exp $' __copyright__ = 'Copyright 1998-2001 Rob Tillotson ' import operator, string metrics_normal = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x0f (controls) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x10 - 0x1f (controls) 3, 2, 4, 8, 6, 7, 6, 2, 4, 4, 6, 6, 3, 4, 2, 5, # 0x20 - 0x2f (spc/punct) 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 3, 6, 6, 6, 5, # 0x30 - 0x3f (num/punct) 8, 5, 5, 5, 6, 4, 4, 6, 6, 2, 4, 6, 5, 8, 6, 7, # 0x40 - 0x4f (@/A-O) 5, 7, 5, 5, 6, 6, 6, 8, 6, 6, 6, 3, 5, 3, 6, 5, # 0x50 - 0x5f (P-Z/punct) 3, 5, 5, 4, 5, 5, 4, 5, 5, 2, 3, 5, 2, 8, 5, 5, # 0x60 - 0x6f (bquote/a-o) 5, 5, 4, 4, 4, 5, 5, 6, 6, 6, 4, 4, 2, 4, 7, 0, # 0x70 - 0x7f (p-z/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x80 - 0x8f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x90 - 0x9f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xa0 - 0xaf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xb0 - 0xbf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xc0 - 0xcf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xd0 - 0xdf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xe0 - 0xef 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xf0 - 0xff ] metrics_bold = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x0f (controls) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x10 - 0x1f (controls) 3, 3, 6,10, 6,13, 9, 3, 5, 5, 6, 6, 3, 5, 3, 6, # 0x20 - 0x2f (spc/punct) 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 6, 6, 6, 6, # 0x30 - 0x3f (num/punct) 10, 7, 7, 6, 7, 5, 5, 8, 8, 3, 5, 7, 6,10, 7, 8, # 0x40 - 0x4f (@/A-O) 7, 8, 8, 6, 7, 7, 8,11, 7, 7, 7, 4, 6, 4, 6, 6, # 0x50 - 0x5f (P-Z/punct) 4, 5, 6, 5, 6, 6, 5, 6, 6, 3, 4, 6, 3, 9, 6, 6, # 0x60 - 0x6f (bquote/a-o) 6, 6, 5, 5, 6, 6, 6, 9, 6, 6, 5, 5, 3, 5, 7, 0, # 0x70 - 0x7f (p-z/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x80 - 0x8f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x90 - 0x9f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xa0 - 0xaf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xb0 - 0xbf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xc0 - 0xcf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xd0 - 0xdf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xe0 - 0xef 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xf0 - 0xff ] metrics_big = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x0f (controls) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x10 - 0x1f (controls) 5, 2, 4, 9, 6,11, 8, 2, 4, 4, 6, 8, 3, 4, 2, 5, # 0x20 - 0x2f (spc/punct) 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 3, 7, 7, 7, 5, # 0x30 - 0x3f (num/punct) 11, 9, 6, 7, 7, 6, 6, 8, 7, 3, 4, 7, 5,10, 7, 8, # 0x40 - 0x4f (@/A-O) 6, 8, 6, 5, 6, 7, 7,11, 7, 6, 5, 3, 5, 3, 6, 6, # 0x50 - 0x5f (P-Z/punct) 3, 6, 7, 6, 7, 7, 4, 7, 6, 3, 3, 6, 2,10, 7, 7, # 0x60 - 0x6f (bquote/a-o) 7, 7, 4, 5, 4, 7, 6,10, 6, 7, 6, 4, 2, 4, 7, 0, # 0x70 - 0x7f (p-z/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x80 - 0x8f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x90 - 0x9f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xa0 - 0xaf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xb0 - 0xbf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xc0 - 0xcf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xd0 - 0xdf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xe0 - 0xef 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xf0 - 0xff ] metrics_template = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x00 - 0x0f (controls) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x10 - 0x1f (controls) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x20 - 0x2f (spc/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x30 - 0x3f (num/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x40 - 0x4f (@/A-O) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x50 - 0x5f (P-Z/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x60 - 0x6f (bquote/a-o) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x70 - 0x7f (p-z/punct) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x80 - 0x8f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0x90 - 0x9f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xa0 - 0xaf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xb0 - 0xbf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xc0 - 0xcf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xd0 - 0xdf 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xe0 - 0xef 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0xf0 - 0xff ] metrics = { 0: metrics_normal, 1: metrics_bold, 2: metrics_big } def width(s, font=0): if font == 'p': # profont (fixed width, used in AportisDoc) return 6 * len(s) mt = metrics.get(font, metrics_normal) return reduce(operator.add, map(lambda x, m=mt: m[x], map(ord, s)), 0) def wordwrap(s, max_width=160, font=0): """Wraps a string to the specified width, using the specified font, and returns a list of lines. As a side effect, collapses multiple spaces to one.""" o = '' tw = 0 wspace = width(' ', font) for word in string.split(string.strip(s)): w = width(word, font) if (tw + w) <= max_width: o = o + word + ' ' tw = tw + w + wspace else: o = o + '\n' + word + ' ' tw = w + wspace return filter(None, map(string.rstrip, string.split(o, '\n'))) pyrite-publisher-2.1.1/PyritePublisher/plugin.py0100644000175000000620000004054207427745266020517 0ustar robstaff# # $Id: plugin.py,v 1.8 2002/02/05 12:06:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """microSulfur plugin support This is a fairly lightweight plugin interface: it doesn't even require plugins to import anything. This module's spiritual ancestor, Sulfur, used an on-demand loader which would search for a requested plugin at runtime, by attempting to import it from the packages named along a search path. While that method was flexible, it was also somewhat complicated. Also, enumeration of plugins was problematic because it was dependent on the external implementation of the Python package system. Therefore, this plugin manager uses a more traditional 'load all the plugins when the program starts' paradigm; lazy-loading can be done by a class wrapper, if it is desired. The loaders here use only the standard Python import mechanism, which should make it easier to freeze a program that uses this code. The loaders and other functionality provided by this module assumes that the calling program will provide a 'callback object' which has callback methods. Currently, this object has to provide the following methods: log(level, name, string) - put something in the logfile """ __version__ = '$Id: plugin.py,v 1.8 2002/02/05 12:06:14 rob Exp $' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys, string, os, traceback def _import(name): mod = __import__(name) components = string.split(name, '.') for comp in components[1:]: mod = getattr(mod, comp) return mod class OptionParsingError(Exception): pass class Callback: def __init__(self, loglevel=2, f=sys.stderr): self.f = f self.level = loglevel def log(self, level, name, string): if level <= self.level: self.f.write('%s: %s\n' % (name, string)) class SilentCallback: def log(self, level, name, string): pass LOG_ERROR = 0 LOG_WARNING = 1 LOG_NORMAL = 2 LOG_DEBUG = 3 class PluginLoader: pass class ObjectLoader(PluginLoader): """Plugin loader for class-based plugins. This loader assumes that: - each plugin is contained in a module, one plugin per module - each plugin is instantiated merely by importing it; for example a class, which the application will make objects of at runtime - the plugin class is located by its name """ def __init__(self, callback=None): if callback is None: self.callback = SilentCallback() else: self.callback = callback def load_plugins(self, modules, obj_name='Plugin'): l = [] for name in modules: self.callback.log(LOG_NORMAL, "plugin loader", "importing %s" % name) try: mod = _import(name) except: z0,z1,z2 = sys.exc_info() self.callback.log(LOG_WARNING, "plugin loader", "warning: import error for %s" % name) self.callback.log(LOG_DEBUG, name, "*** If this is one of the built-in plugins, the following information") self.callback.log(LOG_DEBUG, name, "*** may be helpful to the author in debugging it.") for line in string.split(string.join(traceback.format_exception(z0,z1,z2),'\n'),'\n'): self.callback.log(LOG_DEBUG, name, string.rstrip(line)) continue if hasattr(mod, obj_name): l.append(getattr(mod, obj_name)) else: self.callback.log(LOG_WARNING, "plugin loader", "warning: %s has no attribute named '%s'" % (name, obj_name)) return l class CallableLoader(ObjectLoader): """Plugin loader for callable-based plugins. A plugin loader that assumes that: - each plugin class is found in a module; one module, one plugin - the plugin is obtained by calling a function (or other callable thing, eg. a class) in that module - the plugin instantiation function is located by its name Additional keyword arguments may be provided to the load_plugins function to pass them to the classes' __init__ functions. """ def load_plugins(self, modules, func_name='Plugin', **kw): l = [] for cl in ObjectLoader.load_plugins(self, modules, func_name): try: i = apply(cl, (), kw) l.append(i) except: z0,z1,z2 = sys.exc_info() self.callback.log(LOG_WARNING, "plugin loader", "warning: error calling %s" % cl) self.callback.log(LOG_DEBUG, str(cl), "*** If this is one of the built-in plugins, the following information") self.callback.log(LOG_DEBUG, str(cl), "*** may be helpful to the author in debugging it.") for line in string.split(string.join(traceback.format_exception(z0,z1,z2),'\n'),'\n'): self.callback.log(LOG_DEBUG, str(cl), string.rstrip(line)) return l class MultipleCallableLoader(ObjectLoader): """Plugin loader for modules that may instantiate multiple plugins. Like CallableLoader, except the function called will return a list or tuple of plugins. """ def load_plugins(self, modules, func_name='load_plugins', **kw): l = [] for cl in ObjectLoader.load_plugins(self, modules, func_name): try: i = apply(cl, (), kw) l = l + list(i) except: self.callback.log(LOG_WARNING, "plugin loader", "error calling %s.%s" % (name, func_name)) return l class LazyClass: """Lazy loader stub for classes. This object fakes a small part of the interface of another object (particularly a class object), loading the module containing that object when it is needed. The arguments to __init__ are the name of the module to import, the name of the object within that module that this stub is proxying for, and any number of keyword arguments. The keyword arguments are treated as read-only attributes; getattr'ing them from the proxy will not cause the real object to be loaded. The real object is loaded as soon as the proxy is called. Once that happens, the dummy attributes will be cleared out, and getattr requests and calls will be redirected to the real object. """ def __init__(self, module, objname, **kw): self.__module = module self.__objname = objname self.__object = None self.__attr = {} for k, v in kw: self.__attr[k] = v def __load(self): mod = _import(self.__module) self.__object = getattr(mod, self.__objname) self.__attr = {} def __getattr__(self, k): if self.__attr.has_key(k): return self.__attr[k] if self.__object is not None: return getattr(self.__object, k) else: raise AttributeError, k def __setattr__(self, k, v): if self.__object is not None: return setattr(self.__object, k, v) else: raise AttributeError, k def __call__(self, *a, **kw): if self.__object is None: self.__load() return apply(self.__object, a, kw) # # And here follow some interface mixins that are useful to plugins. # First, the "properties" interface: # # A property is an attribute of a plugin object. It is just like # any other attribute; what makes it a property is that there is # an externally visible interface which tells clients of the plugin # that it exists. This is meant to be used for attributes which # hold configuration information -- the sort of things that might # find a place in a configuration file, on the command line, or # in an option dialog. # # The interface to properties is fairly simple: the plugin must have # a method called "getPropertyDescriptors" which returns a list of # PropertyDescriptor objects. class PropertyDescriptor: def __init__(self, name, description='', indexed=0, boolean=0, read_only=0): self.name = name self.description = description self.indexed = indexed self.read_only = read_only self.boolean = boolean def __cmp__(self, other): return cmp(self.name, other.name) class PropertyMixin: """Mixin class for plugins with properties. As a convenience this class provides a getPropertyDescriptors function which returns a list provided at object initialization time. If the plugin does not want to use that, it must overload getPropertyDescriptors. """ def __init__(self, base='', descriptors=None): self.property_base = base if descriptors: self.property_descriptors = descriptors def getPropertyDescriptors(self): return self.property_descriptors def getPropertyInfo(self, n): for pd in self.getPropertyDescriptors(): if pd.name == n: return pd def has_property(self, p): for pd in self.getPropertyDescriptors(): if pd.name == p: return 1 def _add_property(self, *a, **kw): if not hasattr(self,'property_descriptors'): self.property_descriptors = [] self.property_descriptors.append(apply(PropertyDescriptor, a, kw)) def copyProperties(self, target): for pd in self.getPropertyDescriptors(): if hasattr(self, pd.name): setattr(target, pd.name, getattr(self, pd.name)) # # Command-line support is similar, yet slightly different from the # properties interface. Rather than include support for command line # options in the property interface, it is separated out into another # mixin. # # As with properties, the main thing the plugin has to do is provide # a function, getCLIOptionDescriptors, which returns a list of # CLIOptionDescriptor objects. class CLIOptionDescriptor: """Describes a CLI option. name - the name of the attribute which will contain the value short - the short name (without '-') of this option long - the long name (without '--') of this option (either short or long, or both, must be supplied) help - the help string for this option vtype - the 'value type' used in help for this option, for example '-a FOO set bar to FOO bazs', vtype is FOO boolean - if true, this option takes no value and is just a toggle multiple - if true, this option takes multiple values, and will return a list func - a function, which must take a string as its only argument, which will convert the command-line argument into the form needed by the plugin. It may raise an exception if the value is out of range or otherwise invalid. """ def __init__(self, name, short='', long='', help='', vtype='', boolean=0, multiple=0, func=None): self.name = name self.short = short self.long = long self.help = help self.vtype = vtype self.boolean = boolean self.multiple = multiple self.func = func def __cmp__(self, other): return cmp(self.name, other.name) class CLIOptionMixin: def __init__(self, descriptors=None): if descriptors is not None: self.cli_option_descriptors = descriptors def getCLIOptionDescriptors(self): return self.cli_option_descriptors def _add_cli_option(self, *a, **kw): if not hasattr(self,'cli_option_descriptors'): self.cli_option_descriptors = [] self.cli_option_descriptors.append(apply(CLIOptionDescriptor, a, kw)) class CLIOptionParser: def __init__(self, opts=None, target=None, plugins=None): if opts: self.__opts = opts else: self.__opts = [] self.__target = target if plugins: self.__plugins = plugins else: self.__plugins = [] def __call__(self, argv, partial=0): # if partial is true, then we don't flag unknown options, # we just put them back into argv for a later re-parse oshort = {} olong = {} for o in self.__opts: if o.short: if not oshort.has_key(o.short): oshort[o.short] = [] oshort[o.short].append((o, self.__target)) if o.long: if not olong.has_key(o.long): olong[o.long] = [] olong[o.long].append((o, self.__target)) for p in self.__plugins: for o in p.getCLIOptionDescriptors(): if o.short: if not oshort.has_key(o.short): oshort[o.short] = [] oshort[o.short].append((o, p)) if o.long: if not olong.has_key(o.long): olong[o.long] = [] olong[o.long].append((o, p)) argv_left = [] passall = 0 while argv: a, argv = argv[0], argv[1:] if not a: continue if passall or a[0] != '-': argv_left.append(a) continue a = a[1:] if not a: raise OptionParsingError, 'what the heck does "-" by itself mean?' if a[0] == '-': # long option a = a[1:] if not a: # we have a -- passall = 1 continue if not olong.has_key(a): if not partial: raise OptionParsingError, 'no such option --%s' % a else: argv_left.append('--%s' % a) continue if not argv: arg = None rest = argv else: arg = argv[0] rest = argv[1:] for opt, target in olong[a]: if opt.boolean: if not hasattr(target, opt.name) or not getattr(target, opt.name): setattr(target, opt.name, 1) else: setattr(target, opt.name, 0) else: if arg is None: raise OptionParsingError, 'option --%s requires a value' % a if opt.func is not None: arg = opt.func(arg) if opt.multiple: if not hasattr(target, opt.name): setattr(target, opt.name, []) getattr(target, opt.name).append(arg) else: setattr(target, opt.name, arg) argv = rest else: for aa in a: if not oshort.has_key(aa): if not partial: raise OptionParsingError, 'no such option -%s' % aa else: argv_left.append('-%s' % a) continue if not argv: arg = None rest = argv else: arg = argv[0] rest = argv[1:] for opt, target in oshort[aa]: if opt.boolean: if not hasattr(target, opt.name) or not getattr(target, opt.name): setattr(target, opt.name, 1) else: setattr(target, opt.name, 0) elif len(a) > 1: # cannot do -xyz if x,y,or z need value raise OptionParsingError, 'option -%s must stand alone because it needs a value' % aa else: if opt.func is not None: arg = opt.func(arg) if opt.multiple: if not hasattr(target, opt.name): setattr(target, opt.name, []) getattr(target, opt.name).append(arg) else: setattr(target, opt.name, arg) argv = rest return argv_left def help(self, op=None): l = [] if op is None: opts = self.__options[:] else: opts = op[:] opts.sort() for o in opts: if o.short: c = '-%s' % o.short else: c = '' if o.long: if c: c = c + ', --%s' % o.long else: c = '--%s' % o.long if o.vtype: c = c + ' %s' % o.vtype if o.multiple: c = c + ' ...' if len(c) > 26: l.append(' %s' % c) l.append((' '*(29))+string.split(o.help,'\n')[0][:50]) else: l.append(' %-26s %s' % (c, string.split(o.help,'\n')[0][:50])) return string.join(l, '\n') pyrite-publisher-2.1.1/PyritePublisher/plugin_CSV.py0100644000175000000620000001524607450067746021231 0ustar robstaff# # $Id: plugin_CSV.py,v 1.1 2002/03/26 12:56:06 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_CSV.py,v 1.1 2002/03/26 12:56:06 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' from dtkplugins import ParserPlugin, LOG_ERROR, LOG_NORMAL, LOG_DEBUG, LOG_WARNING from dbprotocol import * import string class CSVError(Exception): pass class CSVParser: def __init__(self): self.fields = [] self.buf = '' self.comment_marker = '#' self.field_separator = ',' self.escape_double_quote = 1 self.skip_blank_lines = 1 self._state = 'start-field' self._accum = '' def flush(self): self._state = 'start-field' self._accum = '' self.buf = '' self.fields = [] def __parse(self): x = 0 done = 0 while x < len(self.buf): c = self.buf[x] # start-field state: looking for beginning of field # skip whitespace, separator means field was empty if self._state == 'start-field': if c == ' ' or c == '\t': x = x + 1 continue elif c == '\n': done = 1 x = x + 1 break elif c == '"': self._state = 'quoted-string' elif c == self.field_separator: self.fields.append('') else: self._accum = self._accum + c self._state = 'in-field' elif self._state == 'in-field': if c == self.field_separator: self.fields.append(self._accum.strip()) self._accum = '' self._state = 'start-field' elif c == '\n': self.fields.append(self._accum.strip()) self._accum = '' self._state = 'start-field' done = 1 x = x + 1 break elif c == '"' and self.escape_double_quote and x < len(self.buf)-1 \ and self.buf[x+1] == '"': x = x + 1 # eat second quote self._accum = self._accum + '"' else: self._accum = self._accum + c elif self._state == 'quoted-string': if c == '"': if self.escape_double_quote and x < len(self.buf)-1 and self.buf[x+1] == '"': x = x + 1 self._accum = self._accum + '"' else: self.fields.append(self._accum) self._accum = '' self._state = 'after-quoted-string' else: self._accum = self._accum + c elif self._state == 'after-quoted-string': if c == '\n': done = 1 x = x + 1 self._state = 'start-field' break elif c == ' ' or c == '\t': x = x + 1 continue elif c == self.field_separator: self._state = 'start-field' else: self.flush() raise CSVError, "text after quote" x = x + 1 self.buf = self.buf[x:] if done: f = self.fields self.fields = [] return f def feed(self, text=''): self.buf = self.buf + text f = self.__parse() while f is not None: if f or not self.skip_blank_lines: self.handle_line(f) f = self.__parse() def eof(self): self.feed('\n') def handle_line(self, fields): print fields class PPCSVParser(CSVParser): def __init__(self, next): CSVParser.__init__(self) self.next = next self.field_specs = 0 def handle_line(self, fields): if self.use_field_names and not self.field_specs: self.field_specs = [FieldSpec(x, FLD_STRING) for x in fields] self.next.define_fields(self.field_specs) else: if not self.field_specs: self.field_specs = [FieldSpec("Field%d" % x, FLD_STRING) \ for x in range(len(fields))] self.next.define_fields(self.field_specs) self.next.feed_record(fields) def eof(self): CSVParser.eof(self) class Plugin(ParserPlugin, CSVParser): name = 'CSV' description = 'Comma-separated-values parser.' links = [ (0, 'text/comma-separated-values', 'columnar-database'), (-100, 'text/plain', 'columnar-database'), (-100, 'application/octet-stream', 'columnar-database'), ] def __init__(self, *a, **kw): ParserPlugin.__init__(self, *a, **kw) self._add_property('use_field_names', 'Get field names from first line', boolean=1) self._add_cli_option('use_field_names', None, 'use-field-names', 'Get field names from first line', boolean=1) self.use_field_names = 0 def open(self, chain, next, *a, **kw): ParserPlugin.open(self, chain, next, *a, **kw) self.parser = PPCSVParser(next) self.copyProperties(self.parser) self.ttbl = string.maketrans('','') return self def feed(self, data): l = string.translate(data, self.ttbl, '\r') self.parser.feed(l) def eof(self): self.parser.eof() pyrite-publisher-2.1.1/PyritePublisher/plugin_HTML.py0100644000175000000620000002304107514640514021321 0ustar robstaff# # $Id: plugin_HTML.py,v 1.10 2002/07/15 21:40:28 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_HTML.py,v 1.10 2002/07/15 21:40:28 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import formatter, htmllib, string, urlparse, os import dtkplugins class Plugin(dtkplugins.ParserPlugin): name = 'HTML' description = 'Processes HTML input.' links = [ (0, "text/html", "doc-assembler"), (-10, "text/plain", "doc-assembler"), (-10, "application/x-dtk-raw-stream", "doc-assembler"), (0, "MULTIPART:web", "doc-assembler")] def __init__(self, *a, **kw): apply(dtkplugins.ParserPlugin.__init__, (self,)+a, kw) self.api.register_wildcard('*.htm', 'HTML files') self.api.register_wildcard('*.html', 'HTML files') self._add_cli_option('footnote_links', None, 'footnote-links', 'Mark and footnote links', boolean=1) self._add_property('footnote_links', 'Mark and footnote links', boolean=1) self.footnote_links = 0 self._add_cli_option('bookmark_anchors', None, 'bookmark-anchors', 'Bookmark local anchor targets', boolean=1) self._add_property('bookmark_anchors', 'Do not bookmark local anchor targets', boolean=1) self.bookmark_anchors = 0 self._add_cli_option('annotate_links', None, 'annotate-links', 'Mark and annotate links', boolean=1) self._add_property('annotate_links', 'Mark and annotate links', boolean=1) self.annotate_links = 0 self._add_cli_option('bookmark_headers', None, 'bookmark-headers', 'Levels of headers to bookmark', vtype="STR") self._add_property('bookmark_headers', 'Levels of headers to bookmark') self.bookmark_headers = '' def open(self, chain, next, *a, **kw): apply(dtkplugins.ParserPlugin.open, (self,chain,next)+a, kw) self.fmt = formatter.AbstractFormatter(next) self.parser = DocHTMLParser(self.fmt) self.copyProperties(self.parser) self.ttbl = string.maketrans('','') self.buf = '' return self def feed(self, data): l = string.translate(data, self.ttbl, '\r') self.parser.feed(l) def eof(self): if self.parser.anchorlist and self.footnote_links: self.fmt.end_paragraph(1) self.fmt.add_hor_rule() self.next.set_bookmark('%s Links' % chr(187)) self.next.send_heading('Links:', 3) for x in range(0, len(self.parser.anchorlist)): self.fmt.add_label_data('[1] ', x+1) self.fmt.add_flowing_data(self.parser.anchorlist[x]) self.fmt.add_line_break() def process_multipart_web(self, pages): # for now, this just concatenates the pages together with # a bookmark in between # we already have a formatter etc., but we will be making # a new parser for each one. addsplit = 0 for url, mtype, filename, extra in pages: if mtype != 'text/html': print "Skipping", url, "because it isn't html" continue if addsplit: self.fmt.end_paragraph(1) self.fmt.add_hor_rule() print "Writing", url self.next.set_bookmark(os.path.basename(urlparse.urlparse(url)[2])) # get title for this instead? parser = DocHTMLParser(self.fmt) self.copyProperties(parser) data = open(filename).read() parser.feed(string.translate(data, self.ttbl, '\r')) addsplit = 1 # handle footnote_links etc. class DocHTMLParser(htmllib.HTMLParser): """A HTML parser with some support for Doc-format e-texts.""" def __init__(self, *a, **kw): apply(htmllib.HTMLParser.__init__, (self,)+a, kw) self.writer = self.formatter.writer self.tcol = 0 self.capture_data = None def end_title(self): htmllib.HTMLParser.end_title(self) self.writer.set_title(self.title) # if not self.writer.has_title(): # self.writer.set_title(self.title) # entities from entitydefs import entitydefs # capturing. this is like save_bgn/save_end in the superclass, # but doesn't eat the data in the process. def handle_data(self, data): if self.capture_data is not None: self.capture_data = self.capture_data + data htmllib.HTMLParser.handle_data(self, data) def capture_bgn(self): self.capture_data = '' def capture_end(self): d = self.capture_data self.capture_data = None if not self.nofill: data = string.join(string.split(d)) return data # headings def start_h1(self, attr): self.save_bgn() def end_h1(self): text = self.save_end() self.formatter.end_paragraph(1) if '1' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 1) def start_h2(self, attr): self.save_bgn() def end_h2(self): text = self.save_end() self.formatter.end_paragraph(1) if '2' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 2) def start_h3(self, attr): self.save_bgn() def end_h3(self): text = self.save_end() self.formatter.end_paragraph(1) if '3' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 3) def start_h4(self, attr): self.save_bgn() def end_h4(self): text = self.save_end() self.formatter.end_paragraph(1) if '4' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 4) def start_h5(self, attr): self.save_bgn() def end_h5(self): text = self.save_end() self.formatter.end_paragraph(1) if '5' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 5) def start_h6(self, attr): self.save_bgn() def end_h6(self): text = self.save_end() self.formatter.end_paragraph(1) if '6' in self.bookmark_headers: self.writer.set_bookmark(text) self.writer.send_heading(text, 6) # anchors. def anchor_bgn(self, href, name, type): if name and self.bookmark_anchors: if name[0] == '#': name = name[1:] self.writer.set_bookmark(name) #if self.writer.has_option('teal-links'): # if name: # if name[0] == '#': name = name[1:] # self.writer.send_raw_tag('LABEL',{'NAME':'"%s"' % name}) # if href and href[0] == '#': # self.writer.send_raw_tag('LINK',{'TEXT':'"%s"' % (chr(187)*2), # 'FONT':'0', # 'TAG':'"%s"' % href[1:], # 'STYLE':'UNDERLINE'}) # elif href and not self.writer.has_option('no-links'): # self.anchor = href # self.anchorlist.append(href) elif href and href[0] != '#' and (self.footnote_links or self.annotate_links): self.anchor = href self.anchorlist.append(href) #self.capture_bgn() self.formatter.push_style('link') def anchor_end(self): self.formatter.pop_style() #title = self.capture_end() if self.anchor: if self.annotate_links: self.writer.set_annotation(self.anchor, 'Link #%s' % len(self.anchorlist)) if (self.footnote_links or self.annotate_links): self.writer.mark_footnote(len(self.anchorlist)) self.anchor = None # now, let's see what we can do about tables. # the simplest thing to do is to treat each table row as a separate line; def do_tr(self, attrs): self.tcol = 0 self.formatter.end_paragraph(0) def do_td(self, attrs): if self.tcol: self.formatter.add_flowing_data(' ') self.tcol = self.tcol+1 def start_table(self, attrs): pass def end_table(self): self.formatter.end_paragraph(1) #-- Lists, mostly cribbed from htmllib. def start_ul(self, attrs): type = 'disc' for a, v in attrs: if a == 'type': type = v if type == 'square': label = chr(0x8d) elif type == 'circle': label = 'o' else: label = chr(0x95) self.formatter.end_paragraph(not self.list_stack) self.formatter.push_margin('ul') self.list_stack.append(['ul', label, 0]) def do_li(self, attrs): self.formatter.end_paragraph(0) if self.list_stack: [dummy, label, counter] = top = self.list_stack[-1] top[2] = counter = counter+1 else: label, counter = chr(0x95), 0 self.formatter.add_label_data(label, counter) # --- elements we want to discard entirely def start_style(self, attr): self.save_bgn() def end_style(self): self.save_end() pyrite-publisher-2.1.1/PyritePublisher/plugin_RichReader.py0100644000175000000620000001174307427745266022610 0ustar robstaff# # $Id: plugin_RichReader.py,v 1.5 2002/02/05 12:06:14 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_RichReader.py,v 1.5 2002/02/05 12:06:14 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import dtkplugins import plugin_basicdoc import string def rr_style(s): """Convert a style spec to a RichReader font byte """ b = 0 for t in map(string.lower, string.split(s)): if t == 'normal' or t == 'medium': b = b | 0x40 elif t == 'small': b = b | 0x20 elif t == 'large': b = b | 0x60 elif t == 'bold': b = b | 0x04 elif t == 'italic': b = b | 0x02 elif t == 'fixed': b = b | 0x08 elif t == 'underline': b = b | 0x01 elif t == 'dotted' or t == 'dotted-underline': b = b | 0x10 return b def rr_justify(s): s = string.split(s) if 'center' in s: return 0x83 elif 'right' in s: return 0x81 else: return 0x80 def rr_indent(x): return 0xc0 | x def rr_hang_indent(x): return 0xf0 | x class RichReaderDocWriter(plugin_basicdoc.BasicDocWriter): def __init__(self, *a, **kw): apply(plugin_basicdoc.BasicDocWriter.__init__, (self,)+a, kw) self.current_style = 0x40 self.features.append('richtext') def send_code(self, code): self.flush() self.doc.write(chr(0x7f)+chr(code)) def send_hor_rule(self, *a, **kw): self.send_code(0x8c) self.doc.write('\n') def send_heading(self, text, level=1): # get header style if hasattr(self, 'h%s_style' % level): s = getattr(self, 'h%s_style' % level) else: s = 'normal left' #s, j = rr_header_styles.get(level, rr_default_header_style) self.send_code(rr_justify(s)) self.send_code(rr_style(s)) self.doc.write(text) self.doc.write('\n') self.send_code(rr_style('normal')) self.send_code(rr_justify('left')) def send_label_data(self, data): self.doc.write(data+' ') def new_margin(self, kind, depth): if depth > 3: depth = 3 self.send_code(0xc0 | (depth << 1)) if kind in ['ol', 'ul', 'dl'] and depth > 0: self.send_code(0xfe) else: self.send_code(0xf0) # note: to get underlining, will need to make a new subclass of # formatter, and override it to use 5-elt font tuples instead of 4-elt. def new_font(self, font): if font is not None: sz, it, bd, tt = font[:4] else: sz = it = bd = tt = 0 # nothing with size yet b = self.current_style #print "current style 0x%02x" % b if it: b = b | 0x02 else: b = b & ~0x02 if bd: b = b | 0x04 else: b = b & ~0x04 if tt: b = b | 0x08 else: b = b & ~0x08 #print font, "= 0x%02x" % b self.send_code(b) self.current_style = b def new_styles(self, styles): b = self.current_style #print "current style 0x%02x" % b if 'link' in styles: b = b | 0x10 else: b = b & ~0x10 self.send_code(b) #print styles, "= 0x%02x" % b self.current_style = b class Plugin(plugin_basicdoc.Plugin): name = 'RichReader' description = 'Assembles a RichReader-enhanced document.' links = [ (0, "doc-assembler", "doc-only-output") ] writerclass = RichReaderDocWriter def __init__(self, *a, **kw): apply(plugin_basicdoc.Plugin.__init__, (self,)+a, kw) #self._add_cli_option("h1_style", None, "h1-style", # "Heading 1 style", # vtype="STR") self._add_property("h1_style", "Heading 1 style") self.h1_style = "large bold left" self._add_property("h2_style", "Heading 2 style") self.h2_style = "normal bold italic underline left" self._add_property("h3_style", "Heading 2 style") self.h3_style = "normal bold underline left" self._add_property("h4_style", "Heading 2 style") self.h4_style = "normal bold left" self._add_property("h5_style", "Heading 2 style") self.h5_style = "normal underline left" self._add_property("h6_style", "Heading 2 style") self.h6_style = "normal italic left" self.api.register_task('mkrichreader', 'Convert to RichReader', ['RichReader']) pyrite-publisher-2.1.1/PyritePublisher/plugin_TaggedText.py0100644000175000000620000001460107427745266022634 0ustar robstaff# # $Id: plugin_TaggedText.py,v 1.7 2002/02/05 12:06:14 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_TaggedText.py,v 1.7 2002/02/05 12:06:14 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import formatter, string, re import dtkplugins class Plugin(dtkplugins.ParserPlugin): name = 'TaggedText' description = 'Processes text input with Pyrite Publisher tags.' links = [ (0, "application/x-dtk-tagged-text", "doc-assembler"), (-10, "text/plain", "doc-assembler"), (-10, "application/x-dtk-raw-stream", "doc-assembler") ] def __init__(self, *a, **kw): apply(dtkplugins.ParserPlugin.__init__, (self,)+a, kw) self.tag_prefix = '.' self.case_fold_tags = 0 self.tag_regexp = '' self._add_property('tag_prefix', 'Tag prefix') self._add_property('case_fold_tags', 'Fold tags to lower case', boolean=1) self._add_property('tag_regexp', 'Regular expression to match tags') self._add_cli_option('tag_prefix', None, 'tag-prefix', "Tag prefix", vtype='STR') self._add_cli_option('case_fold_tags', None, 'case-fold-tags', "Fold tags to lower case", boolean=1) self._add_cli_option('tag_regexp', None, 'tag-regexp', "Regular expression to match tags", vtype='RE') self.bookmark_regexps = [] self._add_property('bookmark_regexps', 'Regular expressions to bookmark', indexed=1) self._add_cli_option('bookmark_regexps', 'r', None, 'Regular expression to bookmark.', vtype='REGEXP', multiple=1) def open(self, chain, next, *a, **kw): apply(dtkplugins.ParserPlugin.open, (self,chain,next)+a, kw) self.fmt = formatter.AbstractFormatter(next) self.in_para = 0 self.tapi = TagAPI(self.tag_prefix, self.case_fold_tags, self.tag_regexp) ##### self.buf = '' self.bookmark_regexps = map(re.compile, self.bookmark_regexps) return self def feed(self, data): b = self.buf + data self.buf = '' if not b: return lines = string.split(b, '\n') self.buf = lines[-1] lines = lines[:-1] for l in map(lambda x: x + '\n', lines): re_matches = filter(None, map(lambda x,s=l: x.search(s), self.bookmark_regexps)) if re_matches: g = re_matches[0].groups() if g: bm = g[0] else: bm = re_matches[0].group(0) self.fmt.writer.set_bookmark(bm) # this is kind of screwy. no, this whole thing is kind of screwy. m = self.tapi.match(l) if m: try: l = self.tapi.process(m, self.fmt, self.next) except RuntimeError: pass if not l: continue if self.tapi.capturing: self.tapi.capture_text(l) elif self.tapi.verbatim: self.fmt.add_literal_data(l) else: if not string.strip(l): if self.in_para: self.in_para = 0 self.fmt.end_paragraph(1) else: self.in_para = 1 self.fmt.add_flowing_data(l) class TagAPI: def __init__(self, prefix='.', case_fold=0, tagregex=None, verbatim=0): if not tagregex: self.tagregex = "^%(prefix)s\s*(?P[a-zA-Z0-9/_]+)(?:\s*(?P.*)\s*)?" self.tagprefix = prefix self.case_fold = case_fold self.tagre = re.compile(self.tagregex % {'prefix': re.escape(prefix)}) self.verbatim = verbatim self.capturing = 0 self.cap_buf = '' self.tag_args = [] # stack for tags to use to temporarily store stuff def start_capture(self): self.cap_buf = '' self.capturing = 1 def end_capture(self): self.capturing = 0 return self.cap_buf def capture_text(self, text): self.cap_buf = self.cap_buf + text def match(self, l): return self.tagre.match(string.rstrip(l)) def process(self, m, fmt, w): tag, arg = m.group('tag','arg') if self.case_fold: tag = string.upper(tag) if tag and tag[0] != '/': meth = 'tag_' + tag else: meth = 'end_' + tag[1:] if hasattr(self, meth): return apply(getattr(self, meth), (arg, fmt, w)) elif hasattr(self, 'tag'): return self.tag(tag, arg, fmt, w) def tag_TITLE(self, arg, fmt, w): w.set_title(arg) def tag_BOOKMARK(self, arg, fmt, w): w.set_bookmark(arg) tag_BM = tag_BOOKMARK def tag_H1(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 1) def tag_H2(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 2) def tag_H3(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 3) def tag_H4(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 4) def tag_H5(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 5) def tag_H6(self, arg, fmt, w): fmt.end_paragraph(1) w.send_heading(arg, 6) def tag_ANNOTATION(self, arg, fmt, w): self.tag_args.append(arg) self.start_capture() def end_ANNOTATION(self, arg, fmt, w): t = self.tag_args.pop() c = self.end_capture() # fix paragraphs in the annotation l = re.split('[ \t]*\n[ \t]*\n', c) l = map(lambda x: string.strip(string.join(string.split(x))), l) c = string.join(l, '\n\n') w.set_annotation(c, t) tag_ANN = tag_ANNOTATION end_ANN = end_ANNOTATION def tag_PRE(self, *a, **kw): self.verbatim = 1 def end_PRE(self, *a, **kw): self.verbatim = 0 def tag(self, tag, arg, fmt, w): raise RuntimeError, 'invalid tag %s' % tag def tag_HR(self, arg, fmt, w): apply(w.send_hor_rule, (), {'break':'both'}) pyrite-publisher-2.1.1/PyritePublisher/plugin_TealDoc.py0100644000175000000620000001110407427745266022102 0ustar robstaff# # $Id: plugin_TealDoc.py,v 1.6 2002/02/05 12:06:14 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_TealDoc.py,v 1.6 2002/02/05 12:06:14 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import string # ack! I need __! import metrics import plugin_basicdoc # teal_header_attrs = { # 1: {'FONT': '2', 'ALIGN': 'CENTER', 'STYLE': 'NORMAL'}, # 2: {'FONT': '1', 'ALIGN': 'LEFT', 'STYLE': 'UNDERLINE'}, # 3: {'FONT': '1', 'ALIGN': 'LEFT', 'STYLE': 'NORMAL'}, # 4: {'FONT': '0', 'ALIGN': 'LEFT', 'STYLE': 'UNDERLINE'}, # 5: {'FONT': '0', 'ALIGN': 'LEFT', 'STYLE': 'UNDERLINE'}, # 6: {'FONT': '0', 'ALIGN': 'LEFT', 'STYLE': 'UNDERLINE'} # } # teal_header_fonts = { # 1: 2, # 2: 1, # 3: 1 # } # teal_default_header_attrs = {'FONT': '0', 'ALIGN': 'LEFT', 'STYLE': 'NORMAL'} def teal_header_style(s): l = string.split(s) d = {} if 'large' in l and 'bold' in l: d['FONT'] = 3 elif 'large' in l: d['FONT'] = 2 elif 'bold' in l: d['FONT'] = 1 else: d['FONT'] = 0 if 'right' in l: d['ALIGN'] = 'RIGHT' elif 'center' in l: d['ALIGN'] = 'CENTER' else: d['ALIGN'] = 'LEFT' if 'underline' in l: d['STYLE'] = 'UNDERLINE' elif 'invert' in l: d['STYLE'] = 'INVERT' return d class TealDocWriter(plugin_basicdoc.BasicDocWriter): def __init__(self, *a, **kw): apply(plugin_basicdoc.BasicDocWriter.__init__, (self,)+a, kw) self.teal_bookmarks = 0 self.features.append('richtext') def send_raw_tag(self, tagname, attr={}): self.flush() self.doc.write('<'+tagname) if attr: self.doc.write(' ') self.doc.write(string.join(map(lambda x: '%s=%s' % x, attr.items()))) self.doc.write('>') def set_bookmark(self, s): if self.teal_bookmarks: self.send_raw_tag('BOOKMARK',{'NAME':'"%s"' % s[:15]}) else: plugin_basicdoc.BasicDocWriter.set_bookmark(self, s) def send_heading(self, text, level=1): if hasattr(self, 'h%s_style' % level): s = getattr(self, 'h%s_style' % level) else: s = 'normal left' style = teal_header_style(s) l = metrics.wordwrap(text, max_width=160, font=style.get('FONT',0)) for t in l: d = {'TEXT': '"%s"' % t} d.update(style) self.send_raw_tag('HEADER', d) if not d.get('FONT') == 2: self.send_line_break() if d.get('FONT') == 2: self.send_line_break() def send_hor_rule(self, *a, **kw): # later, do things based on 'break' and 'char' self.send_raw_tag('HRULE') self.doc.write('\n') class Plugin(plugin_basicdoc.Plugin): name = 'TealDoc' description = 'Assembles a TealDoc-enhanced document.' links = [ (0, "doc-assembler", "doc-only-output") ] writerclass = TealDocWriter def __init__(self, *a, **kw): apply(plugin_basicdoc.Plugin.__init__, (self,)+a, kw) #self._add_cli_option("h1_style", None, "h1-style", # "Heading 1 style", # vtype="STR") self._add_property("h1_style", "Heading 1 style") self.h1_style = "large" self._add_property("h2_style", "Heading 2 style") self.h2_style = "bold underline" self._add_property("h3_style", "Heading 2 style") self.h3_style = "bold" self._add_property("h4_style", "Heading 2 style") self.h4_style = "underline" self._add_property("h5_style", "Heading 2 style") self.h5_style = "underline" self._add_property("h6_style", "Heading 2 style") self.h6_style = "underline" self.api.register_task("mktealdoc", "Convert to TealDoc", ['TealDoc']) pyrite-publisher-2.1.1/PyritePublisher/plugin_Text.py0100644000175000000620000000646607427745266021532 0ustar robstaff# # $Id: plugin_Text.py,v 1.5 2002/02/05 12:06:14 rob Exp $ # # Copyright 1999-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_Text.py,v 1.5 2002/02/05 12:06:14 rob Exp $' __copyright__ = 'Copyright 1999-2001 Rob Tillotson ' import formatter, string, re import dtkplugins _re_indent = re.compile('^(\s+)') class Plugin(dtkplugins.ParserPlugin): name = 'Text' description = 'Processes text input.' links = [ (0, "text/plain", "doc-assembler"), (-10, "application/x-dtk-raw-stream", "doc-assembler") ] def __init__(self, *a, **kw): apply(dtkplugins.ParserPlugin.__init__, (self,)+a, kw) self.api.register_wildcard('*.txt', 'Text files') self.indented_paragraphs = 0 self._add_property('indented_paragraphs', 'Use indentation to find paragraphs', boolean=1) self._add_cli_option('indented_paragraphs', None, 'indented-paragraphs', 'Use indentation to find paragraphs', boolean=1) self.bookmark_regexps = [] self._add_property('bookmark_regexps', 'Regular expressions to bookmark', indexed=1) self._add_cli_option('bookmark_regexps', 'r', None, 'Regular expression to bookmark.', vtype='REGEXP', multiple=1) def open(self, chain, next, *a, **kw): apply(dtkplugins.ParserPlugin.open, (self,chain,next)+a, kw) self.fmt = formatter.AbstractFormatter(next) self.in_para = 0 self.use_indent = self.indented_paragraphs self.indent_level = None self.buf = '' self.bookmark_regexps = map(re.compile, self.bookmark_regexps) return self def feed(self, data): # in case we have multiple lines b = self.buf + data self.buf = '' if not b: return lines = string.split(b, '\n') self.buf = lines[-1] lines = lines[:-1] for l in map(lambda x: x + '\n', lines): re_matches = filter(None, map(lambda x,s=l: x.search(s), self.bookmark_regexps)) if re_matches: g = re_matches[0].groups() if g: bm = g[0] else: bm = re_matches[0].group(0) self.fmt.writer.set_bookmark(bm) if not string.strip(l): if self.in_para: self.in_para = 0 self.fmt.end_paragraph(1) else: if self.use_indent: m = _re_indent.match(l) if m: self.fmt.end_paragraph(1) self.in_para = 1 self.fmt.add_flowing_data(l) pyrite-publisher-2.1.1/PyritePublisher/plugin_URLStream.py0100644000175000000620000000777607450521062022407 0ustar robstaff# # $Id: plugin_URLStream.py,v 1.1 2002/03/28 04:55:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_URLStream.py,v 1.1 2002/03/28 04:55:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys, os, urlparse, mimetypes, re, urllib, tempfile from dtkplugins import InputPlugin from dtkmain import ConversionError class Plugin(InputPlugin): name = 'URLStream' description = 'Gets input from a file or URL.' def __init__(self, *a, **kw): apply(InputPlugin.__init__, (self,)+a, kw) self.f = None self.generic_mimetypes = ["text/plain", "application/octet-stream"] self._temp_file_name = '' def handles_filename(self, fn): return 1 def open_input(self, fn): # On win32, trying to urlopen a name with a drive-letter in it # fails, since it thinks the drive-letter is a protocol like # "http:" or "ftp:", so we handle these names separately. if sys.platform == 'win32' and re.match('^[a-zA-Z]:', fn): f = open(fn) else: f = urllib.urlopen(fn) # raises IOError if fails n, ext = os.path.splitext(os.path.basename(fn)) if ext == '.gz': f = GzipFile(filename="", mode="rb", fileobj=f) n, ext = os.path.splitext(n) self.f = f try: # some type of URL don't have this mtype = f.info()["Content-Type"] except: mtype = "application/octet-stream" mtypes = [mtype] if mtype[:5] == "text/": mtypes.append("application/octet-stream") # try guessing a type if it is text/plain or application/octet-stream if mtype in self.generic_mimetypes: gtype, genc = mimetypes.guess_type(n+ext) if gtype and gtype not in mtypes: mtypes.insert(0, gtype) # add types we can filter to mt0 = [] for intype, outtype in self.api.input_filters.keys(): if intype in mtypes and outtype not in mtypes: mt0.append(outtype) self._unfiltered_types = mtypes self._filtered_types = mt0 mtypes = mtypes + mt0 if not n: # handle urls with no path up = urlparse.urlparse(fn) n = up[1] return mtypes, os.path.basename(n) def close_input(self): if self.f is not None: self.f.close() if self._temp_file_name: os.unlink(self._temp_file_name) self.f = None def go(self, mimetype): if mimetype in self._filtered_types: for t in self._unfiltered_types: if self.api.input_filters.has_key((t,mimetype)): filterprog = self.api.input_filters[(t,mimetype)] tfn = tempfile.mktemp() tf = open(tfn,'wb') tf.write(self.f.read()) tf.close() self._temp_file_name = tfn self.f = os.popen(filterprog % tfn, 'r') break while 1: l = self.f.readline() if not l: break self.next.feed(l) self.next.eof() pyrite-publisher-2.1.1/PyritePublisher/plugin_WebInput.py0100644000175000000620000001305307452030151022303 0ustar robstaff import sys, os, string, re, htmllib, urllib, urlparse, mimetypes from pprint import pprint from dtkplugins import InputPlugin from dtkmain import ConversionError class NullFormatter: def nothing(self, *a, **kw): pass def __getattr__(self, k): return self.nothing class HTMLLinkGatherer(htmllib.HTMLParser): def __init__(self): htmllib.HTMLParser.__init__(self,NullFormatter()) self.links = [] self.images = [] def anchor_bgn(self, href, name, type): self.links.append((href, name, type)) def handle_image(self, src, alt, *a): self.images.append((src, alt)) def __str__(self): l = ['Links:'] + map(str, self.links.keys()) l = l + ['Images:'] + map(str, self.images.keys()) return string.join(l,'\n') def gather_links(fn): p = HTMLLinkGatherer() p.feed(open(fn).read()) return p def type_and_host(url): typ, rest = urllib.splittype(url) hostport, rest = urllib.splithost(rest) if hostport is None: host = None else: upwd, hostport = urllib.splituser(hostport) host, port = urllib.splitport(hostport) return typ, host def guess_mimetype(fn, headers=None): try: mtype = headers['Content-Type'] except: mtype = 'application/octet-stream' if mtype in ['application/octet-stream', 'text/plain']: gtype, genc = mimetypes.guess_type(fn) return gtype else: return mtype class WebSpider: def __init__(self, base): self.base = base self.info = {} self.valid_url_types = ['http','ftp','gopher'] #whatever urllib handles self.maximum_depth = 3 self.follow_offsite_links = 0 self.verbose = 1 typ, host = type_and_host(base) self.base_host = host self.pages = [] def go(self): print "Retrieving", self.base fn, headers = urllib.urlretrieve(self.base) self.info[self.base] = (fn, headers) mtype = guess_mimetype(fn, headers) self.pages.append((self.base, mtype, fn, headers)) self.process_links(fn, self.base, 2) def process_links(self, fn, url, level=0): # If we have reached maximum link depth, quit. if self.maximum_depth and level > self.maximum_depth: return lks = gather_links(fn) for link, lname, ltype in lks.links: nurl = urlparse.urljoin(url, link) # decide whether we want to follow this link typ, host = type_and_host(nurl) if typ is not None and typ not in self.valid_url_types: if self.verbose: print "Skipping", nurl, "due to ignored type", typ continue if host != self.base_host and not self.follow_offsite_links: if self.verbose: print "Skipping", nurl, "because it is an offsite link to", host continue sys.stdout.flush() if not self.info.has_key(nurl): if self.verbose: print "Retrieving", nurl, "(level %s)" % level try: fn, headers = urllib.urlretrieve(nurl) except IOError: # broken link if self.verbose: print "Broken link", nurl continue self.info[nurl] = (fn, headers) # if it's HTML, recurse mtype = guess_mimetype(fn, headers) self.pages.append((nurl, mtype, fn, headers)) if mtype == 'text/html': self.process_links(fn, nurl, level+1) else: if self.verbose: print "Not processing", nurl, "because it isn't html" else: if self.verbose: print "Not retrieving", nurl, "because it is in cache" class Plugin(InputPlugin): name = 'WebInput' description = 'Retrieves a web page or site.' def __init__(self, *a, **kw): InputPlugin.__init__(self, *a, **kw) self._add_property('spider','Use web spider') self._add_cli_option('spider','','spider', 'Use web spider', boolean=1) self.spider = 0 self._add_property('maximum_depth', 'Maximum depth to retrieve') self._add_cli_option('maximum_depth', '', 'maxdepth', 'Maximum depth to retrieve', vtype="NUM", func=int) self.maximum_depth = 1 self._add_property('follow_offsite_links', 'Follow links off site of main page') self._add_cli_option('follow_offsite_links', '', 'offsite-links', 'Follow links off site of main page', boolean=1) self.follow_offsite_links = 0 self._tempdir = '' self._spiderobj = None self._base_url = None def handles_filename(self, fn): # XXX later, retrieve the file and spider if it is HTML # but behave like URLStream if it is not. if self.spider and (fn[:5] == 'http:' or fn[-5:].lower() == '.html' or fn[-4].lower() == '.htm'): return 1 def open_input(self, fn): self._base_url = fn self._spiderobj = WebSpider(fn) self.copyProperties(self._spiderobj) self._spiderobj.go() #pprint(self._spiderobj.info) return ['MULTIPART:web'], os.path.basename(fn) def close_input(self): urllib.urlcleanup() def go(self, mimetype): self.next.process_multipart_web(self._spiderobj.pages[:]) pyrite-publisher-2.1.1/PyritePublisher/plugin_basicdoc.py0100644000175000000620000000777307427745266022357 0ustar robstaff# # $Id: plugin_basicdoc.py,v 1.1 2002/02/05 12:06:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_basicdoc.py,v 1.1 2002/02/05 12:06:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys from dtkplugins import AssemblerPlugin from formatter import NullWriter class BasicDocWriter(NullWriter): def __init__(self, doc=sys.stdout): NullWriter.__init__(self) self.doc = doc self.list_depth = 0 self.footnote_marker_format = '[%d]' self.features = [] def has_feature(self, f): return f in self.features or self.doc.has_feature(f) def close(self): self.flush() self.doc.close() self.doc = None def set_bookmark(self, s): """Set a bookmark at the current output position. """ self.flush() self.doc.bookmark(s) def set_annotation(self, text, t=''): self.flush() self.doc.annotate(text, t) def mark_footnote(self, number=0): self.send_literal_data(self.footnote_marker_format % number) def send_heading(self, text, level=1): self.send_literal_data(text) self.send_line_break() def set_title(self, title): self.doc.name = title # standard writer API def reset(self): self.list_depth = 0 def send_paragraph(self, blankline=0): self.doc.write('\n'*blankline) def send_line_break(self): self.doc.write('\n') def send_hor_rule(self, *a, **kw): char = kw.get('char','*')[:1] if char == '-': count = 39 else: count = 26 brk = kw.get('break','none') if brk in ['before','both']: self.doc.write('\n') self.doc.write(char * count) if brk in ['after','both']: self.doc.write('\n') def send_literal_data(self, data): self.doc.write(data) def send_flowing_data(self, data): self.doc.write(data) def send_label_data(self, data): self.doc.write(' '*self.list_depth) self.doc.write(data) if data and data[-1] not in ' \n\r\t': self.doc.write(' ') def new_alignment(self, align): pass def new_font(self, font): pass def new_margin(self, margin, level): if margin is None or level == 0: self.list_depth = 0 elif margin in ['ol', 'ul']: self.list_depth = level - 1 def new_spacing(self, spacing): pass def new_styles(self, styles): pass class Plugin(AssemblerPlugin): name = 'BasicDoc' description = 'Assembles a standard text document.' links = [ (10, "doc-assembler", "doc-output") ] writerclass = BasicDocWriter def __init__(self, *a, **kw): apply(AssemblerPlugin.__init__, (self,)+a, kw) self.writer = None self._add_cli_option('footnote_marker_format', None, 'footnote-marker-format', 'Footnote marker format', vtype="STR") self._add_property('footnote_marker_format', 'Footnote marker format') self.footnote_marker_format = '[%d]' def open(self, chain, next, *a, **kw): self.writer = self.writerclass(next) self.copyProperties(self.writer) return self.writer def close(self): self.writer.close() self.writer = None pyrite-publisher-2.1.1/PyritePublisher/plugin_copydoc.py0100644000175000000620000000433607450521062022216 0ustar robstaff# # $Id: plugin_copydoc.py,v 1.3 2002/03/28 04:55:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_copydoc.py,v 1.3 2002/03/28 04:55:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import dtkplugins, dtkmain # This plugin is the first one to not fit into the usual 4-stage # chain. Its input interface is used by the pdbinput plugin to # feed the contents of a doc directly, without reformatting but # with metadata, through to a doc-output plugin. class Plugin(dtkplugins.DTKPlugin): name = 'CopyDoc' description = 'Copies a doc database.' version = dtkmain.VERSION author = 'Rob Tillotson' author_email = 'rob@pyrite.org' links = [ (0, "DOC:raw", "doc-output") ] def open(self, chain, next, *a, **kw): # next is a docwritestream self.doc = next return self def close(self): self.doc.close() self.doc = None def feed_title(self, title): self.doc.name = title def feed_bookmark(self, s, pos): self.doc.bookmark(s, pos) def feed_annotation(self, text, t, pos): if self.doc.has_feature('annotate'): self.doc.annotate(text, t, pos) def feed(self, text): self.doc.write(text) pyrite-publisher-2.1.1/PyritePublisher/plugin_dbfieldguess.py0100644000175000000620000000547507450521062023223 0ustar robstaff# # $Id: plugin_dbfieldguess.py,v 1.2 2002/03/28 04:55:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_dbfieldguess.py,v 1.2 2002/03/28 04:55:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import re, string from dtkplugins import DTKPlugin, LOG_NORMAL, LOG_DEBUG, LOG_ERROR, LOG_WARNING from dtkmain import ConversionError import dtkmain from dbprotocol import * _rx_types = [ (re.compile('^[01]$'), FLD_BOOLEAN), (re.compile('^[+-]?\d+$'), FLD_INT), (re.compile('^[+-]?\d+\.\d+$'), FLD_FLOAT), (re.compile('^\d+/\d+/\d+$'), FLD_DATE), (re.compile('^\d+:\d\d [aApP][mM]$'), FLD_TIME), ] _included_types = { (FLD_BOOLEAN, FLD_INT) : None, (FLD_BOOLEAN, FLD_FLOAT): None, (FLD_INT, FLD_FLOAT): None, } _type_convert = { FLD_INT: int, FLD_FLOAT: float, FLD_BOOLEAN: lambda x: int(x and '1' or '0'), } class Plugin(DTKPlugin): name = 'GuessFields' description = 'Guess field types in a database' version = dtkmain.VERSION author = 'Rob Tillotson' author_email = 'rob@pyrite.org' links = [ (0, 'columnar-database', 'columnar-database') ] def open(self, chain, next, *a, **kw): DTKPlugin.open(self, chain, next) self.guesser = None self.recs = [] return self def define_fields(self, fieldspecs): for spec in fieldspecs: spec.type = None # initialize for guessing self.guesser = TypeGuesser(fieldspecs, _rx_types, _included_types) def feed_record(self, rec): self.guesser(rec) self.recs.append(rec) def close(self): self.next.define_fields(self.guesser.fieldspecs) for rec in self.recs: self.next.feed_record(self.guesser.convert_rec(rec)) pyrite-publisher-2.1.1/PyritePublisher/plugin_docoutput.py0100644000175000000620000000627607514640514022616 0ustar robstaff# # $Id: plugin_docoutput.py,v 1.3 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_docoutput.py,v 1.3 2002/07/15 21:40:28 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import os from dtkplugins import OutputPlugin from doc_database import * class Plugin(OutputPlugin): name = 'Doc' description = 'Outputs to a Doc database.' links = [ (0, "doc-output", "pdb-output"), (0, "doc-only-output", "pdb-output") ] # doc-only-output is for formats that will ever only work with # plain doc db (eg. richreader and teal); plain text formats and # things like HTML might one day be readable in zTXT or other formats # too, and thus can use doc-output. doc_version = 0 def __init__(self, *a, **kw): OutputPlugin.__init__(self, *a, **kw) self._add_property('title', 'The document title') self._add_cli_option('title', 't', 'title', 'Set the document title', vtype='STR') self.title = '' self._add_property('backup', 'Set the backup bit', boolean=1) self._add_cli_option('backup', 'b', 'backup', "Set the document's backup bit", boolean=1) self.backup = 0 self._add_property('category', 'The document category') self._add_cli_option('category', 'c', 'category', "Set the document's category", vtype="NUM", func=int) self.category = 0 self._add_property('creator', 'The document creator') self._add_cli_option('creator', None, 'creator', "Set the document's creator ID", vtype="STR") self.creator = 'REAd' self._add_property('type', 'The document type') self._add_cli_option('type', None, 'type', "Set the document's type", vtype="STR") self.type = 'TEXt' self.doc = None self.api.register_task('raw2doc', 'Convert to Doc without reformatting', ['RawText', 'Doc']) def open(self, chain, next, protocol, basename, *a, **kw): self.doc = DocWriteStream(next, self.title and self.title or basename, self.creator, self.type, self.backup and 0x0008 or 0, self.doc_version, self.category, 1) return self.doc def close(self): self.doc = None pyrite-publisher-2.1.1/PyritePublisher/plugin_frotz.py0100644000175000000620000000575007427745266021745 0ustar robstaff# # $Id: plugin_frotz.py,v 1.2 2002/02/05 12:06:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_frotz.py,v 1.2 2002/02/05 12:06:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' from dtkplugins import DTKPlugin import dtkmain import mimetypes class Plugin(DTKPlugin): name = 'Frotz' description = 'Z-Machine game to database converter' version = dtkmain.VERSION author = "Rob Tillotson" author_email = "rob@pyrite.org" links = [ (0, 'application/x-zmachine', 'pdb-output'), ] def __init__(self, *a, **kw): apply(DTKPlugin.__init__, (self,)+a, kw) # register zcode extensions since they aren't known to the # standard mimetypes module mimetypes.types_map['.z3'] = 'application/x-zmachine' mimetypes.types_map['.z5'] = 'application/x-zmachine' mimetypes.types_map['.z6'] = 'application/x-zmachine' mimetypes.types_map['.z8'] = 'application/x-zmachine' self.api.register_wildcard('*.z?', 'Z-Machine games') self._add_property('title', 'Game title') self._add_cli_option('title', 't', 'title', 'Game title', vtype="STR") self.title = '' def open(self, chain, next, *a, **kw): self.pdb = next self.buf = '' self.next_id = 0xa5e001 inf = {'type': 'ZCOD', 'creator': 'Fotz',} if self.title: inf['name'] = self.title self.pdb.updateDBInfo(inf) return self def feed(self, data): self.buf = self.buf + data while len(self.buf) > 4096: b = self.buf[:4096] self.buf = self.buf[4096:] self.pdb.setRecord(0x60, self.next_id, 0, b) self.next_id = self.next_id + 1 def eof(self): if self.buf: self.pdb.setRecord(0x60, self.next_id, 0, self.buf + ('\0' * (4096-len(self.buf)))) self.next_id = self.next_id + 1 self.buf = '' pyrite-publisher-2.1.1/PyritePublisher/plugin_instjpilot.py0100644000175000000620000000364607427745266023002 0ustar robstaff# # $Id: plugin_instjpilot.py,v 1.1 2002/02/05 12:06:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_instjpilot.py,v 1.1 2002/02/05 12:06:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys, os, string from dtkplugins import InstallerPlugin class Plugin(InstallerPlugin): name = 'JPilotInstall' description = 'Installer that uses jpilot.' installer_name = 'jpilot' installer_priority = 0 installer_can_remove = 0 def installable_check(self): if os.path.isdir(os.path.expanduser('~/.jpilot')): return 1 def install(self, filenames): if type(filenames) == type(''): filenames = [filenames] jfn = os.path.expanduser('~/.jpilot/jpilot_to_install') q = open(jfn).readlines() o = open(jfn,'a') for fn in filenames: ifn = os.path.abspath(fn) if ifn not in map(string.strip, q): o.write(ifn+'\n') pyrite-publisher-2.1.1/PyritePublisher/plugin_instpisock.py0100644000175000000620000000622207427745266022762 0ustar robstaff# # $Id: plugin_instpisock.py,v 1.1 2002/02/05 12:06:14 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_instpisock.py,v 1.1 2002/02/05 12:06:14 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' from dtkplugins import InstallerPlugin, LOG_ERROR, LOG_DEBUG, LOG_NORMAL import os class Plugin(InstallerPlugin): name = 'pisockInstall' description = 'Installer that uses python-libpisock.' installer_name = 'pisock' installer_priority = -1 installer_can_remove = 1 def __init__(self, *a, **kw): InstallerPlugin.__init__(self, *a, **kw) self._add_property('port', 'Port handheld is attached to') self._add_cli_option('port', None, 'port', 'Port handheld is attached to', vtype="NAME") self.port = '/dev/pilot' def installable_check(self): try: import pisock return 1 except: return 0 def install(self, filenames): import pisock sd = pisock.pi_socket(pisock.PI_AF_SLP, pisock.PI_SOCK_STREAM, pisock.PI_PF_PADP) if not sd: self.log("failed to create socket", LOG_ERROR) return if (pisock.pi_bind(sd, (pisock.PI_AF_SLP, self.port))) == -1: self.log("failed to bind socket", LOG_ERROR) return if (pisock.pi_listen(sd, 1)) == -1: self.log("failed to listen on socket", LOG_ERROR) return ret = pisock.pi_accept(sd) if ret == -1: self.log("failed to accept connection", LOG_ERROR) return socket = ret[0] pisock.dlp_OpenConduit(socket) #user_info = pisock.dlp_ReadUserInfo(socket) # not used yet if type(filenames) == type(''): filenames = [filenames] for fn in filenames: f = pisock.pi_file(fn) f.install(socket) del f if not self.keep_installed_files: os.unlink(fn) # write back user info... not yet pisock.dlp_AddSyncLogEntry(socket, "Pyrite Publisher install completed.") pisock.pi_close(socket) pyrite-publisher-2.1.1/PyritePublisher/plugin_jfile.py0100644000175000000620000001061407450454733021655 0ustar robstaff# # $Id: plugin_jfile.py,v 1.1 2002/03/26 12:56:06 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_jfile.py,v 1.1 2002/03/26 12:56:06 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' from dtkplugins import DTKPlugin, LOG_NORMAL, LOG_DEBUG, LOG_ERROR, LOG_WARNING from dtkmain import ConversionError import dtkmain import struct, string, re from dbprotocol import * def pad(s, n): return (s[:n]+('\0'*n))[:n] CURRENT_MARKER = 576 MAX_FIELDS = 50 MAX_FIELD_NAME_LENGTH = 20 MAX_DATA_LENGTH = 4000 MAX_FIND_STRING = 16 MAX_FILTER_STRING = 16 _types = { FLD_STRING : 0x0001, FLD_BOOLEAN: 0x0002, FLD_DATE : 0x0004, FLD_INT : 0x0008, FLD_FLOAT : 0x0010, FLD_TIME : 0x0020, } def make_jfile_appinfo(fieldspecs): rec = string.join([pad(x.name,MAX_FIELD_NAME_LENGTH)+'\0' for x in fieldspecs],'') for n in range(MAX_FIELDS - len(fieldspecs)): rec = rec + ('\0'*(MAX_FIELD_NAME_LENGTH+1)) # string fields only right now for x in fieldspecs: rec = rec + struct.pack('>H', _types[x.type]) rec = rec + (struct.pack('>H',0x0000) * (MAX_FIELDS - len(fieldspecs))) rec = rec + struct.pack('>H', len(fieldspecs)) rec = rec + struct.pack('>H', CURRENT_MARKER) # column widths # boolean only needs 15 # int needs 5*len+3 for x in fieldspecs: t = x.type if t == FLD_BOOLEAN: w = 15 elif t == FLD_INT: w = x.max_length*5+3 else: w = 80 rec = rec + struct.pack('>H',w) rec = rec + (struct.pack('>H',80) * (MAX_FIELDS-len(fieldspecs))) rec = rec + struct.pack('>H',80) # showDataWidth # filters and sorts rec = rec + (5 * struct.pack('>H',0)) rec = rec + '\0' * MAX_FIND_STRING rec = rec + '\0' * MAX_FIND_STRING # flags defunct in jfile 5 rec = rec + '\0\0' # first column to show rec = rec + '\0\0' # extra data 1 rec = rec + '\0\0\0\0' * MAX_FIELDS # ... and 2 rec = rec + '\0\0\0\0' * MAX_FIELDS # extra chunk rec = rec + '\0\0' return rec class Plugin(DTKPlugin): name = 'JFile' description = 'JFile database converter' version = dtkmain.VERSION author = 'Rob Tillotson' author_email = 'rob@pyrite.org' links = [ (-1, 'columnar-database', 'pdb-output') ] def __init__(self, *a, **kw): DTKPlugin.__init__(self, *a, **kw) self._add_property('title','Database title') self._add_cli_option('title','t','title','Database title',vtype="STR") self.title = '' def open(self, chain, next, *a, **kw): self.pdb = next inf = {'type': 'JfD5', 'creator': 'JFi5', 'version': 0x0801, # no security, jfile 5 } if self.title: inf['name'] = self.title self.pdb.updateDBInfo(inf) self.uid = 0x6f0000 self.field_names = [] self.guesser = TypeGuesser() return self def define_fields(self, fieldspecs): self.fieldspecs = fieldspecs def feed_record(self, fields): rec = string.join([struct.pack('>H',len(str(x))+1) for x in fields], '') rec = rec + string.join(map(str, fields), '\0') + '\0' self.pdb.setRecord(0x40, self.uid, 0, rec) self.uid = self.uid + 1 def close(self): self.pdb.setAppBlock(make_jfile_appinfo(self.fieldspecs)) pyrite-publisher-2.1.1/PyritePublisher/plugin_pdbinput.py0100644000175000000620000001105307514640514022402 0ustar robstaff# # $Id: plugin_pdbinput.py,v 1.3 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_pdbinput.py,v 1.3 2002/07/15 21:40:28 rob Exp $' __copyright__ = 'Copyright 2001 Rob Tillotson ' import os, string, urllib, sys, re import dtkplugins, prc, doc_database, ztxt try: import _Doc _compress = _Doc.compress _uncompress = _Doc.uncompress except: import doc_compress _compress = doc_compress.compress _uncompress = doc_compress.uncompress class Plugin(dtkplugins.InputPlugin): name = 'PDBInput' description = 'Input from a pdb/prc file.' def __init__(self, *a, **kw): apply(dtkplugins.InputPlugin.__init__, (self,)+a, kw) self.api.register_wildcard('*.pdb', 'Palm databases') self.api.register_wildcard('*.prc', 'Palm resources') self.pdb = None self.is_doc = 0 def handles_filename(self, fn): n, e = os.path.splitext(fn) e = string.lower(e) if e == '.prc' or e == '.pdb': return 10 # The way this will work is that palm database types will be # represented by special MIME types like "PDB:" # and the database will be fed in sequential order as records. # # This has special handling for Doc databases, because they # will be handled as if they contained some other format of # text; i.e. they can have multiple mime types, depending on # their content. def open_input(self, fn): # Win32 hack, see the basic input plugin for why. if sys.platform == 'win32' and re.match('^[a-zA-Z]:', fn): f = open(fn) else: f = urllib.urlopen(fn) # no automatic ungzipping here, i guess # load the entire prc, too bad there isn't a better way to do this self.pdb = prc.File(f) # doc. creator = self.pdb.info['creator'] type = self.pdb.info['type'] mtypes = ['PDB:'+creator+'/'+type] if (type == 'TEXt' and creator in ('REAd', 'TLDc')) or \ (type == 'zTXT' and creator == 'GPlm'): mtypes = self.open_doc(type) + mtypes return mtypes, os.path.basename(fn) def close_input(self): self.pdb = None self.is_doc = 0 def open_doc(self, type): self.is_doc = type # for now, treat all docs as application/octet-stream # later check the first record to try to ascertain a doctype #return ["DOC:raw", "application/octet-stream"] return ["text/plain", "DOC:raw"] def go(self, mimetype): if self.is_doc: if self.is_doc == 'zTXT': stream = ztxt.zTXTReadStream(db=self.pdb) else: stream = doc_database.DocReadStream(db=self.pdb) while 1: l = stream.read() if not l: break self.next.feed(l) if mimetype == 'DOC:raw': self.next.feed_title(self.pdb.info['name']) for pos, text in stream.bookmarks: self.next.feed_bookmark(text, pos) if stream.has_feature('annotate'): for pos, title, text in stream.annotations: self.next.feed_annotation(text, title, pos) stream.close() else: # totally untested, probably need to redesign self.next.feed_pdb_header(self.pdb.info) self.next.feed_pdb_appinfo(self.pdb.appblock) self.next.feed_pdb_sortinfo(self.pdb.sortblock) if self.pdb.info['flagResource']: for i in range(0,self.pdb.getRecords()): apply(self.next.feed_pdb_resource, self.pdb.getResource(i)) else: for i in range(0,self.pdb.getRecords()): apply(self.next.feed_pdb_record, self.pdb.getRecord(i)) self.next.feed_pdb_end(); pyrite-publisher-2.1.1/PyritePublisher/plugin_pdboutput.py0100644000175000000620000000747307514640514022616 0ustar robstaff# # $Id: plugin_pdboutput.py,v 1.4 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """Generic Palm database output plugin. This simple plugin allows the previous plugin in the chain to write a pdb/prc file in whatever arbitrary manner it wants. The value passed to the previous plugin is simply a prc.File instance. """ __version__ = '$Id: plugin_pdboutput.py,v 1.4 2002/07/15 21:40:28 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import os, time from dtkplugins import OutputPlugin import prc # from the python cookbook _num_rep = {} for x in 'abcdefghijklmnopqrstuvwxyz': _num_rep[ord(x)-ord('a')+10] = x def decimalToN(num,n): new_num_string='' current=num while current!=0: remainder=current%n if 36>remainder>9: remainder_string=_num_rep[remainder] elif remainder>=36: remainder_string='('+str(remainder)+')' else: remainder_string=str(remainder) new_num_string=remainder_string+new_num_string current=current/n return new_num_string _id_begin = 1017175873 def gen_id(): s = decimalToN(long((time.time()-1015000000)*10), 36) if len(s) < 6: return ('000000'+s)[-6:] class Plugin(OutputPlugin): name = 'PDBOutput' description = 'Generic Palm database output' installable = 1 links = [ (0, "pdb-output", None) ] def __init__(self, *a, **kw): apply(OutputPlugin.__init__, (self,)+a, kw) self._add_property('output_dir', 'Output directory') self._add_cli_option('output_dir', 'd', 'output-directory', 'Set the output directory', vtype="STR") self.output_dir = '.' self._add_property('output_filename', 'Output filename') self._add_cli_option('output_filename', 'o', None, 'Set the output filename', vtype="STR") self.output_filename = '' self._add_property('unique_names', 'Guarantee unique DB names') self._add_cli_option('unique_names', None, 'unique-names', 'Guarantee unique DB names', boolean=1) self.unique_names = 0 def open(self, chain, next, protocol, basename, *a, **kw): if self.output_filename: fn = self.output_filename else: fn = os.path.join(self.output_dir, basename+'.pdb') self.filename = fn self.pdb = prc.File(fn, read=0, write=1, info={'name': basename}) return self.pdb def close(self): if self.unique_names: id = gen_id() oldname = self.pdb.getDBInfo().get('name','') newname = (oldname + ' '*31)[:31-len(id)]+id self.pdb.updateDBInfo({'name': newname}) self.pdb.close() self.pdb = None if self.install: self.do_install(self.filename) pyrite-publisher-2.1.1/PyritePublisher/plugin_textoutput.py0100644000175000000620000001116507514640514023026 0ustar robstaff# # $Id: plugin_textoutput.py,v 1.5 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_textoutput.py,v 1.5 2002/07/15 21:40:28 rob Exp $' __copyright__ = 'Copyright 2001 Rob Tillotson ' import sys import dtkplugins class FakeDocStream: def __init__(self, f=sys.stdout): self.f = f self.appinfo = None self.bookmarks = [] self.annotations = [] self.pos = 0 def set_appinfo(self, a=''): self.appinfo = None def has_feature(self, f): return 1 def bookmark(self, title, pos=None): if pos is None: pos = self.pos self.bookmarks.append((pos, title)) def annotate(self, text, title='', pos=None): if pos is None: pos = self.pos self.annotations.append((pos, title, text)) def write(self, data): self.pos = self.pos + len(data) self.f.write(data) def writelines(self, list): for l in list: self.write(l) def dump(self): if (self.dump_all or self.dump_appinfo) and self.appinfo: self.f.write("\n*** Appinfo:\n") self.f.write(repr(self.appinfo)) self.f.write("\n") if (self.dump_all or self.dump_bookmarks) and self.bookmarks: self.bookmarks.sort() self.f.write("\n*** Bookmarks:\n") for pos, t in self.bookmarks: self.f.write("(%9d) %s\n" % (pos, t)) self.f.write("\n") if (self.dump_all or self.dump_annotations) and self.annotations: self.annotations.sort() self.f.write("\n*** Annotations:\n") for pos, t, txt in self.annotations: self.f.write(">> (%9d) %s\n" % (pos, t)) self.f.write(txt) self.f.write("\n") def close(self): self.dump() self.f.flush() if self.f is not sys.stdout and self.f is not sys.stderr: self.f.close() class Plugin(dtkplugins.OutputPlugin): name = 'TextOutput' description = 'Text and debugging output.' links = [ (-1000, "doc-output", None), (-1000, "doc-only-output", None), (0, "text-output", None)] def __init__(self, *a, **kw): dtkplugins.OutputPlugin.__init__(self, *a, **kw) self._add_property('dump_bookmarks', 'Dump bookmarks', boolean=1) self._add_cli_option('dump_bookmarks', None, 'dump-bookmarks', 'Dump bookmarks', boolean=1) self.dump_bookmarks = 0 self._add_property('dump_annotations', 'Dump annotations', boolean=1) self._add_cli_option('dump_annotations', None, 'dump-annotations', 'Dump annotations', boolean=1) self.dump_annotations = 0 self._add_property('dump_appinfo', 'Dump appinfo', boolean=1) self._add_cli_option('dump_appinfo', None, 'dump-appinfo', 'Dump appinfo', boolean=1) self.dump_appinfo = 0 self._add_property('dump_all', 'Dump all metadata', boolean=1) self._add_cli_option('dump_all', 'D', 'dump-all', 'Dump all metadata', boolean=1) self.dump_all = 0 self._add_property('output_filename', 'Output filename') self._add_cli_option('output_filename', 'o', None, 'Output filename', vtype='STR') self.output_filename = '' def open(self, chain, next, protocol, basename, *a, **kw): if self.output_filename: f = open(self.output_filename, 'w') else: f = sys.stdout self.doc = FakeDocStream(f) self.copyProperties(self.doc) return self.doc def close(self): self.doc = None pyrite-publisher-2.1.1/PyritePublisher/plugin_ztxtoutput.py0100644000175000000620000000604507514640514023054 0ustar robstaff# # $Id: plugin_ztxtoutput.py,v 1.8 2002/07/15 21:40:28 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """ """ __version__ = '$Id: plugin_ztxtoutput.py,v 1.8 2002/07/15 21:40:28 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import string, os from dtkplugins import OutputPlugin import ztxt def valid_font(s): s = string.lower(s) if s not in ['standard','normal','large','bold','largebold','hk40','fixed']: raise OptionParsingError, 'font must be one of "normal", "large", "bold", "largebold", or "fixed"' if s == 'hk40': s == 'fixed' if s == 'standard': s == 'normal' return s class Plugin(OutputPlugin): name = 'zTXT' description = 'Outputs to a zTXT (Gutenpalm) database.' links = [ (-1, "doc-output", "pdb-output") ] def __init__(self, *a, **kw): OutputPlugin.__init__(self, *a, **kw) self._add_property('title', 'The document title') self._add_cli_option('title','t','title', 'Set the document title', vtype='STR') self.title = '' self._add_property('random_access', 'Compress for random access', boolean=1) self._add_cli_option('random_access', None, 'random-access', 'Toggle random access (default is on)', boolean=1) self.random_access = 1 self.api.register_task('doc2ztxt', 'Convert Doc to zTXT', ['CopyDoc', 'zTXT']) self.api.register_task('mkztxt', 'Convert to zTXT', ['zTXT']) self.api.register_task('raw2ztxt', 'Convert to zTXT without reformatting', ['RawText','zTXT']) self.doc = None def open(self, chain, next, protocol, basename, *a, **kw): params = 0 self.doc = ztxt.zTXTWriteStream(next, self.title and self.title or basename, self.random_access, params) return self.doc def close(self): self.doc = None pyrite-publisher-2.1.1/PyritePublisher/pp_meta.py0100644000175000000620000000101007450521017020607 0ustar robstaff## ## This file is automatically generated. DO NOT EDIT. ## plugin_modules = [ 'plugin_CSV', 'plugin_HTML', 'plugin_RichReader', 'plugin_TaggedText', 'plugin_TealDoc', 'plugin_Text', 'plugin_URLStream', 'plugin_basicdoc', 'plugin_copydoc', 'plugin_dbfieldguess', 'plugin_docoutput', 'plugin_frotz', 'plugin_instjpilot', 'plugin_instpisock', 'plugin_jfile', 'plugin_pdbinput', 'plugin_pdboutput', 'plugin_textoutput', 'plugin_ztxtoutput' ] pyrite-publisher-2.1.1/PyritePublisher/prc.py0100644000175000000620000003212207412560102017752 0ustar robstaff# # $Id: prc.py,v 1.3 2001/12/27 08:48:02 rob Exp $ # # Copyright 1998-2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """PRC/PDB file I/O in pure Python. This module serves two purposes: one, it allows access to Palm OS(tm) database files on the desktop in pure Python without requiring pilot-link (hence, it may be useful for import/export utilities), and two, it caches the contents of the file in memory so it can be freely modified using an identical API to databases over a DLP connection. """ __version__ = '$Id: prc.py,v 1.3 2001/12/27 08:48:02 rob Exp $' __copyright__ = 'Copyright 1998-2001 Rob Tillotson ' # temporary hack until we get gettext support again def _(s): return s # # DBInfo structure: # # int more # unsigned int flags # unsigned int miscflags # unsigned long type # unsigned long creator # unsigned int version # unsigned long modnum # time_t createDate, modifydate, backupdate # unsigned int index # char name[34] # # # DB Header: # 32 name # 2 flags # 2 version # 4 creation time # 4 modification time # 4 backup time # 4 modification number # 4 appinfo offset # 4 sortinfo offset # 4 type # 4 creator # 4 unique id seed (garbage?) # 4 next record list id (normally 0) # 2 num of records for this header # (maybe 2 more bytes) # # Resource entry header: (if low bit of attr = 1) # 4 type # 2 id # 4 offset # # record entry header: (if low bit of attr = 0) # 4 offset # 1 attributes # 3 unique id # # then 2 bytes of 0 # # then appinfo then sortinfo # import sys, os, stat, struct PI_HDR_SIZE = 78 PI_RESOURCE_ENT_SIZE = 10 PI_RECORD_ENT_SIZE = 8 PILOT_TIME_DELTA = 2082844800L flagResource = 0x0001 flagReadOnly = 0x0002 flagAppInfoDirty = 0x0004 flagBackup = 0x0008 flagOpen = 0x8000 # 2.x flagNewer = 0x0010 flagReset = 0x0020 # flagExcludeFromSync = 0x0080 attrDeleted = 0x80 attrDirty = 0x40 attrBusy = 0x20 attrSecret = 0x10 attrArchived = 0x08 default_info = { 'name': '', 'type': 'DATA', 'creator': ' ', 'createDate': 0, 'modifyDate': 0, 'backupDate': 0, 'modnum': 0, 'version': 0, 'flagReset': 0, 'flagResource': 0, 'flagNewer': 0, 'flagExcludeFromSync': 0, 'flagAppInfoDirty': 0, 'flagReadOnly': 0, 'flagBackup': 0, 'flagOpen': 0, 'more': 0, 'index': 0 } def null_terminated(s): for x in range(0, len(s)): if s[x] == '\000': return s[:x] return s def trim_null(s): return string.split(s, '\0')[0] def pad_null(s, l): if len(s) > l - 1: s = s[:l-1] s = s + '\0' if len(s) < l: s = s + '\0' * (l - len(s)) return s # # new stuff # Record object to be put in tree... class PRecord: def __init__(self, attr=0, id=0, category=0, raw=''): self.raw = raw self.id = id self.attr = attr self.category = category # comparison and hashing are done by ID; # thus, the id value *may not be changed* once # the object is created. def __cmp__(self, obj): if type(obj) == type(0): return cmp(self.id, obj) else: return cmp(self.id, obj.id) def __hash__(self): return self.id class PResource: def __init__(self, typ=' ', id=0, raw=''): self.raw = raw self.id = id self.type = typ def __cmp__(self, obj): if type(obj) == type(()): return cmp( (self.type, self.id), obj) else: return cmp( (self.type, self.id), (obj.type, obj.id) ) def __hash__(self): return hash((self.type, self.id)) class PCache: def __init__(self): self.data = [] self.appblock = '' self.sortblock = '' self.dirty = 0 self.next = 0 self.info = {} self.info.update(default_info) # if allow_zero_ids is 1, then this prc behaves appropriately # for a desktop database. That is, it never attempts to assign # an ID, and lets new records be inserted with an ID of zero. self.allow_zero_ids = 0 # pi-file API def getRecords(self): return len(self.data) def getAppBlock(self): return self.appblock and self.appblock or None def setAppBlock(self, raw): self.dirty = 1 self.appblock = raw def getSortBlock(self): return self.sortblock and self.sortblock or None def setSortBlock(self, raw): self.dirty = 1 self.appblock = raw def checkID(self, id): return id in self.data def getRecord(self, i): try: r = self.data[i] except: return None return r.raw, i, r.id, r.attr, r.category def getRecordByID(self, id): try: i = self.data.index(id) r = self.data[i] except: return None return r.raw, i, r.id, r.attr, r.category def getResource(self, i): try: r = self.data[i] except: return None return r.raw, r.type, r.id def getDBInfo(self): return self.info def setDBInfo(self, info): self.dirty = 1 self.info = {} self.info.update(info) def updateDBInfo(self, info): self.dirty = 1 self.info.update(info) def setRecord(self, attr, id, cat, data): if not self.allow_zero_ids and not id: if not len(self.data): id = 1 else: xid = self.data[0].id + 1 while xid in self.data: xid = xid + 1 id = xid r = PRecord(attr, id, cat, data) if id and id in self.data: self.data.remove(id) self.data.append(r) self.dirty = 1 return id def setResource(self, typ, id, data): if (typ, id) in self.data: self.data.remove((typ,id)) r = PResource(typ, id, data) self.data.append(r) self.dirty = 1 return id def getNextRecord(self, cat): while self.next < len(self.data): r = self.data[self.next] i = self.next self.next = self.next + 1 if r.category == cat: return r.raw, i, r.id, r.attr, r.category return '' def getNextModRecord(self, cat=-1): while self.next < len(self.data): r = self.data[self.next] i = self.next self.next = self.next + 1 if (r.attr & attrModified) and (cat < 0 or r.category == cat): return r.raw, i, r.id, r.attr, r.category def getResourceByID(self, type, id): try: r = self.data[self.data.index((type,id))] except: return None return r.raw, r.type, r.id def deleteRecord(self, id): if not id in self.data: return None self.data.remove(id) self.dirty = 1 def deleteRecords(self): self.data = [] self.dirty = 1 def deleteResource(self, type, id): if not (type,id) in self.data: return None self.data.remove((type,id)) self.dirty = 1 def deleteResources(self): self.data = [] self.dirty = 1 def getRecordIDs(self, sort=0): m = map(lambda x: x.id, self.data) if sort: m.sort() return m def moveCategory(self, frm, to): for r in self.data: if r.category == frm: r.category = to self.dirty = 1 def deleteCategory(self, cat): raise RuntimeError, _("unimplemented") def purge(self): ndata = [] # change to filter later for r in self.data: if (r.attr & attrDeleted): continue ndata.append(r) self.data = ndata self.dirty = 1 def resetNext(self): self.next = 0 def resetFlags(self): # special behavior for resources if not self.info.get('flagResource',0): # use map() for r in self.data: r.attr = r.attr & ~attrDirty self.dirty = 1 import pprint class File(PCache): def __init__(self, name=None, read=1, write=0, info={}): PCache.__init__(self) self.filename = name self.info.update(info) self.writeback = write self.isopen = 0 if read: self.load(name) self.isopen = 1 def close(self): if self.writeback and self.dirty: self.save(self.filename) self.isopen = 0 def __del__(self): if self.isopen: self.close() def load(self, f): if type(f) == type(''): f = open(f, 'rb') data = f.read() self.unpack(data) def unpack(self, data): if len(data) < PI_HDR_SIZE: raise IOError, _("file too short") (name, flags, ver, ctime, mtime, btime, mnum, appinfo, sortinfo, typ, creator, uid, nextrec, numrec) \ = struct.unpack('>32shhLLLlll4s4sllh', data[:PI_HDR_SIZE]) if nextrec or appinfo < 0 or sortinfo < 0 or numrec < 0: raise IOError, _("invalid database header") self.info = { 'name': null_terminated(name), 'type': typ, 'creator': creator, 'createDate': ctime - PILOT_TIME_DELTA, 'modifyDate': mtime - PILOT_TIME_DELTA, 'backupDate': btime - PILOT_TIME_DELTA, 'modnum': mnum, 'version': ver, 'flagReset': flags & flagReset, 'flagResource': flags & flagResource, 'flagNewer': flags & flagNewer, 'flagExcludeFromSync': flags & flagExcludeFromSync, 'flagAppInfoDirty': flags & flagAppInfoDirty, 'flagReadOnly': flags & flagReadOnly, 'flagBackup': flags & flagBackup, 'flagOpen': flags & flagOpen, 'more': 0, 'index': 0 } rsrc = flags & flagResource if rsrc: s = PI_RESOURCE_ENT_SIZE else: s = PI_RECORD_ENT_SIZE entries = [] pos = PI_HDR_SIZE for x in range(0,numrec): hstr = data[pos:pos+s] pos = pos + s if not hstr or len(hstr) < s: raise IOError, _("bad database header") if rsrc: (typ, id, offset) = struct.unpack('>4shl', hstr) entries.append((offset, typ, id)) else: (offset, auid) = struct.unpack('>ll', hstr) attr = (auid & 0xff000000) >> 24 uid = auid & 0x00ffffff entries.append((offset, attr, uid)) offset = len(data) entries.reverse() for of, q, id in entries: size = offset - of if size < 0: raise IOError, _("bad pdb/prc record entry (size < 0)") d = data[of:offset] offset = of if len(d) != size: raise IOError, _("failed to read record") if rsrc: r = PResource(q, id, d) self.data.append(r) else: r = PRecord(q & 0xf0, id, q & 0x0f, d) self.data.append(r) self.data.reverse() if sortinfo: sortinfo_size = offset - sortinfo offset = sortinfo else: sortinfo_size = 0 if appinfo: appinfo_size = offset - appinfo offset = appinfo else: appinfo_size = 0 if appinfo_size < 0 or sortinfo_size < 0: raise IOError, _("bad database header (appinfo or sortinfo size < 0)") if appinfo_size: self.appblock = data[appinfo:appinfo+appinfo_size] if len(self.appblock) != appinfo_size: raise IOError, _("failed to read appinfo block") if sortinfo_size: self.sortblock = data[sortinfo:sortinfo+sortinfo_size] if len(self.sortblock) != sortinfo_size: raise IOError, _("failed to read sortinfo block") def save(self, f): """Dump the cache to a file. """ if type(f) == type(''): f = open(f, 'wb') # first, we need to precalculate the offsets. if self.info.get('flagResource'): entries_len = 10 * len(self.data) else: entries_len = 8 * len(self.data) off = PI_HDR_SIZE + entries_len + 2 if self.appblock: appinfo_offset = off off = off + len(self.appblock) else: appinfo_offset = 0 if self.sortblock: sortinfo_offset = off off = off + len(self.sortblock) else: sortinfo_offset = 0 rec_offsets = [] for x in self.data: rec_offsets.append(off) off = off + len(x.raw) info = self.info flg = 0 if info.get('flagResource',0): flg = flg | flagResource if info.get('flagReadOnly',0): flg = flg | flagReadOnly if info.get('flagAppInfoDirty',0): flg = flg | flagAppInfoDirty if info.get('flagBackup',0): flg = flg | flagBackup if info.get('flagOpen',0): flg = flg | flagOpen if info.get('flagNewer',0): flg = flg | flagNewer if info.get('flagReset',0): flg = flg | flagReset # excludefromsync doesn't actually get stored? hdr = struct.pack('>32shhLLLlll4s4sllh', pad_null(info.get('name',''), 32), flg, info.get('version',0), info.get('createDate',0L)+PILOT_TIME_DELTA, info.get('modifyDate',0L)+PILOT_TIME_DELTA, info.get('backupDate',0L)+PILOT_TIME_DELTA, info.get('modnum',0), appinfo_offset, # appinfo sortinfo_offset, # sortinfo info.get('type',' '), info.get('creator',' '), 0, # uid??? 0, # nextrec??? len(self.data)) f.write(hdr) entries = [] record_data = [] rsrc = self.info.get('flagResource') for x, off in map(None, self.data, rec_offsets): if rsrc: record_data.append(x.raw) entries.append(struct.pack('>4shl', x.type, x.id, off)) else: record_data.append(x.raw) a = ((x.attr | x.category) << 24) | x.id entries.append(struct.pack('>ll', off, a)) for x in entries: f.write(x) f.write('\0\0') # padding? dunno, it's always there. if self.appblock: f.write(self.appblock) if self.sortblock: f.write(self.sortblock) for x in record_data: f.write(x) pyrite-publisher-2.1.1/PyritePublisher/pyrpub0100755000175000000620000000062707427737440020111 0ustar robstaff#!/usr/bin/python # -*-Python-*- import sys try: import dtkmain except: from PyritePublisher import dtkmain try: import gui_wxwin except: try: from PyritePublisher import gui_wxwin except: pass if len(sys.argv) == 1: try: app = gui_wxwin.PPApp(0) app.MainLoop() sys.exit() except: sys.exit() dtkmain.PPCLI()(sys.argv) pyrite-publisher-2.1.1/PyritePublisher/ztxt.py0100644000175000000620000002522007450164551020212 0ustar robstaff# # $Id: ztxt.py,v 1.5 2002/03/26 21:34:33 rob Exp $ # # Copyright 2001 Rob Tillotson # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee or royalty is # hereby granted, provided that the above copyright notice appear in # all copies and that both the copyright notice and this permission # notice appear in supporting documentation or portions thereof, # including modifications, that you you make. # # THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE! # """Read and write Gutenpalm zTXT documents. Gutenpalm is a free document reader for Palm Computing Platform handhelds, which uses its own document format that uses zlib to provide greater compression than the standard Doc format used by most other Palm document readers. This module provides objects which allow reading and writing of zTXT documents using a file-like interface. The zTXTWriteStream object allows creation of a new zTXT document database. Because of restrictions in the Palm database format, it is not possible to append to an existing database, and the entire contents of the database must be held in memory in the zTXTWriteStream instance until the stream is closed. Also note that the zTXTWriteStream MUST be explicitly saved by calling its close() method; if it is deleted by the Python garbage collector, it may not be written to the filesystem. The zTXTReadStream object allows reading of an existing zTXT document database. If the document was compressed in random access mode, it is seekable; otherwise it is only readable sequentially and using the tell() or seek() methods will raise IOError. """ __version__ = '$Id: ztxt.py,v 1.5 2002/03/26 21:34:33 rob Exp $' __author__ = 'Rob Tillotson ' __copyright__ = 'Copyright 2001 Rob Tillotson ' import string, struct, zlib import prc def trim_null(s): return string.split(s, '\0', 1)[0] class zTXTWriteStream: """A writable stream that produces a zTXT document database. """ def __init__(self, filename, name, random_access=1, stored_prefs=0, creator='GPlm', type='zTXT'): self.filename = filename self.name = name self.random_access = random_access self.stored_prefs = stored_prefs self.creator = creator self.type = type self.appinfo = '' self.bookmarks = [] self.annotations = [] self.data = [] self.opened = 1 self.buf = '' self.len = 0 self.compressobj = zlib.compressobj(9) def has_feature(self, f): """Test whether the stream has a particular feature. This is used to differentiate this stream from others that are interface-compatible. """ if f in ['annotate']: return 1 def bookmark(self, title, pos=None): """Set a bookmark. If the second argument is None, the bookmark will be placed at the current position in the document. """ if pos is None: pos = self.len self.bookmarks.append((pos,title)) def annotate(self, text, title='', pos=None): """Set an annotation. If the third argument is None, the annotation will be placed at the current position in the document. """ if pos is None: pos = self.len self.annotations.append((pos, title, text)) def __output(self, flush=0): while (flush and self.buf) or len(self.buf) >= 8192: b = self.buf[:8192] self.buf = self.buf[8192:] cdata = self.compressobj.compress(b) if self.random_access: cdata = cdata + self.compressobj.flush(zlib.Z_FULL_FLUSH) self.data.append(cdata) def write(self, data): self.buf = self.buf + data self.len = self.len + len(data) self.__output() def writelines(self, list): for l in list: self.write(l) def close(self): self.__output(1) if not self.random_access: # reblock into 8k chunks s = string.join(self.data,'') + self.compressobj.flush(zlib.Z_FINISH) self.data = [] while s: self.data.append(s[:8192]) s = s[8192:] if type(self.filename) == type(''): db = prc.File(self.filename, read=0, write=1, info = {'name': self.name, 'creator': self.creator, 'type': self.type, 'version': 296, }) else: db = self.filename db.updateDBInfo({'name': self.name, 'creator': self.creator, 'type': self.type, 'version': 296, }) if len(self.bookmarks): bookmark_index = len(self.data)+1 else: bookmark_index = 0 if len(self.annotations): annotation_index = len(self.data)+1 if len(self.bookmarks): annotation_index = annotation_index + 1 else: annotation_index = 0 hdr = struct.pack('>HHLHHHHHBxH11x', 0x0128, # version 1.40 len(self.data), self.len, 8192, len(self.bookmarks), bookmark_index, len(self.annotations), annotation_index, self.random_access, self.stored_prefs) db.setRecord(0x40, 0x424200, 0, hdr) uid = 0x424201 for r in self.data: db.setRecord(0x40, uid, 0, r) uid = uid + 1 if len(self.bookmarks): self.bookmarks.sort() brec = '' for pos, title in self.bookmarks: brec = brec + struct.pack('>L20s', pos, title[:19]+'\0') db.setRecord(0x40, uid, 0, brec) uid = uid + 1 if len(self.annotations): self.annotations.sort() brec = '' for pos, title, text in self.annotations: brec = brec + struct.pack('>L20s', pos, title[:19]+'\0') db.setRecord(0x40, uid, 0, brec) uid = uid + 1 for pos, title, text in self.annotations: db.setRecord(0x40, uid, 0, (text[:4095]+'\0')) uid = uid + 1 if self.appinfo: db.setAppBlock(self.appinfo) self.opened = 0 db.close() class zTXTReadStream: """A file-like interface to read a zTXT document. """ def __init__(self, file=None, db=None): if db is not None: self.db = db else: self.db = prc.File(file,read=1,write=0) self.rec = 0 self.buf = '' raw, idx, id, attr, category = self.db.getRecord(0) (self.version, self.numrecs, self.length, self.recsize, self.num_bookmarks, self.bookmark_index, self.num_annotations, self.annotation_index, self.random_access, self.stored_prefs) = struct.unpack('>HHLHHHHHBxH11x', raw) # if it's non random_access, it isn't seekable. self.decompressobj = zlib.decompressobj() def __next(self): if self.rec >= self.numrecs: return None else: self.rec = self.rec + 1 raw, idx, id, attr, category = self.db.getRecord(self.rec) self.buf = self.buf + self.decompressobj.decompress(raw) return self.buf def read(self, nbytes=0): if not self.buf: if self.__next() is None: return '' if nbytes: e = self.buf[:nbytes] self.buf = self.buf[nbytes:] return e else: # this should really read the whole file e = self.buf self.buf = '' return e def readline(self): while not '\n' in self.buf: if self.__next() is None: b = self.buf self.buf = '' return b j = string.find(self.buf, '\n') e = self.buf[:j+1] self.buf = self.buf[j+1:] return e def readlines(self): l = [] while 1: m = self.readline() if not m: break l.append(m) return l def tell(self): if not self.random_access: raise IOError, 'not seekable' return (self.rec * self.recsize) - len(self.buf) def seek(self, pos, whence=0): if not self.random_access: raise IOError, 'not seekable' if whence == 1: pos = self.tell() + pos elif whence == 2: pos = self.length + pos if pos >= self.length: pos = self.length self.rec = int(pos / self.recsize) + 1 p = pos % self.recsize raw, idx, id, attr, category = self.db.getRecord(self.rec) self.buf = zlib.decompressobj.decompress(raw) def close(self): self.db.close() self.rec = 0 self.buf = '' def has_feature(self, f): if f in ['annotate']: return 1 def get_bookmarks(self): l = [] raw = self.db.getRecord(self.bookmark_index)[0] for x in range(0, self.num_bookmarks): pos, text = struct.unpack('>L20s', raw[:24]) text = trim_null(text) l.append((pos, text)) raw = raw[24:] l.sort() return l def get_annotations(self): l = [] annrec = self.db.getRecord(self.annotation_index)[0] for x in range(0, self.num_annotations): pos, title = struct.unpack('>L20s', annrec[:24]) title = trim_null(title) annrec = annrec[24:] text = trim_null(self.db.getRecord(self.annotation_index + x + 1)[0]) l.append((pos, title, text)) l.sort() return l def __getattr__(self, k): if k == 'title': return self.db.getDBInfo()['name'] elif k == 'bookmarks': return self.get_bookmarks() elif k == 'annotations': return self.get_annotations() else: raise AttributeError, k if __name__ == '__main__': import sys fn = sys.argv[1] s = zTXTReadStream(fn) print string.join(s.readlines(), '') pyrite-publisher-2.1.1/doc/0040755000175000000620000000000007514642022014237 5ustar robstaffpyrite-publisher-2.1.1/doc/pyrite-publisher/0040755000175000000620000000000007514642022017546 5ustar robstaffpyrite-publisher-2.1.1/doc/pyrite-publisher/index.html0100644000175000000620000010326607412012050021535 0ustar robstaff Pyrite Publisher

Pyrite Publisher

Rob Tillotson



Contents

1 Introduction

The Doc format is a simple text database format used by e-book readers on the Palm Computing Platform. Although there are a few proprietary e-book formats still in use, the Doc format is by far the most widely supported way to store long texts on the Palm platform, with over a dozen readers and editors supporting it.

In its simplest form, a Doc database is just compressed text, in a form designed for efficient storage and navigation on the palmtop. However, many newer readers support some form of "rich text" markup, either a subset of HTML or their own unique tagging schemes.

While all of these augmented readers can be an improvement over a traditional text-only reader, the proliferation of markup formats means that every one of them needs to have its own conversion program (which, often, is not available for any OS except Windows) to create Doc databases with the proper markup. It also means that the same Doc database might or might not be readable on a different reader, depending on what sort of markup is in it. Pyrite Publisher is intended to address these problems (to some extent) by providing a conversion framework with pluggable modules.

Pyrite Publisher separates the interpretation of its input from the rendering of that input into a specific markup language. During conversion, an input plugin is used to convert the input (usually text, HTML, or something similar) to a sequence of formatting events which are independent of any particular markup language; an output plugin converts those into the markup used by a particular Doc reader and creates the resulting Doc database.

2 Using Pyrite Publisher

Pyrite Publisher is a command-line program. A graphical user interface may be added in the future, but at present it can be run from the command line only. Normally, Pyrite Publisher's command line interface is called pyrpub, so the most basic way to use it is something like this:

% pyrpub foo.txt

Pyrite Publisher will try to convert the named file in the best way it knows how, based on the type of data in the file. For example, if you give Pyrite Publisher a HTML file as input, it will automatically use its HTML parser. The result of the conversion will be a file named foo.pdb which you can install on your handheld.

3 Plugins and Chains

Most of Pyrite Publisher is built out of a set of plugins, which are assembled into chains to convert an input file to a Palm database. Each plugin has an input and an output; at the beginning of the chain the first plugin's input is the file you specify on the command line, and at the end of the chain the last plugin's output is the database you install on your handheld. In between, each plugin analyzes and modifies the data before passing it to the next plugin in the chain.

When you run Pyrite Publisher, it tries to construct a chain of plugins that can successfully convert the input file to a database. The plugins supplied with Pyrite Publisher are designed to be interchangeable so you can get different results simply by using different plugins.

You can choose plugins to use by using the -P command-line option. Pyrite Publisher will attempt to use the plugins you name, filling in the rest of the chain with whatever defaults are appropriate (unless the requested combination is impossible). You do not need to specify the whole chain, and order doesn't matter. For example, to use the tagged-text parser and create a zTXT instead of a regular Doc database, you could do the following:

% pyrpub -P TaggedText,zTXT foo.txt

Each plugin may have its own command-line options to control its behavior. The help text (viewable with the -h option) shows the available options for all plugins, and you can list all of the installed plugins with pyrpub -l.

3.1 E-Book Conversion Chains

In most cases, the process of converting an input file to an e-book database is divided into four parts, each handled by one plugin:

  • Retrieving the input file (URLStream or PDBInput)
  • Parsing the input: reflowing text, finding bookmarks, analyzing markup, etc. (RawText, Text, TaggedText, and HTML)
  • Applying markup and other reader-specific features (BasicDoc, TealDoc, and RichReader)
  • Writing to the database (DocOutput and zTXT)

This process allows for support of multiple document formats without a lot of code duplication. A single parser plugin can format the same input for many different e-book readers, given appropriate choices for the following plugins.

3.2 Advanced Plugin Management: Protocols and Priorities

Internally, Pyrite Publisher links plugins into chains by the use of named protocols. A protocol is simply a specification for how two plugins talk to one another. For example, the doc-assembler protocol describes how a parser plugin like HTML talks to a markup plugin like RichReader. Every plugin knows what input protocols it supports, and what output protocol it will use when given a particular type of input, and Pyrite Publisher uses this information to decide what chains of plugins are possible.

The only exception to this is at the beginning of the chain. The first plugin in the chain is responsible from getting input from outside of Pyrite Publisher, from a file or URL or some other location. Its ``output protocol'' is actually the MIME type of the data.

For example, if you tell Pyrite Publisher to convert the file foo.html (and you have the standard set of plugins), a typical chain would go something like this:

  1. Since there are only two input plugins available, URLStream and PDBInput, and this is not a .pdb or .prc file, the URLStream plugin is activated to read the input file.
  2. The input file contains HTML, so the URLStream plugin declares that it will use the text/html or application/octet-stream protocol to feed the data to the next plugin.
  3. The HTML plugin can handle the text/html protocol, so it seems like a good choice to be the next one in the stream. Its output follows the doc-assembler protocol.
  4. There are several plugins that support the doc-assembler protocol; the default is BasicDoc. The output of BasicDoc follows the doc-database protocol.
  5. The plugin that handles the doc-database protocol by default is DocOutput. It declares no output, so the chain ends here.

Some protocols are implemented by multiple plugins. In the standard set of plugins, for example, there are at least three that handle text/plain and three that handle doc-assembler. This means there are many possible ways to build a chain that converts a particular file into some kind of output. Pyrite Publisher chooses which one of these possible chains to use based on a priority system that lets each plugin declare how good its handling of a particular protocol is.

For example, among the standard set of plugins plain text files can be handled by the Text, RawText, TaggedText, and HTML plugins. Of those, the most generally useful choice is Text, so it declares a higher priority for the text/plain protocol than the others.

If you name one or more plugins on the command line, Pyrite Publisher will choose a chain that includes them and use priorities to resolve any ambiguities that still exist. You can adjust the priorities of individual plugins using the configuration file, described below.

4 Configuration and Options

Each Pyrite Publisher plugin can have its own set of configurable options, called properties. The command pyrpub -list-properties will display a list of all plugin properties.

At startup, Pyrite Publisher reads a configuration file called .pyrpub in your home directory. This configuration file can contain two types of statements: set statements which set a plugin property, and priority statements which control plugin priorities.

A set statement looks like this:

  • set plugin.property = value ;

The semicolon at the end of the line is required. String values don't need to be quoted unless they contain non-alphanumeric characters. For boolean properties, the value should be either ``1'' or ``0''.

The priority statement comes in three forms:

  • priority plugin input-protocol output-protocol number ;
  • priority plugin input-protocol number
  • priority plugin number

The first form sets a plugin's priority for a specific pair of input and output protocols; if that combination is invalid there will be no effect. The second form sets the plugin's priority for a specific input protocol and all possible outputs. The third form sets the plugin's priority for all possible combinations of input and output.

The second form is probably the most useful. If you use TealDoc all the time, for example, the following statement in your .pyrpubrc file might be helpful:

priority TealDoc ``doc-assembler'' 100

5 Standard Plugins

The plugins described in this section are provided with Pyrite Publisher. If any of them are missing, it is likely that your installation is broken.

Unless otherwise specified, all plugin properties can be specified on the command line, by translating underscores to dashes. For example, a property called some_property will have a corresponding command line option called --some-property.

5.1 Protocols and Priorities

The standard plugins use the following protocols:

Protocol  Priorities  Description 
application/octet-stream RawText 0 MIME type for arbitrary data input
text/html HTML 0 MIME type for HTML input
text/plain Text 0, HTML -10, TaggedText -10 MIME type for text input
doc-assembler BasicDoc 10, RichReader 0, TealDoc 0 Markup generator
doc-output DocOutput 0, TextOutput -1000, zTXT -1 General e-book database output
doc-only-output DocOutput 0, TextOutput -1000 Doc-format database output
DOC:raw CopyDoc 0 Special protocol for e-book metadata passing

5.2 URLStream - Input from a file or URL

The URLStream plugin is the standard input handler. It allows Pyrite Publisher to retrieve input from local files or from remote locations specified by http or ftp URLs. If the file or remote URL ends in .gz it will be un-gzipped automatically. This plugin also determines the MIME type of the input, which in turn determines what the next plugin in the chain will be.

The URLStream plugin has no properties or command-line options.

5.3 PDBInput - Input from a Doc or zTXT database

The PDBInput plugin handles input from files or URLs ending in .pdb or .prc. Unlike URLStream it does not automatically un-gzip. If the input is a Doc or zTXT database it will be decompressed and treated as if it was a text file. In addition, metadata such as the document title and bookmarks may be passed along if the next plugin in the chain is compatible. If the input is not a Doc or zTXT database, or if it isn't a Palm database at all, it won't be converted at all.

The PDBInput plugin has no properties or command-line options.

5.4 RawText - Raw text parser

The RawText plugin takes raw text input and passes it along to an assembler plugin without any additional processing whatsoever.

The RawText plugin has no properties or command-line options.

5.5 Text - Text parser

The Text plugin takes text input and re-flows it to be more readable in a handheld document reader. Generally this consists of joining paragraphs into long lines so that they will be wrapped by the reader on the handheld. In addition, this plugin can automatically add bookmarks based on a regular expression search.

The Text plugin has the following properties:

bookmark_regexps
Regular expressions to search for and bookmark. May be specified multiple times on in the configuration file or on the command line to search for more than one regular expression. (Command line option -r)
indented_paragraphs
(boolean) If true, checks for indentation to determine the beginning of paragraphs, instead of looking for blank lines separating them. (Command line option --indented-paragraphs)

5.6 TaggedText - Tagged text parser

The TaggedText plugin does the same sort of paragraph reformatting as the Text plugin, but it also interprets special markup tags embedded in the text to set bookmarks, add annotations and headers, and the like. This is intended primarily to make it easier to produce e-books from plain text files, by marking interesting bits without requiring a full markup language like HTML.

The tags supported by this plugin are as follows:

TITLE title
Set the document title.
BOOKMARK label
Set a bookmark.
BM label
Same as BOOKMARK.
Hn text
Insert a heading; ``n'' is a digit from 1 to 6 specifying the heading level.
ANNOTATION label
Insert an annotation. The annotation text follows this tag, up to an /ANNOTATION tag.
PRE
Insert preformatted text. The text following this tag up to a /PRE tag is copied verbatim without reflowing paragraphs.
HR
Insert a horizontal rule.

By default, all tags must appear on a line by themselves, prefixed by a single period.

The TaggedText plugin has the following properties:

bookmark_regexps
Regular expressions to search for and bookmark. May be specified multiple times on in the configuration file or on the command line to search for more than one regular expression. (Command line option -r)
case_fold_tags
(boolean) If true, markup tags can be lowercase or mixed-case.
tag_prefix
The prefix which appears before each tag (the default is a single period).
tag_regexp
A regular expression which matches a tag and its argument. It must contain two named groups, tag and arg, which match the tag and its argument respectively. A string-substitution variable named prefix will be replaced by the value of the tag_prefix property.

5.7 HTML - HTML parser

The HTML plugin parses HTML input and creates markup events which the next plugin in the chain can use to display it appropriately on the handheld. (If the next plugin ignores markup, the result will be to simply strip HTML tags from the input.) It can also produce footnotes showing link targets and set bookmarks at HTML headers and anchors.

The HTML plugin has the following properties:

annotate_links
(boolean) If true, links in the input HTML will be marked in the output text, and the link target will be added to the output document as an annotation.
bookmark_anchors
(boolean) If true, anchors with a name attribute will be bookmarked.
bookmark_headers
The value of this property is a string which can contain one or more of the digits 1 through 6. The corresponding HTML header levels will be bookmarked.
footnote_links
(boolean) If true, links in the input HTML will be marked in the output text, and the link targets will be listed at the end of the output text.

5.8 BasicDoc - Standard e-book assembler

The BasicDoc plugin creates a plain-text e-book, ignoring any markup the previous plugin asks for.

The BasicDoc plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.

5.9 TealDoc - TealDoc-enhanced e-book assembler

The TealDoc plugin creates a Doc e-book with markup that is viewable in the TealDoc application. (The resulting document can be read in a normal Doc reader, but the markup will be visible as tags embedded in the text.)

The TealDoc plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.
hN_style
The style of headings; N is a digit from 1 to 6 specifying a heading level. A style consists of one or more words, separated by spaces, which may include: ``large'' or ``bold'' to specify font size, ``underline'' or ``invert'' to specify font decoration, and ``center'' or ``right'' to specify justification.

5.10 RichReader - RichReader-enhanced e-book assembler

The RichReader plugin creates a Doc e-book with markup that is viewable in the RichReader application. (The resulting document can be read in a normal Doc reader, but the markup will be visible as ``garbage'' characters embedded in the text.)

The RichReader plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.
hN_style
The style of headings; N is a digit from 1 to 6 specifying a heading level. A style consists of one or more words, separated by spaces, which may include: ``normal'', ``small'', or ``large'' to specify font size; one or more of ``bold'', ``italic'', ``fixed'', ``underline'', and ``dotted'' to specify font decoration; and ``left'', ``right'', or ``center'' to specify justification. Note that these properties cannot be set from the command line, only in the configuration file.

5.11 DocOutput - Output to a Doc-format database

The DocOutput plugin creates a Doc-format e-book database, usable by any Doc-compatible application.

The DocOutput plugin has the following properties:

title
The document title. (Command line option -t)
backup
(boolean) If true, set the backup bit of the document database. (Command line option -b)
category
The category number of the document. (Note: This is only useful if you know the category name to number mapping of your document reader.) (Command line option -c)
creator
The creator ID of the document database. The default is ``REAd''. (Command line option -C)
type
The type ID of the document database. The default is ``TEXt''. (Command line option -T)
output_directory
Directory to place document databases into. (Command line option -d)
output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)

5.12 zTXT - Output to a zTXT-format database

The zTXT plugin creates a zTXT-format e-book database. The zTXT format is currently supported by the Weasel reader, and provides annotations and better compression than the Doc format.

The zTXT plugin has the following properties:

title
The document title. (Command line option -t)
output_directory
Directory to place document databases into. (Command line option -d)
output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)
random_access
(boolean) If true the document is compressed in ``random access'' mode which provides better efficiency and allows larger documents to be read, at the cost of a few percent of compression. The default is for this property to be true, so specifying it on the command line will turn it off.

5.13 TextOutput - Output to a text file or the console

The TextOutput plugin sends output to the console or to a text file, instead of putting it in a database, to facilitate conversion of e-books back to plain text.

The TextOutput plugin has the following properties:

output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)
dump_annotations
(boolean) If true, output all annotation records (if present) after the text.
dump_bookmarks
(boolean) If true, output all bookmarks (if present) after the text.
dump_appinfo
(boolean) If true, output a string representation of the appinfo block (if present) after the text.
dump_all
(boolean) If true, dump all annotations, bookmarks, and appinfo block after the text.

5.14 CopyDoc - Copy from one e-book database format to another

The CopyDoc plugin works with the PDBInput plugin to copy a document directly to an output plugin without any parsing or other processing. It is intended to allow one document format to be converted to another; for example, the following command will convert an existing e-book to zTXT format, preserving bookmarks and annotations if possible:

% pyrpub -P CopyDoc,zTXT foo.pdb

6 Plugin Developers Reference

This section will be filled in later.

7 Protocols Reference

This section will be filled in later.

8 Low-Level Modules Reference

This section will be filled in later.

About this document ...

Pyrite Publisher

This document was generated using the LaTeX2HTML translator.

LaTeX2HTML is Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds, and Copyright © 1997, 1998, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The application of LaTeX2HTML to the Python documentation has been heavily tailored by Fred L. Drake, Jr. Original navigation icons were contributed by Christopher Petrilli.

pyrite-publisher-2.1.1/doc/pyrite-publisher/pyrite-publisher.css0100644000175000000620000000621507412012050023555 0ustar robstaff/* * The first part of this is the standard CSS generated by LaTeX2HTML, * with the "empty" declarations removed. */ /* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ .math { font-family: "Century Schoolbook", serif; } .math i { font-family: "Century Schoolbook", serif; font-weight: bold } .boldmath { font-family: "Century Schoolbook", serif; font-weight: bold } /* Implement both fixed-size and relative sizes: */ small.xtiny { font-size : xx-small } small.tiny { font-size : x-small } small.scriptsize { font-size : smaller } small.footnotesize { font-size : small } big.xlarge { font-size : large } big.xxlarge { font-size : x-large } big.huge { font-size : larger } big.xhuge { font-size : xx-large } /* * Document-specific styles come next; * these are added for the Python documentation. * * Note that the size specifications for the H* elements are because * Netscape on Solaris otherwise doesn't get it right; they all end up * the normal text size. */ body { color: #000000; background-color: #ffffff; } a:active { color: #ff0000; } a:visited { color: #551a8b; } a:link { color: #0000bb; } h1, h2, h3, h4, h5, h6 { font-family: sans-serif; font-weight: bold } h1 { font-size: 180% } h2 { font-size: 150% } h3, h4 { font-size: 120% } code, tt { font-family: monospace } var { font-family: serif; font-style: italic; font-weight: normal } .navigation td { background-color: #99ccff; font-weight: bold; font-family: sans-serif; font-size: 110% } .titlegraphic { vertical-align: top; } .verbatim { color: #00008b } .email { font-family: sans-serif } .mimetype { font-family: sans-serif } .newsgroup { font-family: sans-serif } .url { font-family: sans-serif } .file { font-family: sans-serif } .tableheader td { background-color: #99ccff; } .tableheader th { background-color: #99ccff; font-family: sans-serif; } .refcount-info { font-style: italic } .refcount-info .value { font-weight: bold; color: #006600 } /* * Some decoration for the "See also:" blocks, in part inspired by some of * the styling on Lars Marius Garshol's XSA pages. * (The blue in the navigation bars is #99CCFF.) */ .seealso { background-color: #fffaf0; border: thin solid black; padding: 4pt } .seealso .heading { font-size: 110% } /* * Class 'availability' is used for module availability statements at * the top of modules. */ .availability .platform { font-weight: bold } pyrite-publisher-2.1.1/doc/pyrite-publisher/pyrite-publisher.how0100644000175000000620000000074307411776220023601 0ustar robstaff+++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html +++ perl /home/rob/c/pydoc/Python-2.0/Doc/tools/node2label.pl *.html pyrite-publisher-2.1.1/doc/pyrite-publisher/pyrite-publisher.html0100644000175000000620000010326607412012050023735 0ustar robstaff Pyrite Publisher

Pyrite Publisher

Rob Tillotson



Contents

1 Introduction

The Doc format is a simple text database format used by e-book readers on the Palm Computing Platform. Although there are a few proprietary e-book formats still in use, the Doc format is by far the most widely supported way to store long texts on the Palm platform, with over a dozen readers and editors supporting it.

In its simplest form, a Doc database is just compressed text, in a form designed for efficient storage and navigation on the palmtop. However, many newer readers support some form of "rich text" markup, either a subset of HTML or their own unique tagging schemes.

While all of these augmented readers can be an improvement over a traditional text-only reader, the proliferation of markup formats means that every one of them needs to have its own conversion program (which, often, is not available for any OS except Windows) to create Doc databases with the proper markup. It also means that the same Doc database might or might not be readable on a different reader, depending on what sort of markup is in it. Pyrite Publisher is intended to address these problems (to some extent) by providing a conversion framework with pluggable modules.

Pyrite Publisher separates the interpretation of its input from the rendering of that input into a specific markup language. During conversion, an input plugin is used to convert the input (usually text, HTML, or something similar) to a sequence of formatting events which are independent of any particular markup language; an output plugin converts those into the markup used by a particular Doc reader and creates the resulting Doc database.

2 Using Pyrite Publisher

Pyrite Publisher is a command-line program. A graphical user interface may be added in the future, but at present it can be run from the command line only. Normally, Pyrite Publisher's command line interface is called pyrpub, so the most basic way to use it is something like this:

% pyrpub foo.txt

Pyrite Publisher will try to convert the named file in the best way it knows how, based on the type of data in the file. For example, if you give Pyrite Publisher a HTML file as input, it will automatically use its HTML parser. The result of the conversion will be a file named foo.pdb which you can install on your handheld.

3 Plugins and Chains

Most of Pyrite Publisher is built out of a set of plugins, which are assembled into chains to convert an input file to a Palm database. Each plugin has an input and an output; at the beginning of the chain the first plugin's input is the file you specify on the command line, and at the end of the chain the last plugin's output is the database you install on your handheld. In between, each plugin analyzes and modifies the data before passing it to the next plugin in the chain.

When you run Pyrite Publisher, it tries to construct a chain of plugins that can successfully convert the input file to a database. The plugins supplied with Pyrite Publisher are designed to be interchangeable so you can get different results simply by using different plugins.

You can choose plugins to use by using the -P command-line option. Pyrite Publisher will attempt to use the plugins you name, filling in the rest of the chain with whatever defaults are appropriate (unless the requested combination is impossible). You do not need to specify the whole chain, and order doesn't matter. For example, to use the tagged-text parser and create a zTXT instead of a regular Doc database, you could do the following:

% pyrpub -P TaggedText,zTXT foo.txt

Each plugin may have its own command-line options to control its behavior. The help text (viewable with the -h option) shows the available options for all plugins, and you can list all of the installed plugins with pyrpub -l.

3.1 E-Book Conversion Chains

In most cases, the process of converting an input file to an e-book database is divided into four parts, each handled by one plugin:

  • Retrieving the input file (URLStream or PDBInput)
  • Parsing the input: reflowing text, finding bookmarks, analyzing markup, etc. (RawText, Text, TaggedText, and HTML)
  • Applying markup and other reader-specific features (BasicDoc, TealDoc, and RichReader)
  • Writing to the database (DocOutput and zTXT)

This process allows for support of multiple document formats without a lot of code duplication. A single parser plugin can format the same input for many different e-book readers, given appropriate choices for the following plugins.

3.2 Advanced Plugin Management: Protocols and Priorities

Internally, Pyrite Publisher links plugins into chains by the use of named protocols. A protocol is simply a specification for how two plugins talk to one another. For example, the doc-assembler protocol describes how a parser plugin like HTML talks to a markup plugin like RichReader. Every plugin knows what input protocols it supports, and what output protocol it will use when given a particular type of input, and Pyrite Publisher uses this information to decide what chains of plugins are possible.

The only exception to this is at the beginning of the chain. The first plugin in the chain is responsible from getting input from outside of Pyrite Publisher, from a file or URL or some other location. Its ``output protocol'' is actually the MIME type of the data.

For example, if you tell Pyrite Publisher to convert the file foo.html (and you have the standard set of plugins), a typical chain would go something like this:

  1. Since there are only two input plugins available, URLStream and PDBInput, and this is not a .pdb or .prc file, the URLStream plugin is activated to read the input file.
  2. The input file contains HTML, so the URLStream plugin declares that it will use the text/html or application/octet-stream protocol to feed the data to the next plugin.
  3. The HTML plugin can handle the text/html protocol, so it seems like a good choice to be the next one in the stream. Its output follows the doc-assembler protocol.
  4. There are several plugins that support the doc-assembler protocol; the default is BasicDoc. The output of BasicDoc follows the doc-database protocol.
  5. The plugin that handles the doc-database protocol by default is DocOutput. It declares no output, so the chain ends here.

Some protocols are implemented by multiple plugins. In the standard set of plugins, for example, there are at least three that handle text/plain and three that handle doc-assembler. This means there are many possible ways to build a chain that converts a particular file into some kind of output. Pyrite Publisher chooses which one of these possible chains to use based on a priority system that lets each plugin declare how good its handling of a particular protocol is.

For example, among the standard set of plugins plain text files can be handled by the Text, RawText, TaggedText, and HTML plugins. Of those, the most generally useful choice is Text, so it declares a higher priority for the text/plain protocol than the others.

If you name one or more plugins on the command line, Pyrite Publisher will choose a chain that includes them and use priorities to resolve any ambiguities that still exist. You can adjust the priorities of individual plugins using the configuration file, described below.

4 Configuration and Options

Each Pyrite Publisher plugin can have its own set of configurable options, called properties. The command pyrpub -list-properties will display a list of all plugin properties.

At startup, Pyrite Publisher reads a configuration file called .pyrpub in your home directory. This configuration file can contain two types of statements: set statements which set a plugin property, and priority statements which control plugin priorities.

A set statement looks like this:

  • set plugin.property = value ;

The semicolon at the end of the line is required. String values don't need to be quoted unless they contain non-alphanumeric characters. For boolean properties, the value should be either ``1'' or ``0''.

The priority statement comes in three forms:

  • priority plugin input-protocol output-protocol number ;
  • priority plugin input-protocol number
  • priority plugin number

The first form sets a plugin's priority for a specific pair of input and output protocols; if that combination is invalid there will be no effect. The second form sets the plugin's priority for a specific input protocol and all possible outputs. The third form sets the plugin's priority for all possible combinations of input and output.

The second form is probably the most useful. If you use TealDoc all the time, for example, the following statement in your .pyrpubrc file might be helpful:

priority TealDoc ``doc-assembler'' 100

5 Standard Plugins

The plugins described in this section are provided with Pyrite Publisher. If any of them are missing, it is likely that your installation is broken.

Unless otherwise specified, all plugin properties can be specified on the command line, by translating underscores to dashes. For example, a property called some_property will have a corresponding command line option called --some-property.

5.1 Protocols and Priorities

The standard plugins use the following protocols:

Protocol  Priorities  Description 
application/octet-stream RawText 0 MIME type for arbitrary data input
text/html HTML 0 MIME type for HTML input
text/plain Text 0, HTML -10, TaggedText -10 MIME type for text input
doc-assembler BasicDoc 10, RichReader 0, TealDoc 0 Markup generator
doc-output DocOutput 0, TextOutput -1000, zTXT -1 General e-book database output
doc-only-output DocOutput 0, TextOutput -1000 Doc-format database output
DOC:raw CopyDoc 0 Special protocol for e-book metadata passing

5.2 URLStream - Input from a file or URL

The URLStream plugin is the standard input handler. It allows Pyrite Publisher to retrieve input from local files or from remote locations specified by http or ftp URLs. If the file or remote URL ends in .gz it will be un-gzipped automatically. This plugin also determines the MIME type of the input, which in turn determines what the next plugin in the chain will be.

The URLStream plugin has no properties or command-line options.

5.3 PDBInput - Input from a Doc or zTXT database

The PDBInput plugin handles input from files or URLs ending in .pdb or .prc. Unlike URLStream it does not automatically un-gzip. If the input is a Doc or zTXT database it will be decompressed and treated as if it was a text file. In addition, metadata such as the document title and bookmarks may be passed along if the next plugin in the chain is compatible. If the input is not a Doc or zTXT database, or if it isn't a Palm database at all, it won't be converted at all.

The PDBInput plugin has no properties or command-line options.

5.4 RawText - Raw text parser

The RawText plugin takes raw text input and passes it along to an assembler plugin without any additional processing whatsoever.

The RawText plugin has no properties or command-line options.

5.5 Text - Text parser

The Text plugin takes text input and re-flows it to be more readable in a handheld document reader. Generally this consists of joining paragraphs into long lines so that they will be wrapped by the reader on the handheld. In addition, this plugin can automatically add bookmarks based on a regular expression search.

The Text plugin has the following properties:

bookmark_regexps
Regular expressions to search for and bookmark. May be specified multiple times on in the configuration file or on the command line to search for more than one regular expression. (Command line option -r)
indented_paragraphs
(boolean) If true, checks for indentation to determine the beginning of paragraphs, instead of looking for blank lines separating them. (Command line option --indented-paragraphs)

5.6 TaggedText - Tagged text parser

The TaggedText plugin does the same sort of paragraph reformatting as the Text plugin, but it also interprets special markup tags embedded in the text to set bookmarks, add annotations and headers, and the like. This is intended primarily to make it easier to produce e-books from plain text files, by marking interesting bits without requiring a full markup language like HTML.

The tags supported by this plugin are as follows:

TITLE title
Set the document title.
BOOKMARK label
Set a bookmark.
BM label
Same as BOOKMARK.
Hn text
Insert a heading; ``n'' is a digit from 1 to 6 specifying the heading level.
ANNOTATION label
Insert an annotation. The annotation text follows this tag, up to an /ANNOTATION tag.
PRE
Insert preformatted text. The text following this tag up to a /PRE tag is copied verbatim without reflowing paragraphs.
HR
Insert a horizontal rule.

By default, all tags must appear on a line by themselves, prefixed by a single period.

The TaggedText plugin has the following properties:

bookmark_regexps
Regular expressions to search for and bookmark. May be specified multiple times on in the configuration file or on the command line to search for more than one regular expression. (Command line option -r)
case_fold_tags
(boolean) If true, markup tags can be lowercase or mixed-case.
tag_prefix
The prefix which appears before each tag (the default is a single period).
tag_regexp
A regular expression which matches a tag and its argument. It must contain two named groups, tag and arg, which match the tag and its argument respectively. A string-substitution variable named prefix will be replaced by the value of the tag_prefix property.

5.7 HTML - HTML parser

The HTML plugin parses HTML input and creates markup events which the next plugin in the chain can use to display it appropriately on the handheld. (If the next plugin ignores markup, the result will be to simply strip HTML tags from the input.) It can also produce footnotes showing link targets and set bookmarks at HTML headers and anchors.

The HTML plugin has the following properties:

annotate_links
(boolean) If true, links in the input HTML will be marked in the output text, and the link target will be added to the output document as an annotation.
bookmark_anchors
(boolean) If true, anchors with a name attribute will be bookmarked.
bookmark_headers
The value of this property is a string which can contain one or more of the digits 1 through 6. The corresponding HTML header levels will be bookmarked.
footnote_links
(boolean) If true, links in the input HTML will be marked in the output text, and the link targets will be listed at the end of the output text.

5.8 BasicDoc - Standard e-book assembler

The BasicDoc plugin creates a plain-text e-book, ignoring any markup the previous plugin asks for.

The BasicDoc plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.

5.9 TealDoc - TealDoc-enhanced e-book assembler

The TealDoc plugin creates a Doc e-book with markup that is viewable in the TealDoc application. (The resulting document can be read in a normal Doc reader, but the markup will be visible as tags embedded in the text.)

The TealDoc plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.
hN_style
The style of headings; N is a digit from 1 to 6 specifying a heading level. A style consists of one or more words, separated by spaces, which may include: ``large'' or ``bold'' to specify font size, ``underline'' or ``invert'' to specify font decoration, and ``center'' or ``right'' to specify justification.

5.10 RichReader - RichReader-enhanced e-book assembler

The RichReader plugin creates a Doc e-book with markup that is viewable in the RichReader application. (The resulting document can be read in a normal Doc reader, but the markup will be visible as ``garbage'' characters embedded in the text.)

The RichReader plugin has the following properties:

footnote_marker_format
The format of footnote markers in the output text. This property should contain a single %s which will be replaced by the footnote number.
hN_style
The style of headings; N is a digit from 1 to 6 specifying a heading level. A style consists of one or more words, separated by spaces, which may include: ``normal'', ``small'', or ``large'' to specify font size; one or more of ``bold'', ``italic'', ``fixed'', ``underline'', and ``dotted'' to specify font decoration; and ``left'', ``right'', or ``center'' to specify justification. Note that these properties cannot be set from the command line, only in the configuration file.

5.11 DocOutput - Output to a Doc-format database

The DocOutput plugin creates a Doc-format e-book database, usable by any Doc-compatible application.

The DocOutput plugin has the following properties:

title
The document title. (Command line option -t)
backup
(boolean) If true, set the backup bit of the document database. (Command line option -b)
category
The category number of the document. (Note: This is only useful if you know the category name to number mapping of your document reader.) (Command line option -c)
creator
The creator ID of the document database. The default is ``REAd''. (Command line option -C)
type
The type ID of the document database. The default is ``TEXt''. (Command line option -T)
output_directory
Directory to place document databases into. (Command line option -d)
output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)

5.12 zTXT - Output to a zTXT-format database

The zTXT plugin creates a zTXT-format e-book database. The zTXT format is currently supported by the Weasel reader, and provides annotations and better compression than the Doc format.

The zTXT plugin has the following properties:

title
The document title. (Command line option -t)
output_directory
Directory to place document databases into. (Command line option -d)
output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)
random_access
(boolean) If true the document is compressed in ``random access'' mode which provides better efficiency and allows larger documents to be read, at the cost of a few percent of compression. The default is for this property to be true, so specifying it on the command line will turn it off.

5.13 TextOutput - Output to a text file or the console

The TextOutput plugin sends output to the console or to a text file, instead of putting it in a database, to facilitate conversion of e-books back to plain text.

The TextOutput plugin has the following properties:

output_filename
Full name (including directory) of the output database. Note that this will produce unwanted results if you are converting more than one document at one time. (Command line option -o)
dump_annotations
(boolean) If true, output all annotation records (if present) after the text.
dump_bookmarks
(boolean) If true, output all bookmarks (if present) after the text.
dump_appinfo
(boolean) If true, output a string representation of the appinfo block (if present) after the text.
dump_all
(boolean) If true, dump all annotations, bookmarks, and appinfo block after the text.

5.14 CopyDoc - Copy from one e-book database format to another

The CopyDoc plugin works with the PDBInput plugin to copy a document directly to an output plugin without any parsing or other processing. It is intended to allow one document format to be converted to another; for example, the following command will convert an existing e-book to zTXT format, preserving bookmarks and annotations if possible:

% pyrpub -P CopyDoc,zTXT foo.pdb

6 Plugin Developers Reference

This section will be filled in later.

7 Protocols Reference

This section will be filled in later.

8 Low-Level Modules Reference

This section will be filled in later.

About this document ...

Pyrite Publisher

This document was generated using the LaTeX2HTML translator.

LaTeX2HTML is Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds, and Copyright © 1997, 1998, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The application of LaTeX2HTML to the Python documentation has been heavily tailored by Fred L. Drake, Jr. Original navigation icons were contributed by Christopher Petrilli.

pyrite-publisher-2.1.1/doc/pyrite-publisher-rich.pdb0100644000175000000620000002765707412012060021164 0ustar robstaffPyrite Publisher|%|%|%TEXtREAd"`@op@o @o@o@o!=@o'@o-@o-@o-@o -@o -@o -@o . @o .@o.3@o.G@o.[@o.o@o.@o.@o.@o.@o.@o.@o.@o/@o/#@o/7@o/K@o/_@o/s@o/@o /@o![|PUpneevel@yriteublisher DUp:PThȁroject@ dρ@DRobillotsonx rob@p*.orgPJGContents1ntroducti2sing_\3luginsndhaXAĂ3.1-Book versЁ?2dvancedlanagem:tocolPrioie24figuraфZOp)-5tar/(7742XURLStreamP -hputromiler 3PDBBDoczTXTatabasev4pRawTexӀ`hars5.5.q6 agged?z.7(HTMLIK80Basic*e-b1assembl.9ald-enhׁ׌01ichReadJ~߃ߎ1@Out[o-format揁ၷǒK8"etc ol1Copy a+oȋ4 yno}6uDqopefereX78ow-Z͠leUAboqhisocu ...|dעѥj aimp8JusbyE0s:Palmmjlat.lthoughire!ewroprietarcst 8,2OI0famostidelyupport0waystolooip,th8dozenedi8Yit. In@`(cjucre؟,饠esignؘPffici)navigӋ|pxtop.owXr,anyew0݈omKf "rZ"rkup,subset9irw`niqu`yschsHWhaaugᲀ=canq rhRtra(al*-onxhliЈ,fܑmeathPyyb{medoavAȸ}Xgram (whh,8 s vaila9OSxceptindows)cȽs.ɂlsoϞ)eaXmighЀJ@񄚻iiftdependQpwsQϡ8Hyxublis9ڈd!8ddюԝms (ɹ)َianworkSpgmh/,seHx„rtiwQ(Azhispecilangu.u8iIs9{(usuھ Йiimr:0eHМy(n"B:Rf҈ticu`?;;`UoaA㮍arۧꖣ+ulseP-2s꒗z??1md-li< xp`famТAdؾfuturHb(`I9iru)< I.ξ),ol'ЁLJlDpyrpub@,Xp\8lik`:h½hH % co.txt0@A/,wtؔBnXb鸉knhowhdtypz.ƴxa!foui8aa@hautoPss3H .ԓ&L <(pdbٳ9instrnqrndheldWd3О¿Cha2Mɹ0bui٣iJBsa`m)Bc*@У52PȚah$hƜ韎benhَ|r{ܪzѩ#_\;la[~ ɝytwee߻alyzx@ԕbeлȧTnhhehain. Whenouunyriteublisher,triesoonstructfluginhatanuccessfullyver inputileiaatabase.ԄEsuppliedithreesignbterxngeabsoagetifferentesultimpbysingmYchoos0uxކD-P@mmand-lineption.willttempЂs=Qname,IXgtQ wefaappropri (unl,qu@pbina1s ossi). doote;specifyBwho, rd oesn'tr.orxaXhWagged-texars(creqaTXT`0af`regularoc,#ouldafollow: H %yrpub -PԃT,foo.txt`@Eachlyavpow0嘕trolBbeioЖyhelp "(viewʖh+)howavail*-f0a<(`ĉh蜵*H5l@xG3.1-BookÝBsqCsIXoc0ؕBroўn-bџ51divid؝oxur tedoˎceAvo (HURLStmrPDBIy@) Г8Ϥ0:`fk*qndamarkRalyz up8tc.yRawA@,WјHHTML+AywӨra-#icPuhBasicȃeallRichRSW˜ OutQ Th8鬁oXmixcumx;oa졨d@uPc".ng\3"sرMy`givooi𙺠)ߗ2dvancpPMge:rotocol(Preݳly,-khԗrz(!I`E)mDa\woalkaȽQB-assemHcribHa7lik@ljZae?9xŴyknY`e42?R1wh$RicBtypGD0n3QdHUPQon exceӺPbenYi9firT󼨿pHrometzdsH?;ap0so loIP``w'':ctuIMIMEwpifteȘ칻ѽahtml (tarde*L)PIlwouldo ش1<1.i1PqYMUݳب(H.pdbe.prڜ<Jiv0xoW. 2g)tҳI,[И𗺛OI/1,BȮ/octet-s=Ietn. 3k翬eemsKa(oddYb[LsDѨwwU. 4se8nŪǪǪ;;efa஡wyWľmH5CtҊ'ׯlQObItwn* endhȺSQjm ihfɍM'w9leathre9=plxt//GBimeamanЯ|aybui@aDjGrÿki.owhZX.Xyrio yystem+leeap'!owoodtsandlingfarticularrotocols. Forxample,mothetardetplugins@ainextilesanesedyHT@,PRawoTaggedXHTML@.fosQmostenerallysefulhoice_oec0aigheiority`B /bɈtorIfouamnrre~ocomma0Pypeublis wilXchhatnclud0mrio Xolvnybigu܂ stexist.مPyadju9_9individualus#nfrati,`scribelowxP@d4Á0QOps EachOM3haAown-bl s,lBpe8es@.ԋǓxpyrpub -t-OdisЊx|ǘAhupONreadɉge5.L(rdirectory_8twoypHtements:pPhiH) 8yρσrXVՈQ%ooks8ke0is:Ӎ(J=value@;0semiPearequd.trBson'te0quotunynon-alpumeric!ract) booleP녢houl!i`1''i``0''0iW8ree!mǕȌinpuRboutberwwwwׁҁDŽ76.fir1٫1'GrspecifhpairSVs;鯡Pbin#ini@eChnoff3sedӄctnYossirxIh`LJĂ'†u aԆmbS. |)TealDocBtimvAfolȯ!/drcmptbelp H ``doc-asXr`100@ɱ5aP۰ҭp`)ropwrming,СIB`ztxbrokenHUwi@ed8_RF C,0ransHundcodaHs8_irxpY#D--IG5.1МPvθ؎YGD@DJ applic/ot-stmRawTex0IME Krbirypta+ t`/htmlHTMLǁƀwt/plH=,-1rTagged HEADERONT=2LIGN=LEFTEXT=""> Robillotson rob@p.org1Contents"TYLE=UNLINp 1ntroducti 2sing׆ 3luginsndhaX .1-Book hvers` Pdvanced,anagem:1tocolPrioies4figuraOpi5thar5 @RLStream -(putromiler tDB#gbDocYzTXTatabase4awTexPYpars8+5 2߆6agged j7TML:8asic-?e-bAassembl^9XalcT-enh!0ichReader)n߃1OutLto-format2Cف)3 ??sUtcolfpyQoЊu lnoPY65D9opefere7ǜx8ow-MleǁAboAhisocu ...GA!\aimpؕJcusbysPalmm0lat!.lthoughre!ewroprietarsth ,OI0famostidelyupportwayastolopoip,th殺dozen9edi8itIn@`(cލ8jucre,@0esignfficiRnavig|pxtop.ow@r,anyewH݈om+f "rZ"rkup,subset9irw`niquरschsWhyaHaug`=canq rhҋtra(al*-onxhli,fܑmeathyyB{medoavAxMXgr8(whx,8 s vailaX9OSxceptindows)cXs(.ɂlsoϞeaXmighЀJ@񄚸iftdepend9@wsQϢHGDۈdddюԝms (ɷ)َianworkSpgm /,seHx„rtiش8Ahspecilangu.u8riIs9{(usuڼ Piimr:0eHyKHn"B:Rfҿticu`?;;UoaA㮍arۧꖣ+ulse߳߳߳2sWR(mxd-lipP (p0hHfaHmdyfuturbмiihcruّ샜 .ξ,'JlPpyrpub,Gx캀iHGlik: %$o.txt7^wAtǼrn8tbknowd%typъ6.ƴxaQfouihaӃap!autoscx.ԓV|OP9 IHoHcroaAT(gbn-bAdividHourteٞd0o;etriev (URLSthmDBI)Ph@:pfndqAmarkalyzuptc. (Raw 3'HTMLAPyPrPa-icu (BasichalJ9RichRqW늮(0Out9q) Th(U YQo(mihcuma`ho젘Bdu c.nga3sȰxyߐ×given_Hi*2dvancPʗGF="Mge1:rotocolndg"PrheIɔly,OMXk"ȭ[A Eymda4ZPwoalkќyaxWSioc-assemX]cribag like*:ae? Eykn܇U2?whic"typ'$yn#1hd(ЅLݽɼ5вonexcecq0ben{firxHprometʭs(ߓ۩XaPsoloI``wr'':ctu MIME ifte(!qhtml (]:tarde)Ilwouldo ط11.iQ0Qڙٔd-,L2T=.pdbq.pr@ӷW-iv`oZ. 2t, nڛo /B/octet-sLye1En@L. 3KndPeemsaodbbQ#q. 4zsey5;+efa0Basic؞[T0ń׫aba5CdۆЄ? O21nvendxHSYAصm`iɌMǩؿɳWfleahthreهVplÁ3g`ɸmeaڞeman0aybuia`r#ki.HowhXZ-D!ɂbrioyystemkleeapgai ²dols. Forxample,monghetandardetflugins@ainextilesanexledyT,awKTaggedTML.fosmostenerallysefulhoicesoteclar0aigherriorityIY/protoctp2oXr1Ifouamnr@reocommlinpPyPeubliswilR`chZhatncludpmeio 8olvny@bigu܂ stexist.مPadju`y_iindividualusiconfrati),@scribelow Each#haPitwnblЂc,lpera.ԌWyrpub -t-/[disx8=0Aartupread׉Մ.<ؒ8ro@directoryȋρ茀twoyptements:hi蜹a_y/YrȈGooksHk: bK. =alue ; semiexrequd.trson'te8quotunHyvnon-alpumericqracty booleϢsshoulqi!`1''``0'')Wree1mΗ4inpuoutbx;ׁׁׁW̋firq'r@specifpairkH#>s;Ѧbinini0enoffsePdCGctViossiŠB,ȵLJĂ'2u|1mbط*. 쮙TealDocBima&fol8drcmtelpPH .}``doc-asrP100///)5takrً݈Rskto mprwithoyddialcessi0whatsoe5 t+WWU?=re-flow*moreadaiandheldocumenthener8y0ZnsistfoinPagraphHn)so@y0llw`pbSXoUPHd,cutomcڍ7 dagularxprQ(searchĒ/Qfolʖ: 5_xs Ro0r.͟specifimultip(timonfigurfi@ 4 ʋnh. (C% -r) inded_o (lean)ruchec@道determpbe@@`nstБlىQblanketm--)wwws6aggedjon="77Gdo xsamXorth򔄧O@bЛherp9upgs edd@| P,an8rshlikTɑriioktasiثXduce-rom ѐ@9esbi8requiru@TguagTMLsupp ea[TITLEH Sf. BOOKMARKbel:a BM͎. H In ;`n''!aigX"1I6\yT8ANNOTATIONṖîh, /9. PREŪ 󂟩 /pٺ@bme)wqRhorizoluy Byfa00ؗmusس[mselvIixa Hiod߰߰͡c_d_ٰװף׏(@0erѵm-A. _*shi۾(Qa`i({) AϿσ(9argZ.t tJwohdroupŁ,9Ptiy.tQ-subPtu2v@abPkiXrecEeplu[|y(OOOI7:WV#sinpcreatnn7FhQiu disy ptdheldHgn@`=imppjڼɆ.ȐQIEfootHshckprg)ѺtncXϣ,e_''rO5assemblerTnplugcreatesဈain-ك,gnoringXyRupBprevious-as for''hasfollowproperties: footnote_br_Hmatr[ف-rt.hiyhouldontЇsXle %0hich&rec0byDdnumbe???99eal3T-enhan??ǐǐ"ȎwWwoly$with viewa0ͅapplication. (result!documentanaa (maler,1uvisiags Xedd)3/////////////////-hN_style:Rh0s;Όaigitrom 1o 6pecifyBevel.T(ss1onHroreords,epar@sph+mayclude:`lA'' ``bold`0$(siz``un`Ёinvdeco``cPerMrighǃRjustifmWWWS10ҞR -www"WWW߮߬WW7Wgg‡``garbazchcw瞿''shZ"//;g`0ita`fixehcdotآO;lef頜4~g{N sesYseAcommqpnlQnfiguile1Os- L(ao"-databaseWOO-e,s"anyRpG77 titlƀCϻp*-t) bac8 (lean)fruh᦮-b) hegorym +:˼ؑusefulfounowSfamomxHr3.)gg`c`ior[cDRefah!``REAyC) typIG컨HggaT) _direc@y DU1 todY Fu(sB)-TotduunwapdԲxsaȱvpJnҺ洙PtimWWRopoooj2TXTGGGG)OON'$/|(currxpsuppor1We0lxvidi!jaer)pHsIrѫ0TXTluginasheollowingroperties: title Tdocument . (Commandineption -t) output_directory DUoacoatabasesntodfilename Full Q (includك)fk>.otea@hisi8duunwantedesultfoureonvmonЈƂtimo) rom_access (boolean)Ʌruً_ compxsp`` ''hdehichvidXbetterfficiency Aa sarg}oad,!icoshfew pcɊu`. efaXQfor zІ,opecify!ia\7sturn ff. tX 1 zs _^ךsendsŗ' .,stؕa,facilita[!1e- kack 9_________________O_dump_annowv, Ucor0()famark_l Fppinfo}trareç lo///"llW׊#,Wpg' 4opyDocQr؝www"kmYoWWעע"aher'Ϲ//="ReferHe"sདllrDŽGǴ7rotocols ?8ow-L!ͪle?g??????Ab9...@PyrihPublis@i7t(generdLaTeX2HTMLansoǗright  1993,1425b67,ikosra8,Xm`@BdearnUni0tyLeed4z8,ҕ0,aXpicxetacquariey,ydneylic&Ѣyn39beeeaviQaildyƀAL.ʷ@Jr.ωinaavigSihؗ𻹲b ChropPexlliRULPUpϼgeUp: 95rojpPyrite Publishe?Contents1 Introduction2 Using Pyrite 3 Plugins and C 3.1 E-Book Conv3.2 Advanced Pl4 Configuration"5 Standard Plug'5.1 Protocols a)5.2 URLStream -,5.3 PDBInput - .Y5.4 RawText - R15.5 Text - Text25.6 TaggedText 5v5.7 HTML - HTML<5.8 BasicDoc - @5.9 TealDoc - TB`5.10 RichReaderE5.11 DocOutput Je5.12 zTXT - OutN5.13 TextOutputR5.14 CopyDoc - Vl6 Plugin DeveloX7 Protocols RefYE8 Low-Level ModYAbout this docuZ,pyrite-publisher-2.1.1/doc/pyrite-publisher-ztxt.pdb0100644000175000000620000002106607412012055021240 0ustar robstaffPyrite Publisher(|%|%|%zTXTGPlmx@BB@BB @BB@BB@BB(T xڜYm_A(|ډR'N`[]rKrOVo!Eҹ@ ֝33ϼ܇^zmLnFnmkCcj3~L;Uwm] [3w/aY=U7]hqz>U]P/ǕRbTٹ{Zx^V+M7;/J׊[oDv7xM=}o>CGeޫ5@臨jJڶF9OaXg9sr+wSzIS|;"}SN?\k_~zɬ'oߠnl6]#{ӿwl]OF?O CHӇu^S@>W] C=n%"ʵU('qC&[*MR;%UQH ?fxl[G46ʕel6II7*}sS_m6^ 0 SC#ۨmlܰk7J6{{kLy)AL ͂/]`+U5H`\TO*IЬrn4hTbBN8o:dPBdIA?|YoBH Zq~Rmie;ȏU % zX]Q"jVd*xQZ}呂WPƒs6Hǘ+$W_t?b4C3<ÎFe]Jݩ-Y 5@ye֮;WZO I1bFAp9 E=P1Uxha=T }AWWJ?hj0{`~^Oh5I/p4 dRBaܩ֜qĬ2*HzHmxsV+PQ"7yf}PW_X^$Z~Uᭁ98/h+B ) wYɋvtȓʞmQ97EveZdF֤qqʗ{h`Gnc1Eg0 ))u d{  dMzJ3 pp)AI3(іdz% Fp`Δ\*Ş3tHeM =^ -=ȪZ39P'a;8yyKQQDZ!zGq\xȕ`13)4ғ$r٨pZqic ZݠvSO딀=ʪ!:3 y׹E('X: xj҂T+99Cѯ<VƋ#r^:$JPr(|j%TsF`9׹sE>t'`7R7|ș$s [*cyX9D< 40! US33s]{ӈPf;55n=vK%xIMrbyNS7sFϏBhR8(P{@|N'Rtg9|zQybgO3\-9wyJ(|`I $lՀJa*ٵ]RB 1K3IkJނ@jك] qg 'ԵZemLS] ]zA [Bۭ%e; (r.Q@Jrh0ljSɫ ݓ$]-qfiY{=g2 *HW\׶%FUc(T(Oz3 $9#o{9p =IBUh/8?m2BdP&2b(ΆLTSSH,oѿ;p-\z!-gܝ43[k3B/V&0De3Fp◇L-[kܹE,.@'m&'rbQWiTSİӣ/KD?FF4BDE^iC?wq5qjuG )MQ/VUݤ}ĸl>+z=11yJzFnALU "go6o tTa̙yC#2E &ݩԐi<:%{*T3t{OΥOkSlWq7=ZWz>pg̍"=[iz7L?Tb">'jj%QS:uZ=ୌ<5:Ynw fH"Dlrs(4>GJi+ ^^kO }/Ņ%xw. O~(cܛ7gfh?IU&[uKG^״ƒ< =2=ݨiɮԙT)8RMnݓ lz_QfiC@탎iTDǬ8Q'([4ϞIG"Lj73U~do\M\$*VroLwK>y5Kf) mIsUhonEG Y}Z&jbu%r={\O\m Bt[ͩfc{>FD r6o+\RiI̐ w`}OT0Q0/(R=Ig&1!ФoR նKyuz`N,[LʹcD;jef۶EH[,2iɜ;Lax./yG=Wk|NBAS#j€YpJzRӔ?m3ҤNu3i3%,0t;EZ5vp &r6 -ɵPW\Αctj_YMܶWT53IAx˖wk5JN+K4h?rOn|Ȳ/T"@~6Aŝij5zos y0x{P-vHv:m(Rrtt"ɜre>zЍak}u:ZשmBԪ4ۯ :?CFW;us>nag|Ć4;L$FecI nh'Wj6-iJ=Q7`)zM: ,1X/ @/FBq֭{pW;hb.oqNC/1Ѵkg; p>q H7*f ƹ r†߼BJ7EAq k($ ,Cgc;=ϟ`SԻ)XBPEl/yp?1+0չn~5Vih<ΫҹylXvnh(bG?=zgΑ7FmoqmvPnGav />SV^X^d )(LCo*`^8qF5n\ui;I\KE[H) BAMBJ'f]ymL$Q\̛p UNK\ j:n0fhVjCUτ6y*GmAd۞o\ԟ2+sG_ Ev4=!يѵUKP}p#?{V_H7։'N|pYS;tܣBHF$j" 0;!Fx!5(@f̣һ{%ǔ^\lfrN.. 5 UB] {jCZÓDcxȦJn|YBS"Kznrq݌l,ٗ#7gs;c"'_fr~Q9l/V SZuk!g7܄h|i?LOwmԷߟᇳ_sۭ9~IQ^W:؊Dܢݢ59 LYښΠ:2aŵ[> 6<Ó8DgK29&vI$*8cb;Jh22w#k3ʢ?o'q\+fwGΨian4˜c  xsp>0mht6z)g޴hJw*YT.ƞ?V8˓i;$wH-mC̖.O]W|Т<)Ҁ®,ҰD8niKCMG :k!%z2*u3w:P\LF`yJBzZt9 B fi M}]cvJi<3d~<9` vcWŶ=84P@F>كVjÄ@C jjK(-"%΁;j1,ʧ-8TAV_ *mKMF䢫 z"~@V7i'(4=%dMtߓJ_$^Ixi@b+Ё9tO#-1ZBjaR '>Tɷ_N8rb<#C:0~Sjy;(xʑ#^IʟK\QB0bX{)DSo%'ugLźYP!{;4AXѾڥlHA&ąL6\"_ƋYaе];4b$:(_Nq`w+ #?),%y7xNOjv¤LvqyQN$r2MM+HX>猡 1մ2zM^NP|u4zg|0[nx)3[re8eFB04Zڢޢ)`[\b:fyy;o,Kt7ikD$ ) A=%Rv=T&M^A:̲c/ | [%n2mp~=ELފ<c@y8*o:gʐ*o}❉Zxu}ۗAii^+^/(ׯoCj.μ9|m6M Rbڤ>~^\_]f,rğ~'oH-% w3x*nnd)$RLYz 'ZB*aO;&XivcުQ~pp2 -Z~텞 4q ijAaLgM<K=gt~ , B3$thjy)z9 *//5eEqRW\|^_(w(ZmzLg1*${A(}rNeKɖa(tq`<+IgmV<#5Fn Y_6ٍ_ "䨂p_)|HWYgS gm7Lb"MqAm^|d'Um殴q.iIA#!o0J:I% ḱ _$J6wbE񋌮gɨKyi ;kKWrǩ9bieΒL=dfI-7rW(.8mj;v_PJG -Yߊd}mL7z:]wJx\9εeKns!%#Ww]aNu|F''|> >\@3TIr>ŭ.4u]濖ѾHg@ôeVs_{fq&gv \lHx/v # (ޱʑ}ii67zQ7]x~ ǧBSҹ P!`[ٮ[mHpjܴב/2"R^H:R]gF>z9;NfV1g e; uf*PʗJS(.ѹzQ81q{|i ObaG`GD(];>;.cv754*@ PJ{,@O($u^3J>c1-=73]XTORML8Y|I9+S3NgeږͲdǚadŽ>6%&j\Ů2#kUBbD>nZw.oJ$g;rG7xqO*;yE%,(: *.RUTAe{\JLVZQ9xC墠A*G0'I_rlzpa>rR-r2fKa8YﯜMp8kEr]*^}ilbvg$Cx(Хÿ.,Tj#!.B'TV *A*bpqF콕MwaStjivr]m w5%8iQ4ŕy7idD *Ļ*g>cpc6n gsҴ^kO'}it`%i͞-kp/t ͫcNvf C*M wK^>#|8~,i_K$=W܎Qy˖ZMOHLjuV s%R)tTEJ]9G>'8_oCmq-dv/ów2Lho!Wdܯ~ o*5z04Ncj T>O/vT: 7hrvt;o'2mx7QPyrite PublisherContentsT1 Introduction 2 Using Pyrite Publ ;3 Plugins and Chain 3.1 E-Book Conversi3.2 Advanced Plugin 4 Configuration and&R5 Standard Plugins'5.1 Protocols and P*35.2 URLStream - Inp,5.3 PDBInput - Inpu.Z5.4 RawText - Raw t/95.5 Text - Text par2d5.6 TaggedText - Ta9 5.7 HTML - HTML par<5.8 BasicDoc - Stan>T5.9 TealDoc - TealDAs5.10 RichReader - RE_5.11 DocOutput - OuIH5.12 zTXT - Output L5.13 TextOutput - OP:5.14 CopyDoc - CopyQ6 Plugin DevelopersR:7 Protocols ReferenRw8 Low-Level ModulesRAbout this documentpyrite-publisher-2.1.1/doc/pyrite-publisher.pdb0100644000175000000620000002621607412012053020231 0ustar robstaffPyrite Publisher|%|%|%TEXtREAd"`@op@o @o@ow@o @o'r@o*r@o*@o*@o *@o *@o *@o *@o *@o+@o+&@o+:@o+N@o+b@o+v@o+@o+@o+@o+@o+@o+@o,@o,@o,*@o,>@o,R@o,f@o ,z@o!TUpneevelyriteublisher Up:hroject ***3g '$Robillotson rob@pB.org Contents  1ntroductiX 2singDŽ 3luginsndhaX .1-Book vers` 2dvanced,anagem@:!tocolPrioie 4figuraOpi5thar5 @RLStream -(putromiler tDB#gbDocYzTXTatabase4awTexPYpars(+5 2߆6agged j7TML:8asic-?e-bAassembl^9XalcT-enh!0ichReader)n߃1OutLto-format2Cف)3 ??sUtxcolfpyQou lnoPY65D)opefere7ǜx8ow-MleǁAboAhisocu ... GBيLxaimpȔ:SusbysxPalmm lat.lthough re!ewroprietarst ,҅OI0famostidelyupportwayQstolo`хoip,th鸞dozen)edi8itxIn@`(c{Ό(jucre ,0 esignfficiBnavig|pxtop.ow r,anyew8݈omf "rJ"rkup,subset9irw`niqu࣠schsWhia8augP=canq rh‹tra(al-onxhli,fܑmeathyy2{medoavAh=Xgr((whh,8 s pvailaH9OSxceptindows)cH8sВ.ɂlsoϝeaXmighЀJ@񄚷yiftdepend)0wsQϠH74ۿdqxddюԝms (ɵ)َianworkSpgm/,se8x„rtiȳ(Ahspecilangu.u8迂iIs9{(usuڻq @iimr:0eHy;8n"B:RfҾticu`?;;ЅUoaA㮍arۧꖣ+ulse2sWRߐmhd-liPܱ@ p X8fa8mpdYfuturb IiXSruɐ܃ .μ, 'pJl@pyrpub,ش7Pܹpi(7lik: %$o.txt'Nw1tbn(xtb knowdtyp6.ƳxaٴfouiXߠaÃa `autopsSh.ԒFl?@)%E dnumbe5.9ealT-enXWWOL9䙂΢viewBlŸlic (s̭œXiler, Rvisi!eRr);WWW e %shichilleeplacedyheootnoteumber. hN_style TRfeadings;sigitrom 1o 6pecify0evel.Tconsistsonroreords,eparatsp@maynclude:`large'' ``bold`0$ntize,`underlininvertdecoion,nd@centerMrighǃRjustific. 5.10ҋRer -or-enhane-bookssemblerjluginrepyDocwityrkupatviewaHppl (resultAdocumanadaormaler,utܒvisiasgarbachPcs eddtext.) hHllowqpropies: _er_fItj[q+soutp1 hHphould tasx '' sXZrݚ;AitaPfixehdot(; lefP94ΟgkNHseGsse4icommipnlnfiguilec1O- Lx-databas// ,sanyhp`'77 titCXp -t) bac (Plean)fruHqb) egoryJm< Џq:usefulfounow3fampomXxr.)gg`c`Io;dDRefaH``REA yC) typI'TEXxggaT) _direc@y DU᧊o}todqY Fu([")-4OtҰduunwadXsahIQ*pnƱy0timWWRoP2TXT'"//c/,T5.10 RichReaderAs5.11 DocOutput E_5.12 zTXT - OutIH5.13 TextOutputL5.14 CopyDoc - P:6 Plugin DeveloQ7 Protocols RefR:8 Low-Level ModRwAbout this docuRpyrite-publisher-2.1.1/doc/pyrpub.10100644000175000000620000000265407411755400015647 0ustar robstaff.TH PYRPUB 1 "29 Mar 2001" "The Pyrite Project" "Pyrite Publisher" .SH NAME pyrpub \- convert content to Palm-compatible format .SH SYNOPSIS .B dtk [ .BI -P " plugin-names" ] [ .B -h | --help ] [ .B -l | --list-plugins ] [ .I OPTIONS ... ] .I FILES ... .SH DESCRIPTION This manual pages documents .B pyrpub, the command-line user interface to Pyrite Publisher. Pyrite Publisher is a modular system for converting various kinds of content into forms which are usable on Palm Computing(R) platform handhelds. (Currently only textual document conversion is supported, targeting the Doc format which is the de facto standard for large text documents on the Palm platform.) .SH OPTIONS The options described in this section are part of the Pyrite Publisher core, and are always available. There may be additional options available, depending on what plugins are installed .SS BASIC OPTIONS .TP .B \-h, \-\-help Show a summary of options, including those that apply to all installed plugins. .TP .B \-l, \-\-list-plugins List all installed plugins. .TP .B \-P NAME[,NAME...] Request that a particular plugin be used, if it is possible to do so. (If it is not possible to convert the input using a sequence of plugins that includes the ones specified in this option, an error will be reported.) .TP .B \-\-list-properties List the names of the properties of all installed plugins. .SH AUTHOR Pyrite Publisher was written by Rob Tillotson . pyrite-publisher-2.1.1/ChangeLog0100644000175000000620000003545107514637157015266 0ustar robstaff2002-07-15 Rob Tillotson * Release 2.1.1 * PyritePublisher/plugin_pdbinput.py: Add import of 're'. 2002-04-01 Rob Tillotson * PyritePublisher/dtkmain.py (Chain.close): Support protocol specs in path. (Chain.open): Same. * PyritePublisher/plugin_ztxtoutput.py (Plugin.open): Add protocol argument. * PyritePublisher/plugin_textoutput.py (Plugin.open): Add protocol argument. * PyritePublisher/plugin_pdboutput.py (Plugin.open): Add protocol argument. * PyritePublisher/plugin_docoutput.py (Plugin.open): Add protocol argument. * PyritePublisher/dtkplugins.py (DTKPlugin.open): Add third argument for protocol. * PyritePublisher/dtkmain.py (Chain.pathfind): Add protocol specifications to path returns. (PPInstance.find_paths): Support protocol specs in paths. 2002-03-27 Rob Tillotson * Release 2.1.0 * PyritePublisher/config.py (PPConfigProcessor.cmd_inputfilter): Added inputfilter command support. * PyritePublisher/dtkmain.py (PPInstance.__init__): Pass instance to config processor. * PyritePublisher/plugin_URLStream.py: Moved URLStream plugin to its own file. (Plugin): Added support for external filters which convert one mime type to another. * PyritePublisher/dtkplugins.py (load_all_plugins): Check for plugin usability. 2002-03-26 Rob Tillotson * PyritePublisher/plugin_ztxtoutput.py: Use PDBOutput instead of native. * PyritePublisher/ztxt.py (zTXTWriteStream.close): Allow passing of prc.File object instead of filename. * PyritePublisher/plugin_docoutput.py: Use PDBOutput instead of native. * PyritePublisher/plugin_pdboutput.py (Plugin.close): Added install support. * PyritePublisher/doc_database.py (DocWriteStream.close): Allow passing of an already opened prc.File object instead of a filename. * PyritePublisher/plugin_pdboutput.py (Plugin.close): Added unique name generation. * PyritePublisher/plugin_docoutput.py: Moved DocOutput plugin to separate file. * PyritePublisher/dtkmain.py (Chain.pathfind): Rewrote using a graph-traversal algorithm to allow for cycles and such. * PyritePublisher/dbprotocol.py: Added. * PyritePublisher/plugin_jfile.py: Added. * PyritePublisher/plugin_dbfieldguess.py: Added. 2002-02-05 Rob Tillotson * PyritePublisher/dtkplugins.py (load_all_plugins): Only try to load plugins if __file__ is a .py. (Modules inside a py2exe compressed archive have a __file__, but it is something like "". (load_all_plugins): Fully qualify names from the plugin_modules metadata. * setup.py: Add support for py2exe on win32. * PyritePublisher/pyrpub: Exit after running GUI. * PyritePublisher/pyrpub: Try importing gui_wxwin from PyritePublisher. * PyritePublisher/gui_wxwin.py (PPMainFrame.__init__): Add task picker. (PPMainFrame.init_advanced_options): Comment out plugin pickers. (PPMainFrame.do_options): Handle task selection. (PPMainFrame.do_options): Fix return of forced plugins. * PyritePublisher/plugin_pdbinput.py (Plugin.open_input): Win32 filename fix. * PyritePublisher/dtkplugins.py (URLStreamInputPlugin.open_input): Win32 filename fix. 2002-01-09 Rob Tillotson * PyritePublisher/plugin_RichReader.py (Plugin.__init__): Register task. * PyritePublisher/plugin_TealDoc.py (Plugin.__init__): Register task. * PyritePublisher/dtkplugins.py (DocDBOutputPlugin.__init__): Removed -C and -T short options. (load_all_plugins): Get list of plugins from generated metadata if necessary. * PyritePublisher/plugin_ztxtoutput.py (Plugin.__init__): Added task definitions. * PyritePublisher/dtkmain.py (TaskDefinition): Added. (PPInstance.register_task): Added. (PPCLI.cmd_list_tasks): Added. 2002-01-07 Rob Tillotson * PyritePublisher/dtkplugins.py (DTKPlugin.has_feature): Added. 2002-01-01 Rob Tillotson * PyritePublisher/gui_wxwin.py: Added. * PyritePublisher/dtkmain.py (PPInstance.find_plugins_with_link): Added. (PPInstance.set_property_all): Added. * PyritePublisher/plugin_textoutput.py (Plugin.__init__): Fixed call to superclass. * PyritePublisher/plugin_TaggedText.py (Plugin.__init__): Fixed call to superclass. * PyritePublisher/plugin_Text.py (Plugin.__init__): Fixed call to superclass. * PyritePublisher/plugin_frotz.py (Plugin.__init__): Register wildcards. * PyritePublisher/plugin_pdbinput.py (Plugin.__init__): Register wildcards. * PyritePublisher/plugin_HTML.py (Plugin.__init__): Register wildcards. * PyritePublisher/dtkmain.py (PPInstance.register_wildcard): Added. 2001-12-29 Rob Tillotson * PyritePublisher/dtkmain.py (PPInstance.find_installer): Added. * PyritePublisher/dtkplugins.py (load_all_plugins): Add API call support. (Also throughout the rest of this file and other plugins.) * PyritePublisher/plugin.py (CallableLoader.load_plugins): Fixed a stupid error display bug. * PyritePublisher/plugin_basicdoc.py: Moved code from dtkplugins.py. * PyritePublisher/dtkplugins.py (DocDBOutputPlugin): Made installable. (InstallerPlugin): Added. * PyritePublisher/plugin_ztxtoutput.py (Plugin): Made installable. * PyritePublisher/dtkplugins.py (OutputPlugin.__init__): Added direct install support. * PyritePublisher/prc_install.py: Added. * PyritePublisher/dtkmain.py (PPInstance.has_plugin): Added. (PPInstance.get_plugin): Added. (PPCLI.cmd_list_plugins): Distinguish between filter plugins [which return a list of supported links] and others. 2001-12-27 Rob Tillotson * PyritePublisher/prc.py (PCache.updateDBInfo): Added. * PyritePublisher/dtkplugins.py (URLStreamInputPlugin.open_input): Guess mimetypes from some URLs, and whenever text/plain or application/octet-stream are otherwise chosen. * PyritePublisher/plugin_pdboutput.py: Added. * PyritePublisher/dtkmain.py (PPInstance): More refactoring. 2001-12-25 Rob Tillotson * PyritePublisher/dtkplugins.py (DTKCallback): Removed, since the PPInstance is now its own callback. * PyritePublisher/dtkmain.py (PPInstance): Added as part of main program refactoring. (PPCLI): Same. 2001-12-24 Rob Tillotson * Release 2.0.0 * PyritePublisher/plugin_TealDoc.py (Plugin.__init__): Added heading style properties. * PyritePublisher/plugin_RichReader.py (RichReaderDocWriter.send_heading): Fix restoration of justify after a centered header. (Plugin.__init__): Added heading style properties. * PyritePublisher/plugin_HTML.py (DocHTMLParser.handle_data): Added capability for capturing text without swallowing it (like save_bgn/save_end). * PyritePublisher/dtkplugins.py (load_all_plugins): Fixed mismatch between builtin plugin names and keys in plugin dictionary. (RawParserPlugin): Renamed to 'RawText'. (DocDBOutputPlugin): Renamed to 'DocOutput'. * PyritePublisher/plugin_textoutput.py (FakeDocStream.close): Added doc-only-output link for debugging. * PyritePublisher/dtkplugins.py (BasicDocWriter.mark_footnote): Added. (BasicAssemblerPlugin.__init__): Added footnote_marker_format property/option. * PyritePublisher/plugin_textoutput.py (FakeDocStream.close): Don't close output stream if it is stdout or stderr. * PyritePublisher/plugin_HTML.py (Plugin.open): Use copyProperties to transfer preferences. (Plugin.__init__): Change no_link_footnotes to footnote_links, and no_anchor_bookmarks to bookmark_anchors (DocHTMLParser.*): See above. * PyritePublisher/plugin_TaggedText.py (Plugin.feed): Add bookmark regexp searching. (Plugin.__init__): Same. 2001-12-23 Rob Tillotson * PyritePublisher/plugin_textoutput.py (Plugin): Added text-output link for use by future doc-dumper plugins. 2001-12-20 Rob Tillotson * PyritePublisher/dtkmain.py (main): Added loading of .pyrpubrc. (main): Added list_properties option/code. * PyritePublisher/config.py: Added. * PyritePublisher/dtkplugins.py (DTKPlugin.set_priority): Added (for config support). * PyritePublisher/plugin.py (PropertyMixin.has_property): Added. * PyritePublisher/dtkmain.py (main): Allow multiple plugins to be specified with multiple -P options, in addition to by using commas. * PyritePublisher/plugin_debugoutput.py (FakeDocStream): Allow specification of file to write to. (Plugin.__init__): Added output_filename and various dump_* options. * PyritePublisher/plugin.py (PropertyMixin.copyProperties): Added. * PyritePublisher/plugin_ztxtoutput.py (Plugin.open): Added output filename option. * PyritePublisher/dtkplugins.py (DocDBOutputPlugin.__init__): Added output filename option. (DocDBOutputPlugin.open): Same. * PyritePublisher/doc_database.py (DocWriteStream.has_feature): Added. * PyritePublisher/plugin_copydoc.py: Added (first DOC:raw plugin). * PyritePublisher/plugin_pdbinput.py (Plugin.open_input): Added zTXT support. (Plugin.go): Same. (Plugin.open_doc): Return text/plain mimetype instead of application/octet-stream. (Plugin.go): Added DOC:raw protocol support. * PyritePublisher/ztxt.py (zTXTReadStream.__getattr__): Added. * PyritePublisher/doc_database.py (bookmark_unpack): Was stupidly broken. * PyritePublisher/ztxt.py (zTXTReadStream.get_annotations): Added. (zTXTReadStream.get_bookmarks): Added. * PyritePublisher/plugin_Text.py (Plugin.feed): Add regexp matching for bookmarks. (Plugin.__init__): Same. * PyritePublisher/doc_database.py (bookmark_pack): Null-terminate bookmark titles. * PyritePublisher/ztxt.py (zTXTWriteStream.close): Null-terminate bookmark and annotation titles. * PyritePublisher/plugin_HTML.py (Plugin.__init__): Added bookmark_headers. (DocHTMLParser.end_h1 et. al.): Same. * PyritePublisher/doc_database.py (DocReadStream.get_bookmarks): Added. 2001-12-19 Rob Tillotson * PyritePublisher/doc_database.py (DocReadStream.__getattr__): Added. * Release 1.99.3 * PyritePublisher/plugin.py (CLIOptionParser.__call__): Support multiple plugins with the same commandline arguments. * PyritePublisher/dtkplugins.py (DocDBOutputPlugin.open): Added output directory option. * PyritePublisher/plugin_ztxtoutput.py (Plugin.open): Added output directory option. * PyritePublisher/plugin_TaggedText.py (TagAPI): Added text capture support for multiline tags. (Plugin.feed): Same. (TagAPI.process): Fixed case folding to actually work. (TagAPI.process): Added support for end-tag methods. (TagAPI.begin_ANNOTATION): Added annotation support. 2001-12-18 Rob Tillotson * PyritePublisher/dtkplugins.py (load_all_plugins): Fixed path problem which prevented plugins from loading the pyrpub script is located in a different directory. (load_all_plugins): Construct list of plugins dynamically. * PyritePublisher/plugin_HTML.py (DocHTMLParser): Swallow and ignore