Index: /pykota/trunk/README
===================================================================
--- /pykota/trunk/README (revision 1181)
+++ /pykota/trunk/README (revision 1185)
@@ -280,34 +280,61 @@
 printers, and change the administrator's email address.
 
-  - CUPS Print Backend :
-  
-    Modify the PPD files for each printer on which you want to manage
-    print quotas, for example /etc/cups/ppd/lp.ppd :
-    
-    --- Add the line below exactly as-is somewhere near the top ---
-    *cupsFilter:  "application/vnd.cups-postscript 0 /usr/bin/pykota"
-    --- Add the line above exactly as-is somewhere near the top  ---
-    
-    Modify the path to the pykota executable if needed, unfortunately 
-    you have to supply the correct absolute path here due to CUPS 
-    internals, or put the pykota executable into /usr/lib/cups/filter 
-    instead of into /usr/bin. 
-    
-    Do this for each ppd file present in this directory if you want
-    to enable quota on every printer.
-              
-    WARNING : In the case you've got a non-postscript printer, chances
-              are that the *cupsFilter is already filled-in and points
-              to cupsomatic or such a print filter. In this case please
-              check if you can switch your printer to PostScript mode
-              or if there's a way to make it accept PostScript jobs.
-              If yes then ensure that your workstations uses a PostScript
-              printer driver, and replace the *cupsFilter line with the
-              one pointing to the pykota filter. This should work, but
-              is currently untested.
-              If your printer really needs the original *cupsFilter line
-              then you may not be able to use PyKota easily for now.
-
-  - LPRng Print Backend :
+  - CUPS Printing System :
+  
+    Once and for all :
+    
+        Create a symbolic link to the cupspykota backend 
+        in CUPS's backend directory :
+        
+          $ cd /usr/lib/cups/backend
+          $ ln -s /usr/bin/cupspykota cupspykota
+          
+        Restart CUPS so that the new backend can be detected.
+        
+          $ /etc/init.d/cupsys restart
+          
+    For new printers :
+    
+        Go to CUPS management interface (http://localhost:631) 
+        and choose the appropriate PyKota managed device depending
+        on the type of printer you use. For example, if your
+        printer is waiting on :
+        
+            socket://myprinter.domain.com:9100
+            
+        Then choose :    
+        
+            cupspykota:socket://myprinter.domain.com:9100
+            
+        Configure your printer as usual.    
+        
+        That's all.
+    
+    For existing printers :
+    
+        If you had already installed PyKota, then remove
+        the *cupsFilter lines in your PPD files for each
+        printer already managed printers. Each line to
+        remove is of the form :
+        
+          *cupsFilter: "application/vnd.cups-postscript 0 /usr/bin/pykota"
+        
+        Don't touch anything else, especially any other *cupsFilter line.
+        Then save each of these files and restart CUPS.
+        
+        Then, the easiest is to directly modify the DeviceURI lines 
+        in /etc/cups/printers.conf, you just have to put 
+        'cupspykota:' in front of what is already on these lines. 
+        For example, replace :
+    
+            DeviceURI socket://myprinter.domain.com:9100
+        
+        with :    
+    
+            DeviceURI cupspykota:socket://myprinter.domain.com:9100
+        
+        Save the file and restart CUPS.
+
+  - LPRng Printing System :
   
     Modify the /etc/printcap file to add two lines identical to these ones :
Index: /pykota/trunk/pykota/version.py
===================================================================
--- /pykota/trunk/pykota/version.py (revision 1179)
+++ /pykota/trunk/pykota/version.py (revision 1185)
@@ -22,5 +22,5 @@
 #
 
-__version__ = "1.16alpha6_unofficial"
+__version__ = "1.16alpha7_unofficial"
 
 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""
Index: /pykota/trunk/bin/cupspykota
===================================================================
--- /pykota/trunk/bin/cupspykota (revision 1184)
+++ /pykota/trunk/bin/cupspykota (revision 1185)
@@ -24,4 +24,8 @@
 #
 # $Log$
+# Revision 1.7  2003/11/14 22:05:12  jalet
+# New CUPS backend fully functionnal.
+# Old CUPS configuration method is now officially deprecated.
+#
 # Revision 1.6  2003/11/14 20:13:11  jalet
 # We exit the loop too soon.
@@ -147,9 +151,4 @@
     return "%s >/dev/null" % (cmdline % locals())
 
-def sigterm_handler(signum, frame) :
-    """Handler for SIGTERM."""
-    sys.stderr.write("INFO: PyKota backend aborted.")
-    sys.exit(1)
-    
 def main(thebackend) :    
     """Do it, and do it right !"""
@@ -157,7 +156,10 @@
     # CUPS backends ignore SIGPIPE and exit(1) on SIGTERM
     # Fortunately SIGPIPE is already ignored by Python
-    # It's there just in case this changes in the future
+    # It's there just in case this changes in the future.
+    # But here we will IGNORE SIGTERM for now, and see
+    # if we shouldn't pass it to the original backend
+    # instead.
     signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-    signal.signal(signal.SIGTERM, sigterm_handler)
+    signal.signal(signal.SIGTERM, signal.SIG_IGN)
     
     #
@@ -226,5 +228,7 @@
         thebackend.accounter.beginJob(printer, user)
         
-        # executes original backend
+        # Now it becomes tricky...
+        
+        # First ensure that we have a file object as input
         mustclose = 0    
         if thebackend.inputfile is not None :    
@@ -236,7 +240,12 @@
         else :    
             infile = sys.stdin
+            
+        # Find the real backend pathname    
         realbackend = os.path.join(os.path.split(sys.argv[0])[0], thebackend.originalbackend)
-        subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, arg0=os.environ["DEVICE_URI"])
-        pollster = select.poll()
+        
+        # And launch it
+        subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, bufsize=0, arg0=os.environ["DEVICE_URI"])
+        
+        # Save file descriptors, we will need them later.
         infno = infile.fileno()
         stdoutfno = sys.stdout.fileno()
