ChangeLog
=========

HEAD (2010-08-16)
------------------

  * *README.txt*: Adds Till, Hin-Tak, Flávio, Diógenes and OSPO as
    Contributors. 

  * *README.txt*: Adds # and $ to indicate commands done in the shell. 

  * *README.txt*: Adding multiple archives usage's instructions. 

v0.4.4 (2010-08-11)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.4.4 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/ppd.py*: 1. The PPD parser confused
    "NickName" and "ShortNickName". This leads to the   "ShortNickName" being
    used in "pyppd-ppdfile list" and so important information   is missing in the
    listings and the listings are 2. "pyppd-ppdfile list" lists   PPD URIs with
    the full path of pyppd-ppdfile, usually   /usr/lib/cups/driver/pyppd-ppdfile.
    This makes the PPD generator not working   correctly with CUPS. 

v0.4.3 (2010-08-10)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.4.3 

  * *pyppd/ppd.py*, *pyppd/archiver.py*: Renaming filename -> uri. 

  * *pyppd/archiver.py*: So we avoid the problem of having PPDs with the same
    filename. It's working as:   if I call pyppd with the folder /usr/share/ppd,
    it removes this part of each   PPD path and uses the rest as the URI. For
    example,   /usr/share/ppd/cups-included/textonly.ppd.gz will have the URI  
    "cups-included/textonly.ppd". 

v0.4.2 (2010-08-09)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.4.2 

  * *pyppd/archiver.py*: Oops! Forgot to change the variable name. 

  * *setup.py*: Adds classifiers to setup.py. 

  * *ISSUES.txt*: Removing resolved issue. 

  * *pyppd/runner.py*: I don't think logging to a file is worthwile to pyppd,
    as it isn't a daemon. If   you encounter some problem, you'll be running it
    yourself, Also, I prefer using   -v and -vv instead of -v and -d. I think it
    makes things more standard. 

  * *pyppd/runner.py*, *pyppd/ppd.py*, *pyppd/archiver.py*, *ISSUES.txt*: Now,
    pyppd prints to stdout the WARNING, ERROR and FATAL messages, and logs  
    those and every other to a log file (defaults to pyppd.log), which has a size
    limit of 2 MB. You can use --verbose and --debug to make pyppd more
    verbose. 

v0.4.1 (2010-08-09)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.4.1 

  * *pyppd/archiver.py*: When we have two PPDs with the same filename, we
    create add a random number to   one of them, so we can add both to our
    archive. But, if this PPD which'll have   the new name have more than one
    entry in the list, each entry will have a   different random name. This
    commit fixes this. 

  * *ISSUES.txt*: Adds new issue. 

  * *ISSUES.txt*: "setup.py install" shouldn't link pyppd to
    /usr/lib/cups/driver. What should be   there isn't pyppd, but the archive
    generated by it. 

v0.4.0 (2010-08-09)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.4.0 

  * *pyppd/ppd.py*: Now, when parsing a PPD, it adds as many \*1284DeviceID
    lines as it can find.   Afterwards, it looks for \*Product lines. If found,
    check if it's unique (if   there wasn't an \*1284DeviceID line with the same
    product model), and adds it if   so. 

  * *pyppd/archiver.py*, *ISSUES.txt*: If we found a PPD with the same
    filename, we simply add a random number   (between 0 and 99) to the end of
    its filename. 

  * *pyppd/pyppd-ppdfile.in*: Renaming index -> value. 

v0.3.0 (2010-08-07)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.3.0 

  * *pyppd/ppd.py*, *pyppd/archiver.py*, *ISSUES.txt*: There are some of PPDs
    which are in different files but contains the same   "\*PCFileName:". So,
    without this change, pyppd overwritten them in the index,   making them
    impossible to recover. Now, it'll only happen when there're two   PPDs with
    the same basename. I'm also writing at ISSUES.txt that we need to   figure
    out how to solve this possible problem. 

  * *pyppd/ppd.py*: Fixing typo deviceid -> deviceid_re. 

  * *pyppd/ppd.py*, *pyppd/archiver.py*: Renaming name -> filename. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/ppd.py*, *pyppd/archiver.py*: As Till
    Kamppeter pointed out, a single PPD might have multiple descriptions,  
    following the rules: a) If the PPD has an "\*1284DeviceID:" line, create a  
    single list entry with this device ID; b) If the PPD has one or more  
    "\*Product:" lines, create a pseudo device ID using the content of the  
    "\*Manufacturer:" line as manufacturer and "\*Product:" line as model,
    whichout   the parentheses. 

  * *ISSUES.txt*: Removing "support for .ppd.gz" from our ISSUES list. 

v0.2.2 (2010-08-07)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.2.2 

  * *pyppd/archiver.py*, *README.txt*: Now, instead of looking for only \*.ppd
    in the directory passed as parameter, it   looks also for \*.ppd.gz. When
    found, it tests: does it ends with ".gz"   (case-sensitive (I have to change
    this))? If so, opens with gzip. If not, opens   as usual. When we archive
    gzipped PPDs, we save them uncompressed. 

  * *pyppd/archiver.py*: Refactoring some variable names. 