@@ -245,4 +254,8 @@
         tocfno = subprocess.tochild.fileno()
         cerrfno = subprocess.childerr.fileno()
+        
+        # We will have to be careful when dealing with I/O 
+        # So we use a poll object to know when to read or write
+        pollster = select.poll()
         pollster.register(infno, select.POLLIN | select.POLLPRI)
         pollster.register(fromcfno, select.POLLIN | select.POLLPRI)
@@ -251,54 +264,108 @@
         pollster.register(stderrfno, select.POLLOUT)
         pollster.register(tocfno, select.POLLOUT)
+        
+        # Initialize our buffers
         indata = ""
         outdata = ""
         errdata = ""
-        end = 0
-        while not end :
-            availablefds = pollster.poll(500)
-            if not availablefds :
-                end = 1
-            else :    
-                for (fd, mask) in availablefds :
-                    thebackend.logdebug("Stream: %02i  Mask: %02x" % (fd, mask))
-                    if mask & select.POLLHUP :
-                        if fd == infno :
-                            end = 1 # this happens with real stdin
-                    if mask & select.POLLOUT :
-                        if fd == tocfno :
-                            if indata :
-                                os.write(fd, indata)    
-                                indata = ""
-                        elif fd == stdoutfno :
-                            if outdata :
-                                os.write(fd, outdata)
+        endinput = 0
+        status = -1
+        while status == -1 :
+            # First check if original backend is still alive
+            status = subprocess.poll()
+            
+            # In any case, deal with any remaining I/O
+            availablefds = pollster.poll()
+            for (fd, mask) in availablefds :
+                if mask & select.POLLOUT :
+                    # We can write
+                    if fd == tocfno :
+                        if indata :
+                            os.write(fd, indata)    
+                            indata = ""
+                    elif fd == stdoutfno :
+                        if outdata :
+                            os.write(fd, outdata)
+                            outdata = ""
+                    elif fd == stderrfno :
+                        if errdata :
+                            os.write(fd, errdata)
+                            errdata = ""
+                if (mask & select.POLLIN) or (mask & select.POLLPRI) :     
+                    # We have something to read
+                    data = os.read(fd, 256 * 1024)
+                    if fd == infno :
+                        indata += data
+                        if not data :    # If yes, then no more input data
+                            endinput = 1 # this happens with real files.
+                    elif fd == fromcfno :
+                        outdata += data
+                    elif fd == cerrfno :    
+                        errdata += data
+                if mask & select.POLLHUP :
+                    # Some standard I/O stream has no more datas
+                    if fd == infno :
+                        # Here we are in the case where the input file is stdin.
+                        # which has no more data to be read.
+                        endinput = 1
+                    elif fd == fromcfno :    
+                        # This should never happen, since
+                        # CUPS backends don't send anything on their
+                        # standard output
+                        if outdata :                
+                            try :
+                                os.write(stdoutfno, outdata)
                                 outdata = ""
-                        elif fd == stderrfno :
-                            if errdata :
-                                os.write(fd, errdata)
+                            except :    
+                                pass
+                        try :        
+                            pollster.unregister(fromcfno)        
+                        except KeyError :    
+                            pass
+                        else :    
+                            os.close(fromcfno)
+                    elif fd == cerrfno :    
+                        # Original CUPS backend has finished 
+                        # to write informations on its standard error
+                        if errdata :                
+                            # Try to write remaining info (normally "Ready to print.")
+                            try :
+                                os.write(stderrfno, errdata)
                                 errdata = ""
-                    if (mask & select.POLLIN) or (mask & select.POLLPRI) :     
-                        data = os.read(fd, 256 * 1024)
-                        if fd == infno :
-                            indata += data
-                            if not data :
-                                end = 1 # this happens with real files
-                        elif fd == fromcfno :
-                            outdata += data
-                        elif fd == cerrfno :    
-                            errdata += data
-        if indata :                
-            try :
-                os.write(tocfno, indata)
-            except :    
-                pass
-        sys.stdout.flush()                
-        sys.stderr.flush()
-        subprocess.fromchild.close()
-        subprocess.childerr.close()
-        subprocess.tochild.close()
+                            except :    
+                                pass
+                        # We are no more interested in this file descriptor        
+                        try :        
+                            pollster.unregister(cerrfno)        
+                        except KeyError :    
+                            pass
+                        else :    
+                            os.close(cerrfno)
+                        
+            if endinput :            
+                # We deal with remaining input datas here
+                # because EOF can happen in two different
+                # situations and I don't want to duplicate
+                # code, nor making functions.
+                if indata :                
+                    try :
+                        os.write(tocfno, indata)
+                        indata = ""
+                    except :    
+                        pass
+                # Again, we're not interested in this file descriptor        
+                # anymore.
+                try :        
+                    pollster.unregister(tocfno)        
+                except KeyError :    
+                    pass
+                else :    
+                    os.close(tocfno)
+            
+        # Input file was a real file, we have to close it.    
         if mustclose :
             infile.close()
-        status = subprocess.wait()
+            
+        # Check exit code of original CUPS backend.    
         if os.WIFEXITED(status) :
             retcode = os.WEXITSTATUS(status)
Index: /pykota/trunk/NEWS
===================================================================
--- /pykota/trunk/NEWS (revision 1179)
+++ /pykota/trunk/NEWS (revision 1185)
@@ -22,4 +22,12 @@
 PyKota NEWS :
 
+    - 1.16alpha7 :
+    
+        - The CUPS backend is now fully functionnal (it seems).
+        
+        - From now on, the use of the pykota filter with CUPS 
+          is deprecated. The pykota filter still has to be
+          used with LPRng though.
+        
     - 1.16alpha6 :
     