v0.2.1 (2010-08-07)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.2.1 

  * *pyppd/archiver.py*: Updating archiver.compress() documentation. 

v0.2.0 (2010-08-07)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Changing version
    to 0.2.0 

  * *pyppd/archiver.py*: Removing useless try/except block. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/archiver.py*: Instead of creating a .tar
    archive, we simply concatenate all PPDs found in a   big text file, and save
    each's start and length inside of it. Then, when we   have to "cat" it, we
    decompress the text file and print only In my tests, it's   around 8% faster
    than using .tar. Also, it makes it easier, in the future, to   support not
    only files .ppd, but also .ppd.gz. 

  * *pyppd/runner.py*, *pyppd/archiver.py*: Rename method create_archive ->
    archive 

  * *pyppd/archiver.py*: Reorganizing methods, the most important at the top 

  * *pyppd/compressor.py*: Updating decompress() documentation 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/lzma_proxy.py*, *pyppd/compressor.py*,
    *pyppd/archiver.py*: Renaming lzma_proxy.py to compressor.py 

  * *pyppd/runner.py*, *pyppd/compressor.py*, *pyppd/archiver.py*: The
    runner.py shouldn't have to use compressor.py. It should simply ask  
    archiver.py to create the archive. Compression is an implementation detail of
    archiver. 

  * *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Fixing version number 0.1.0 ->
    0.1.6 

  * *pyppd/ppd.py*: Removing space between DeviceID's field name and content. 

  * *setup.py*, *contrib/git2changes.py*: I made git2changes which simply gets
    the commit messages and organizes them   into something similar to the GNU
    ChangeLog style. But, instead of separating   the commits by day, I organize
    them by version. I think this way, even as it's   not standard, generates a
    file that is more useful to pyppd's users. 

  * *CHANGES.txt*, *.gitignore*: As CHANGES.txt is created from the git log, we
    have no use to maintain it under   version control. 

v0.1.6 (2010-08-03)
--------------------

  * *setup.py*: Changing version to 0.1.6 

  * *MANIFEST.in*: So, pyppd/pyppd-ppdfile.in gets into the distribution
    archive. 

  * *pyppd/pyppd-ppdfile.in*: As now we don't uncompress the .tar.xz archive at
    ls(), when we tested if   ppd.__class__ == tarfile.TarFile, it always failed.
    Now, at the iteration, we   get the key AND the value (ppd). If the key is
    ARCHIVE, we continue. 

  * *ISSUES.txt*: Adding Till's suggestions and some ideas to ISSUES.txt 

  * *setup.py*: Making setup.py executable 

v0.1.5 (2010-08-01)
--------------------

  * *setup.py*: Adding a description and bumping version to 0.1.4 

v0.1.4 (2010-08-01)
--------------------

  * *setup.py*: Changing version to 0.1.4 

  * *README.txt*: Adding instructions to install with pip 

v0.1.3 (2010-08-01)
--------------------

  * *setup.py*: Changing version to 0.1.3 

  * *pyppd/compressor.py*: Instead of silently ignoring exceptions, raises them 

  * *README.txt*: Minor reStructuredText fixes 

v0.1.1 (2010-08-01)
--------------------

  * *setup.py*: Renaming the license in setup.py to GPLv3 

  * *setup.py*: Forgot to add read() to the long_description 

  * *setup.py*, *README.txt*: Adding a README.txt and setting it as
    long_description 

  * *pyppd/pyppd-ppdfile.in*: When you tried ./pyppd-ppdfile cat my-ppd.ppd, it
    raised a IndexError, as it   tried to "my-ppd.ppd".split(":")[1], which don't
    exists. Now, instead of trying   to read the second element ([1]), we read
    the last element, which works in both   cases. 

  * *setup.py*: Remove pyppd/test, as I am not using tests (yet) 

  * *ISSUES.txt*: As Gitorious don't offers us one. Maybe we should consider
    switching to github   or something... 

  * *pyppd/pyppd-ppdfile.in*: As the file lzma_proxy.py is inserted into
    pyppd-compressor, we don't use   "import lzma_proxy as lzma". So, the methods
    are in the local namespace, thus   we use compress/decompress, and not
    lzma.compress/decompress. 

  * *pyppd/lzma_proxy.py*: Now, I need to change it's name. Lzma_proxy seems
    unnatural to me, and we   already use compressor for another file. Maybe
    xz.py? 

  * *pyppd/pyppd-ppdfile.in*: We used to decompress the PPDs in load(). So,
    even if the user simply wanted to   list the PPDs (which we use only the
    index), he had to decompress everything.   Now, only when the user asks to
    cat a file, we decompress the PPDs. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/compressor.py*: This way, we compress
    212mb to 3.2mb, instead of 8,2mb. This makes the program   a bit slower (0.2s
    in my tests), but I think it's worthy. 

  * *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Adding the \ to concatenate
    the multiline strings 

  * *pyppd/pyppd-ppdfile.in*: Checks if pyppd-ppdfile is run directly and, if
    so, run main() 

  * *pyppd/pyppd-ppdfile.in*: The name 'list()' is used for instantiate list
    objects. 

  * *pyppd/ppd.py*: Ppd: Add object inheritance 

  * *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*: Fitting to 80 columns 

  * *pyppd/archiver.py*: Archiver: Removes unused import 

  * *pyppd/compressor.py*: When we add the PPDs to the .tar file,
    /usr/share/ppd becomes usr/share/ppd.   This way, we were unable to find it
    afterwards. Now, we convert every path   passed as argument to absolute, and
    simply ignore the first slash when creating   the index. 

  * *pyppd/ppd.py*: Removes the "(" and ")" from the Product line 

  * *pyppd/ppd.py*: Use Product instead of NickName in the 1284DeviceID
    fallback 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/compressor.py*: It's better than the
    sqlite3 solution because we can decompress and load the   tar file from
    memory, without the need to write to decompress, write As tar   doesn't have
    an index, it needs to do a sequential search to find the files. It   might be
    better to use 7z instead. 

  * *pyppd/lzma_proxy.py*: Adds compress_file(path), which compresses path with
    xz binary 

  * *pyppd/compressor.py*: This way, sqlite3 indexes its values, making the
    selects much faster. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/compressor.py*: It's much faster to load
    the sqlite3 DB and begin issuing SQL statements than   to load the .sql dump
    into a new sqlite3 DB. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/compressor.py*: cPickle uses too much
    memory when pickling/unpickling. And, as we work with   large files, it
    becomes a problem. SQLite3 is more efficient in this regard. 

  * *pyppd/runner.py*: Fixing bug of undefined variable when running pyppd with
    unexistent directory   as argument 

  * *pyppd/ppd.py*: This is probably wrong [1] but, as I found many PPDs which
    1284DeviceID [1]  
    http://www.undocprint.org/formats/communication_protocols/ieee_1284#ieee_1284_device_

  * *pyppd/compressor.py*: Sets ppds = None when we don't need it anymore,
    trying to keep memory usage   low. 

  * *pyppd/ppd.py*: Adding languages not found in ISO 639, but found in some
    PPDs 

  * *pyppd/ppd.py*: Converts PPD's language name to it's ISO 639 code 

  * *pyppd/ppd.py*, *pyppd/compressor.py*: When compressing, ignore the PPDs
    which couldn't be parsed. 

  * *pyppd/pyppd-ppdfile.in*, *pyppd/ppd.py*: Instead of having the PPDs names
    hardcoded with the executable's, prefix them   at execution time. Also,
    remove the "./" of executable's name, when called like   ./pyppd-ppdfile. 

  * *setup.py*, *pyppd/runner.py*, *pyppd/pyppd-ppdfile.in*,
    *pyppd/archiver.py*: Now, everything will be in only one file by default.
    When running pyppd, it'll   parse the folder passed as argument and create,
    by default, pyppd-ppdfile. You   can run it with list or cat URI to get the
    compressed PPDs. 

  * *pyppd/runner.py*: Refactoring (extract method parse_args()) 

  * *pyppd/ppd.py*: We don't need to save the ppd_file anymore 

  * *pyppd/compressor.py*: So, when loading the resulting pickle file, we don't
    need the PPD class   definition. We just load a tuple with two strings. 

  * *pyppd/runner.py*: Adds command-line argument parsing 

  * *pyppd/ppd.py*, *pyppd/compressor.py*: PPD class now saves its PPD file in
    an attribute 

  * *pyppd/lzma_proxy.py*: communicate() returns a tuple (stdout, stderr).
    Returns stdout. 

  * *pyppd/compressor.py*: Refactoring import lzma_proxy 

  * *pyppd/ppd.py*, *pyppd/compressor.py*: The PPD class parses a string with
    the ppd, looking for its attributes. Now,   instead of saving a dictionary
    with ppd_path => {size: ppd_size, start:   ppd_start}, it creates a PPD
    object for each ppd, with it's attributes, so we   can in the future list
    them as foomatic does. 

  * *pyppd/lzma_proxy.py*, *pyppd/compressor.py*: This way, we can fallback to
    using the xz binary, if there isn't python-lzma   available. It also has the
    advantage of using the fastest, if both are   available. I need to do some
    tests to check which is better. 

  * *pyppd/compressor.py*: Adding compressor.compress() docstring. 

  * *pyppd/compressor.py*: Forgot the final period. 

  * *pyppd/compressor.py*: Removing garbage 

  * *pyppd/compressor.py*: Minor typo fix 

v0.1.0 (2010-06-12)
--------------------

  * *setup.py*, *pyppd/runner.py*, *pyppd/compressor.py*, *bin/pyppd*,
    *MANIFEST.in*, *LICENSE.txt*, *CHANGES.txt*, *.gitignore*: Initial release. 

