1 /*
2  * cups - Python bindings for CUPS
3  * Copyright (C) 2002-2020  Tim Waugh <twaugh@redhat.com>
4  * Authors: Tim Waugh <twaugh@redhat.com>
5  *          Zdenek Dohnal <zdohnal@redhat.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21 
22 #include "cupsppd.h"
23 #include "cupsmodule.h"
24 
25 #include <ctype.h>
26 #include <iconv.h>
27 #include <stdbool.h>
28 
29 typedef struct
30 {
31   PyObject_HEAD
32   ppd_option_t *option;
33   PPD *ppd;
34 } Option;
35 
36 typedef struct
37 {
38   PyObject_HEAD
39   ppd_const_t *constraint;
40   PPD *ppd;
41 } Constraint;
42 
43 typedef struct
44 {
45   PyObject_HEAD
46   ppd_group_t *group;
47   PPD *ppd;
48 } Group;
49 
50 typedef struct
51 {
52   PyObject_HEAD
53   ppd_attr_t *attribute;
54   PPD *ppd;
55 } Attribute;
56 
57 /////////////////////////
58 // Encoding conversion //
59 /////////////////////////
60 
61 static int
ppd_encoding_is_utf8(PPD * ppd)62 ppd_encoding_is_utf8 (PPD *ppd)
63 {
64   const char *lang_encoding, *from_encoding;
65   iconv_t cdf, cdt;
66   if (ppd->conv_from != NULL)
67     return 0;
68 
69   lang_encoding = ppd->ppd->lang_encoding;
70   if (lang_encoding && !strcasecmp (lang_encoding, "UTF-8"))
71     return 1;
72 
73   if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin1"))
74     from_encoding = "ISO-8859-1";
75   else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin2"))
76     from_encoding = "ISO-8859-2";
77   else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin5"))
78     from_encoding = "ISO-8859-5";
79   else if (lang_encoding && !strcasecmp (lang_encoding, "JIS83-RKSJ"))
80     from_encoding = "SHIFT-JIS";
81   else if (lang_encoding && !strcasecmp (lang_encoding, "MacStandard"))
82     from_encoding = "MACINTOSH";
83   else if (lang_encoding && !strcasecmp (lang_encoding, "WindowsANSI"))
84     from_encoding = "WINDOWS-1252";
85   else
86     // Guess
87     from_encoding = "ISO-8859-1";
88 
89   cdf = iconv_open ("UTF-8", from_encoding);
90   if (cdf == (iconv_t) -1)
91     cdf = iconv_open ("UTF-8", "ISO-8859-1");
92 
93   cdt = iconv_open (from_encoding, "UTF-8");
94   if (cdt == (iconv_t) -1)
95     cdt = iconv_open ("ISO-8859-1", "UTF-8");
96 
97   ppd->conv_from = malloc (sizeof (iconv_t));
98   *ppd->conv_from = cdf;
99 
100   ppd->conv_to = malloc (sizeof (iconv_t));
101   *ppd->conv_to = cdt;
102 
103   return 0;
104 }
105 
106 static PyObject *
cautious_PyUnicode_DecodeUTF8(const char * str,size_t len)107 cautious_PyUnicode_DecodeUTF8 (const char *str, size_t len)
108 {
109   PyObject *ret = PyUnicode_DecodeUTF8 (str, len, NULL);
110   if (ret == NULL) {
111     // It wasn't UTF-8 after all.  Just make the string safe for ASCII.
112     char *safe;
113     size_t i;
114 
115     PyErr_Clear ();
116     safe = malloc (len + 1);
117     for (i = 0; i < len; i++) {
118       unsigned char ch = str[i];
119       if (!isascii (ch))
120 	ch = '?';
121 
122       safe[i] = ch;
123     }
124     safe[i] = '\0';
125     ret = PyUnicode_DecodeUTF8 (safe, len, NULL);
126     printf ("Bad UTF-8 string \"%s\" changed to \"%s\"\n", str, safe);
127     free (safe);
128   }
129 
130   return ret;
131 }
132 
133 static PyObject *
make_PyUnicode_from_ppd_string(PPD * ppd,const char * ppdstr)134 make_PyUnicode_from_ppd_string (PPD *ppd, const char *ppdstr)
135 {
136   iconv_t cdf;
137   size_t len;
138   char *outbuf, *outbufptr;
139   size_t outbytesleft;
140   size_t origleft;
141   PyObject *ret;
142 
143   if (ppd_encoding_is_utf8 (ppd))
144     return cautious_PyUnicode_DecodeUTF8 (ppdstr, strlen (ppdstr));
145 
146   cdf = *ppd->conv_from;
147 
148   // Reset to initial state
149   iconv (cdf, NULL, NULL, NULL, NULL);
150   len = strlen (ppdstr); // CUPS doesn't keep track of string lengths
151   origleft = outbytesleft = MB_CUR_MAX * len;
152   outbufptr = outbuf = malloc (outbytesleft);
153   if (iconv (cdf, (char **) &ppdstr, &len,
154 	     &outbufptr, &outbytesleft) == (size_t) -1) {
155     free (outbuf);
156     return PyErr_SetFromErrno (PyExc_RuntimeError);
157   }
158 
159   ret = cautious_PyUnicode_DecodeUTF8 (outbuf, origleft - outbytesleft);
160   free (outbuf);
161   return ret;
162 }
163 
164 static char *
utf8_to_ppd_encoding(PPD * ppd,const char * inbuf)165 utf8_to_ppd_encoding (PPD *ppd, const char *inbuf)
166 {
167   char *outbuf, *ret;
168   size_t len, outsize, outbytesleft;
169   iconv_t cdt;
170 
171   if (ppd_encoding_is_utf8 (ppd))
172     return strdup (inbuf);
173 
174   cdt = *ppd->conv_to;
175 
176   // Reset to initial state
177   iconv (cdt, NULL, NULL, NULL, NULL);
178   len = strlen (inbuf);
179   outsize = 1 + 6 * len;
180   outbytesleft = outsize - 1;
181   ret = outbuf = malloc (outsize);
182   if (iconv (cdt, (char **) &inbuf, &len,
183 	     &outbuf, &outbytesleft) == (size_t) -1) {
184     free (outbuf);
185     return NULL;
186   }
187   *outbuf = '\0';
188 
189   return ret;
190 }
191 
192 /////////
193 // PPD //
194 /////////
195 
196 static PyObject *
PPD_new(PyTypeObject * type,PyObject * args,PyObject * kwds)197 PPD_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
198 {
199   PPD *self;
200   self = (PPD *) type->tp_alloc (type, 0);
201   if (self != NULL) {
202     self->ppd = NULL;
203     self->file = NULL;
204     self->conv_from = NULL;
205     self->conv_to = NULL;
206   }
207 
208   return (PyObject *) self;
209 }
210 
211 static int
PPD_init(PPD * self,PyObject * args,PyObject * kwds)212 PPD_init (PPD *self, PyObject *args, PyObject *kwds)
213 {
214   PyObject *filenameobj;
215   char *filename;
216 
217   if (!PyArg_ParseTuple (args, "O", &filenameobj))
218     return -1;
219 
220   if (UTF8_from_PyObj (&filename, filenameobj) == NULL)
221     return -1;
222 
223   self->file = fopen (filename, "r");
224   if (!self->file) {
225     PyErr_SetString (PyExc_RuntimeError, "fopen failed");
226     free (filename);
227     return -1;
228   }
229 
230   debugprintf ("+ PPD %p %s (fd %d)\n", self, filename, fileno (self->file));
231   self->ppd = ppdOpenFile (filename);
232   free (filename);
233   if (!self->ppd) {
234     fclose (self->file);
235     self->file = NULL;
236     PyErr_SetString (PyExc_RuntimeError, "ppdOpenFile failed");
237     return -1;
238   }
239   self->conv_from = self->conv_to = NULL;
240 
241   return 0;
242 }
243 
244 static void
PPD_dealloc(PPD * self)245 PPD_dealloc (PPD *self)
246 {
247   if (self->file) {
248     debugprintf ("- PPD %p (fd %d)\n", self, fileno (self->file));
249     fclose (self->file);
250   } else
251     debugprintf ("- PPD %p (no fd)\n", self);
252 
253   if (self->ppd)
254     ppdClose (self->ppd);
255   if (self->conv_from)
256     iconv_close (*self->conv_from);
257   if (self->conv_to)
258     iconv_close (*self->conv_to);
259 
260   ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
261 }
262 
263 /////////
264 // PPD // METHODS
265 /////////
266 
267 static PyObject *
PPD_localize(PPD * self)268 PPD_localize (PPD *self)
269 {
270   if (!ppdLocalize (self->ppd))
271     Py_RETURN_NONE;
272   return PyErr_SetFromErrno (PyExc_RuntimeError);
273 }
274 
275 static PyObject *
PPD_localizeIPPReason(PPD * self,PyObject * args,PyObject * kwds)276 PPD_localizeIPPReason (PPD *self, PyObject *args, PyObject *kwds)
277 {
278   PyObject *ret;
279   PyObject *reasonobj;
280   PyObject *schemeobj = NULL;
281   char *reason;
282   char *scheme = NULL;
283   char *buffer;
284   const size_t bufsize = 1024;
285   static char *kwlist[] = { "reason", "scheme", NULL };
286 
287   if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist,
288 				    &reasonobj, &schemeobj))
289     return NULL;
290 
291   if (UTF8_from_PyObj (&reason, reasonobj) == NULL)
292     return NULL;
293 
294   if (schemeobj)
295     if (UTF8_from_PyObj (&scheme, schemeobj) == NULL) {
296       free (reason);
297       return NULL;
298     }
299 
300   buffer = malloc (bufsize);
301   if (ppdLocalizeIPPReason (self->ppd, reason, scheme, buffer, bufsize))
302   {
303     ret = make_PyUnicode_from_ppd_string (self, buffer);
304   } else {
305     Py_RETURN_NONE;
306   }
307 
308   free (reason);
309   if (scheme)
310     free (scheme);
311   free (buffer);
312   return ret;
313 }
314 
315 static PyObject *
PPD_localizeMarkerName(PPD * self,PyObject * args)316 PPD_localizeMarkerName (PPD *self, PyObject *args)
317 {
318   PyObject *ret;
319   PyObject *nameobj;
320   char *name;
321   const char *lname;
322 
323   if (!PyArg_ParseTuple (args, "O", &nameobj))
324     return NULL;
325 
326   if (UTF8_from_PyObj (&name, nameobj) == NULL)
327     return NULL;
328 
329   lname = ppdLocalizeMarkerName (self->ppd, name);
330   free (name);
331 
332   if (lname != NULL)
333   {
334     ret = make_PyUnicode_from_ppd_string (self, lname);
335   } else {
336     Py_RETURN_NONE;
337   }
338 
339   return ret;
340 }
341 
342 static PyObject *
PPD_markDefaults(PPD * self)343 PPD_markDefaults (PPD *self)
344 {
345   ppdMarkDefaults (self->ppd);
346   Py_RETURN_NONE;
347 }
348 
349 static PyObject *
PPD_markOption(PPD * self,PyObject * args)350 PPD_markOption (PPD *self, PyObject *args)
351 {
352   int conflicts;
353   char *name, *value;
354   char *encname, *encvalue;
355   if (!PyArg_ParseTuple (args, "eses", "UTF-8", &name, "UTF-8", &value))
356     return NULL;
357 
358   encname = utf8_to_ppd_encoding (self, name);
359   PyMem_Free (name);
360   if (!encname) {
361     PyMem_Free (value);
362     return PyErr_SetFromErrno (PyExc_RuntimeError);
363   }
364 
365   encvalue = utf8_to_ppd_encoding (self, value);
366   PyMem_Free (value);
367   if (!encvalue) {
368     free (encname);
369     return PyErr_SetFromErrno (PyExc_RuntimeError);
370   }
371 
372   conflicts = ppdMarkOption (self->ppd, encname, encvalue);
373   free (encname);
374   free (encvalue);
375   return Py_BuildValue ("i", conflicts);
376 }
377 
378 static PyObject *
PPD_conflicts(PPD * self)379 PPD_conflicts (PPD *self)
380 {
381   return PyLong_FromLong (ppdConflicts (self->ppd));
382 }
383 
384 static PyObject *
PPD_findOption(PPD * self,PyObject * args)385 PPD_findOption (PPD *self, PyObject *args)
386 {
387   PyObject *ret;
388   PyObject *optionobj;
389   char *option;
390   ppd_option_t *opt;
391 
392   if (!PyArg_ParseTuple (args, "O", &optionobj))
393     return NULL;
394 
395   if (UTF8_from_PyObj (&option, optionobj) == NULL)
396     return NULL;
397 
398   opt = ppdFindOption (self->ppd, option);
399   free (option);
400   if (opt) {
401     PyObject *args = Py_BuildValue ("()");
402     PyObject *kwlist = Py_BuildValue ("{}");
403     Option *optobj = (Option *) PyType_GenericNew (&cups_OptionType,
404 						   args, kwlist);
405     Py_DECREF (args);
406     Py_DECREF (kwlist);
407     optobj->option = opt;
408     optobj->ppd = self;
409     Py_INCREF (self);
410     ret = (PyObject *) optobj;
411   } else {
412     Py_RETURN_NONE;
413   }
414 
415   return ret;
416 }
417 
418 static PyObject *
PPD_findAttr(PPD * self,PyObject * args,PyObject * kwds)419 PPD_findAttr (PPD *self, PyObject *args, PyObject *kwds)
420 {
421   PyObject *ret;
422   PyObject *nameobj;
423   PyObject *specobj = NULL;
424   char *name;
425   char *spec = NULL;
426   ppd_attr_t *attr;
427   static char *kwlist[] = { "name", "spec", NULL };
428 
429   if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist,
430 				    &nameobj, &specobj))
431     return NULL;
432 
433   if (UTF8_from_PyObj (&name, nameobj) == NULL)
434     return NULL;
435 
436   if (specobj)
437     if (UTF8_from_PyObj (&spec, specobj) == NULL) {
438       free (name);
439       return NULL;
440     }
441 
442   attr = ppdFindAttr (self->ppd, name, spec);
443   free (name);
444   if (spec)
445     free (spec);
446   if (attr) {
447     PyObject *largs = Py_BuildValue ("()");
448     PyObject *lkwlist = Py_BuildValue ("{}");
449     Attribute *attrobj = (Attribute *) PyType_GenericNew (&cups_AttributeType,
450 							  largs, lkwlist);
451     Py_DECREF (largs);
452     Py_DECREF (lkwlist);
453     attrobj->attribute = attr;
454     attrobj->ppd = self;
455     Py_INCREF (self);
456     ret = (PyObject *) attrobj;
457   } else {
458     Py_RETURN_NONE;
459   }
460 
461   return ret;
462 }
463 
464 static PyObject *
PPD_findNextAttr(PPD * self,PyObject * args,PyObject * kwds)465 PPD_findNextAttr (PPD *self, PyObject *args, PyObject *kwds)
466 {
467   PyObject *ret;
468   PyObject *nameobj;
469   PyObject *specobj = NULL;
470   char *name;
471   char *spec = NULL;
472   ppd_attr_t *attr;
473   static char *kwlist[] = { "name", "spec", NULL };
474 
475   if (!PyArg_ParseTupleAndKeywords (args, kwds, "O|O", kwlist,
476 				    &nameobj, &specobj))
477     return NULL;
478 
479   if (UTF8_from_PyObj (&name, nameobj) == NULL)
480     return NULL;
481 
482   if (specobj)
483     if (UTF8_from_PyObj (&spec, specobj) == NULL) {
484       free (name);
485       return NULL;
486     }
487 
488   attr = ppdFindNextAttr (self->ppd, name, spec);
489   free (name);
490   if (spec)
491     free (spec);
492   if (attr) {
493     PyObject *largs = Py_BuildValue ("()");
494     PyObject *lkwlist = Py_BuildValue ("{}");
495     Attribute *attrobj = (Attribute *) PyType_GenericNew (&cups_AttributeType,
496 							  largs, lkwlist);
497     Py_DECREF (largs);
498     Py_DECREF (lkwlist);
499     attrobj->attribute = attr;
500     attrobj->ppd = self;
501     Py_INCREF (self);
502     ret = (PyObject *) attrobj;
503   } else {
504     Py_RETURN_NONE;
505   }
506 
507   return ret;
508 }
509 
510 static int
nondefaults_are_marked(ppd_group_t * g)511 nondefaults_are_marked (ppd_group_t *g)
512 {
513   ppd_option_t *o;
514   int oi;
515   for (oi = 0, o = g->options;
516        oi < g->num_options;
517        oi++, o++) {
518     ppd_choice_t *c;
519     int ci;
520     for (ci = 0, c = o->choices;
521 	 ci < o->num_choices;
522 	 ci++, c++) {
523       if (c->marked) {
524 	if (strcmp (c->choice, o->defchoice))
525 	  return 1;
526 	break;
527       }
528     }
529   }
530 
531   return 0;
532 }
533 
534 static PyObject *
PPD_nondefaultsMarked(PPD * self)535 PPD_nondefaultsMarked (PPD *self)
536 {
537   int nondefaults_marked = 0;
538   ppd_group_t *g;
539   int gi;
540   for (gi = 0, g = self->ppd->groups;
541        gi < self->ppd->num_groups && !nondefaults_marked;
542        gi++, g++) {
543     ppd_group_t *sg;
544     int sgi;
545     if (nondefaults_are_marked (g)) {
546       nondefaults_marked = 1;
547       break;
548     }
549 
550     for (sgi = 0, sg = g->subgroups;
551 	 sgi < g->num_subgroups;
552 	 sgi++, sg++) {
553       if (nondefaults_are_marked (sg)) {
554 	nondefaults_marked = 1;
555 	break;
556       }
557     }
558   }
559 
560   return PyBool_FromLong (nondefaults_marked);
561 }
562 
563 #if !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
564 /*
565  * A rudimentary emulation of getline() for systems that dont support it
566  * natively.  Since this is used for PPD file reading, it assumes (possibly
567  * falsely) that BUFSIZ is big enough.
568  */
569 ssize_t
getline(char ** line,size_t * linelen,FILE * fp)570 getline(char **line, size_t *linelen, FILE *fp)
571 {
572   if (*linelen == 0) {
573     *linelen = BUFSIZ;
574     *line = malloc(*linelen);
575   }
576 
577   memset(*line, 0, *linelen);
578   fgets(*line, *linelen, fp);
579 
580   return (strlen(*line));
581 }
582 #endif
583 
584 /*
585  * emit marked options by returning a string.
586  */
587 
588 static PyObject *
PPD_emitString(PPD * self,PyObject * args)589 PPD_emitString (PPD *self, PyObject *args)
590 {
591   ppd_section_t section;
592   float min_order;
593   char *emitted;
594   PyObject *ret;
595 
596   if (!PyArg_ParseTuple (args, "if", &section, &min_order))
597     return NULL;
598 
599   emitted = ppdEmitString(self->ppd, section, min_order);
600 
601   if (emitted) {
602     ret = PyUnicode_FromString (emitted);
603     free (emitted);
604   } else {
605     Py_RETURN_NONE;
606   }
607 
608   return ret;
609 }
610 
611 /*
612  * emit marked options by writing to a file object.
613  */
614 
615 static PyObject *
PPD_emit(PPD * self,PyObject * args)616 PPD_emit (PPD *self, PyObject *args)
617 {
618   PyObject *pyFile;
619   ppd_section_t section;
620   FILE *f;
621 
622   if (!PyArg_ParseTuple (args, "Oi", &pyFile, &section))
623     return NULL;
624 
625   int fd = PyObject_AsFileDescriptor(pyFile);
626   f = fdopen(fd, "w");
627   if (!f)
628     return PyErr_SetFromErrno (PyExc_RuntimeError);
629 
630   if (!ppdEmit(self->ppd, f, section))
631     Py_RETURN_NONE;
632   return PyErr_SetFromErrno (PyExc_RuntimeError);
633 }
634 
635 /*
636  * emit marked options after order dependency by writing to a file object.
637  */
638 
639 static PyObject *
PPD_emitAfterOrder(PPD * self,PyObject * args)640 PPD_emitAfterOrder (PPD *self, PyObject *args)
641 {
642   PyObject *pyFile;
643   ppd_section_t section;
644   FILE *f;
645   int limit;
646   float min_order;
647 
648   if (!PyArg_ParseTuple (args, "Oiif", &pyFile, &section, &limit, &min_order))
649     return NULL;
650 
651   int fd = PyObject_AsFileDescriptor(pyFile);
652   f = fdopen(fd, "w");
653   if (!f)
654     return PyErr_SetFromErrno (PyExc_RuntimeError);
655 
656   if (!ppdEmitAfterOrder(self->ppd, f, section, limit, min_order))
657     Py_RETURN_NONE;
658   return PyErr_SetFromErrno (PyExc_RuntimeError);
659 }
660 
661 /*
662  * emit marked options by writing to a file descriptor.
663  */
664 
665 static PyObject *
PPD_emitFd(PPD * self,PyObject * args)666 PPD_emitFd (PPD *self, PyObject *args)
667 {
668   ppd_section_t section;
669   int f;
670 
671   if (!PyArg_ParseTuple (args, "ii", &f, &section))
672     return NULL;
673 
674   if (!ppdEmitFd(self->ppd, f, section))
675     Py_RETURN_NONE;
676   return PyErr_SetFromErrno (PyExc_RuntimeError);
677 }
678 
679 /*
680  * emit JCL options by writing to a file object.
681  */
682 
683 static PyObject *
PPD_emitJCL(PPD * self,PyObject * args)684 PPD_emitJCL (PPD *self, PyObject *args)
685 {
686   PyObject *pyFile;
687   int job_id;
688   PyObject *userobj;
689   PyObject *titleobj;
690   char *user;
691   char *title;
692   FILE *f;
693 
694   if (!PyArg_ParseTuple (args, "OiOO", &pyFile, &job_id, &userobj, &titleobj))
695     return NULL;
696 
697   if (UTF8_from_PyObj (&user, userobj) == NULL)
698     return NULL;
699 
700   if (UTF8_from_PyObj (&title, titleobj) == NULL) {
701     free (user);
702     return NULL;
703   }
704 
705   int fd = PyObject_AsFileDescriptor(pyFile);
706   f = fdopen(fd, "w");
707   if (!f)
708     return PyErr_SetFromErrno (PyExc_RuntimeError);
709 
710   if (!ppdEmitJCL(self->ppd, f, job_id, user, title))
711     Py_RETURN_NONE;
712 
713   free (user);
714   free (title);
715   return PyErr_SetFromErrno (PyExc_RuntimeError);
716 }
717 
718 /*
719  * emit JCL end by writing to a file object.
720  */
721 
722 static PyObject *
PPD_emitJCLEnd(PPD * self,PyObject * args)723 PPD_emitJCLEnd (PPD *self, PyObject *args)
724 {
725   PyObject *pyFile;
726   FILE *f;
727 
728   if (!PyArg_ParseTuple (args, "O", &pyFile))
729     return NULL;
730 
731   int fd = PyObject_AsFileDescriptor(pyFile);
732   f = fdopen(fd, "w");
733   if (!f)
734     return PyErr_SetFromErrno (PyExc_RuntimeError);
735 
736   if (!ppdEmitJCLEnd(self->ppd, f))
737     Py_RETURN_NONE;
738   return PyErr_SetFromErrno (PyExc_RuntimeError);
739 }
740 
741 PyObject *
PPD_writeFd(PPD * self,PyObject * args)742 PPD_writeFd (PPD *self, PyObject *args)
743 {
744   char *line = NULL;
745   size_t linelen = 0;
746   FILE *out;
747   int fd;
748   int dfd;
749   if (!PyArg_ParseTuple (args, "i", &fd))
750     return NULL;
751 
752   dfd = dup (fd);
753   if (dfd == -1)
754     return PyErr_SetFromErrno (PyExc_RuntimeError);
755 
756   out = fdopen (dfd, "w");
757   if (!out)
758     return PyErr_SetFromErrno (PyExc_RuntimeError);
759 
760   rewind (self->file);
761   while (!feof (self->file)) {
762     int written = 0;
763     ssize_t got = getline (&line, &linelen, self->file);
764     if (got == -1)
765       break;
766 
767     if (!strncmp (line, "*Default", 8)) {
768       char *keyword;
769       char *start = line + 8;
770       char *end;
771       ppd_choice_t *choice;
772       for (end = start; *end; end++)
773 	if (isspace (*end) || *end == ':')
774 	  break;
775       keyword = calloc(1, (end - start) + 1);
776       strncpy(keyword, start, end - start);
777       choice = ppdFindMarkedChoice (self->ppd, keyword);
778 
779       // Treat PageRegion, PaperDimension and ImageableArea specially:
780       // if not marked, use PageSize option.
781       if (!choice && (!strcmp (keyword, "PageRegion") ||
782 		      !strcmp (keyword, "PaperDimension") ||
783 		      !strcmp (keyword, "ImageableArea")))
784 	choice = ppdFindMarkedChoice (self->ppd, "PageSize");
785 
786       if (choice) {
787 	fprintf (out, "*Default%s: %s", keyword, choice->choice);
788 	if (strchr (end, '\r'))
789 	  fputs ("\r", out);
790 	fputs ("\n", out);
791 	written = 1;
792       }
793     }
794 
795     if (!written)
796       fputs (line, out);
797   }
798 
799   fclose (out);
800   if (line)
801     free (line);
802 
803   Py_RETURN_NONE;
804 }
805 
806 /////////
807 // PPD // ATTRIBUTES
808 /////////
809 
810 static PyObject *
PPD_getConstraints(PPD * self,void * closure)811 PPD_getConstraints (PPD *self, void *closure)
812 {
813   PyObject *ret = PyList_New (0);
814   ppd_const_t *c;
815   int i;
816   for (i = 0, c = self->ppd->consts;
817        i < self->ppd->num_consts;
818        i++, c++) {
819     PyObject *args = Py_BuildValue ("()");
820     PyObject *kwlist = Py_BuildValue ("{}");
821     Constraint *cns = (Constraint *) PyType_GenericNew (&cups_ConstraintType,
822 							args, kwlist);
823     Py_DECREF (args);
824     Py_DECREF (kwlist);
825     cns->constraint = c;
826     cns->ppd = self;
827     Py_INCREF (self);
828     PyList_Append (ret, (PyObject *) cns);
829   }
830 
831   return ret;
832 }
833 
834 static PyObject *
PPD_getAttributes(PPD * self,void * closure)835 PPD_getAttributes (PPD *self, void *closure)
836 {
837   PyObject *ret = PyList_New (0);
838   int i;
839   for (i = 0; i < self->ppd->num_attrs; i++) {
840     PyObject *args = Py_BuildValue ("()");
841     PyObject *kwlist = Py_BuildValue ("{}");
842     Attribute *as = (Attribute *) PyType_GenericNew (&cups_AttributeType,
843 						     args, kwlist);
844     ppd_attr_t *a = self->ppd->attrs[i];
845     Py_DECREF (args);
846     Py_DECREF (kwlist);
847     as->attribute = a;
848     as->ppd = self;
849     Py_INCREF (self);
850     PyList_Append (ret, (PyObject *) as);
851   }
852 
853   return ret;
854 }
855 
856 static PyObject *
PPD_getOptionGroups(PPD * self,void * closure)857 PPD_getOptionGroups (PPD *self, void *closure)
858 {
859   PyObject *ret = PyList_New (0);
860   ppd_group_t *group;
861   int i;
862 
863   for (i = 0, group = self->ppd->groups;
864        i < self->ppd->num_groups;
865        i++, group++) {
866     PyObject *args = Py_BuildValue ("()");
867     PyObject *kwlist = Py_BuildValue ("{}");
868     Group *grp = (Group *) PyType_GenericNew (&cups_GroupType,
869 					      args, kwlist);
870     Py_DECREF (args);
871     Py_DECREF (kwlist);
872     grp->group = group;
873     grp->ppd = self;
874     Py_INCREF (self);
875     PyList_Append (ret, (PyObject *) grp);
876   }
877 
878   return ret;
879 }
880 
881 PyGetSetDef PPD_getseters[] =
882   {
883     { "constraints",
884       (getter) PPD_getConstraints, (setter) NULL,
885       "List of constraints", NULL },
886 
887     { "attributes",
888       (getter) PPD_getAttributes, (setter) NULL,
889       "List of attributes", NULL },
890 
891     { "optionGroups",
892       (getter) PPD_getOptionGroups, (setter) NULL,
893       "List of PPD option groups" },
894 
895     { NULL }, /* Sentinel */
896   };
897 
898 PyMethodDef PPD_methods[] =
899   {
900     { "localize",
901       (PyCFunction) PPD_localize, METH_NOARGS,
902       "localize() -> None\n\n"
903       "Localize PPD to the current locale." },
904 
905     { "localizeIPPReason",
906       (PyCFunction) PPD_localizeIPPReason, METH_VARARGS | METH_KEYWORDS,
907       "localizeIPPReason(reason, scheme) -> string or None\n\n"
908       "Localize IPP reason to the current locale." },
909 
910     { "localizeMarkerName",
911       (PyCFunction) PPD_localizeMarkerName, METH_VARARGS,
912       "localizeMarkerName(name) -> string or None\n\n"
913       "Localize marker name to the current locale." },
914 
915     { "markDefaults",
916       (PyCFunction) PPD_markDefaults, METH_NOARGS,
917       "markDefaults() -> None\n\n"
918       "Set (mark) all options to their default choices." },
919 
920     { "markOption",
921       (PyCFunction) PPD_markOption, METH_VARARGS,
922       "markOption(option, choice) -> integer\n\n"
923       "Set an option to a particular choice.\n\n"
924       "@type option: string\n"
925       "@param option: option keyword\n"
926       "@type choice: string\n"
927       "@param choice: option choice\n"
928       "@return: number of conflicts" },
929 
930     { "conflicts",
931       (PyCFunction) PPD_conflicts, METH_NOARGS,
932       "conflicts() -> integer\n\n"
933       "@return: number of conflicts." },
934 
935     { "findOption",
936       (PyCFunction) PPD_findOption, METH_VARARGS,
937       "findOption(name)\n\n"
938       "@type name: string\n"
939       "@param name: option keyword\n"
940       "@rtype: L{Option} or None\n"
941       "@return: named option, or None if not found." },
942 
943     { "findAttr",
944       (PyCFunction) PPD_findAttr, METH_VARARGS | METH_KEYWORDS,
945       "findAttr(name)\n\n"
946       "@type name: string\n"
947       "@param name: attribute name\n"
948       "@type spec: string\n"
949       "@keyword spec: specifier string (optional)\n"
950       "@rtype: L{Attribute} or None\n"
951       "@return: matching attribute, or None if not found." },
952 
953     { "findNextAttr",
954       (PyCFunction) PPD_findNextAttr, METH_VARARGS | METH_KEYWORDS,
955       "findNextAttr(name)\n\n"
956       "@type name: string\n"
957       "@param name: attribute name\n"
958       "@type spec: string\n"
959       "@keyword spec: specifier string (optional)\n"
960       "@rtype: L{Attribute} or None\n"
961       "@return: next matching attribute, or None if not found." },
962 
963     { "nondefaultsMarked",
964       (PyCFunction) PPD_nondefaultsMarked, METH_NOARGS,
965       "nondefaultsMarked() -> boolean\n\n"
966       "Returns true if any non-default option choices are marked." },
967 
968     { "emitString",
969       (PyCFunction) PPD_emitString, METH_VARARGS,
970       "emitString(section, min_order) -> string\n\n"
971       "Return a string with the marked options for section, with at least min_order order dependency.\n"
972       "@type section: integer\n"
973       "@param section: section id\n"
974       "@type min_order: float\n"
975       "@param min_order: minimum order dependency\n"
976       "@return: string containing emitted postscript" },
977 
978     { "emit",
979       (PyCFunction) PPD_emit, METH_VARARGS,
980       "emit(file, section) -> None\n\n"
981       "Output marked options for section to a file.\n"
982       "@type file: file\n"
983       "@param file: file object\n"
984       "@type section: integer\n"
985       "@param section: section id" },
986 
987     { "emitAfterOrder",
988       (PyCFunction) PPD_emitAfterOrder, METH_VARARGS,
989       "emitAfterOrder(file, section, limit, min_order) -> None\n\n"
990       "Output marked options for section to a file.\n"
991       "@type file: file\n"
992       "@param file: file object\n"
993       "@type section: integer\n"
994       "@param section: section id\n"
995       "@type limit: integer\n"
996       "@param limit: non-zero to use min_order\n"
997       "@type min_order: float\n"
998       "@param min_order: minimum order dependency" },
999 
1000     { "emitFd",
1001       (PyCFunction) PPD_emitFd, METH_VARARGS,
1002       "emitFd(fd, section) -> None\n\n"
1003       "Output marked options for section to a file descriptor.\n"
1004       "@type fd: integer\n"
1005       "@param fd: file descriptor\n"
1006       "@type section: integer\n"
1007       "@param section: section id" },
1008 
1009     { "emitJCL",
1010       (PyCFunction) PPD_emitJCL, METH_VARARGS,
1011       "emitJCL(file, job_id, user, title) -> None\n\n"
1012       "Emit code for JCL options to a file.\n"
1013       "@type file: file object\n"
1014       "@param file: file\n"
1015       "@type job_id: integer\n"
1016       "@param job_id: job id\n"
1017       "@type user: string\n"
1018       "@param user: user name on job\n"
1019       "@type title: string\n"
1020       "@param title: title of job" },
1021 
1022     { "emitJCLEnd",
1023       (PyCFunction) PPD_emitJCLEnd, METH_VARARGS,
1024       "emitJCLEnd(file) -> None\n\n"
1025       "Emit JCLEnd code to a file.\n"
1026       "@type file: file object\n"
1027       "@param file: file" },
1028 
1029     { "writeFd",
1030       (PyCFunction) PPD_writeFd, METH_VARARGS,
1031       "writeFd(fd) -> None\n\n"
1032       "Write PPD file, with marked choices as defaults, to file\n"
1033       "descriptor.\n\n"
1034       "@type fd: integer\n"
1035       "@param fd: open file descriptor" },
1036 
1037     { NULL } /* Sentinel */
1038   };
1039 
1040 PyTypeObject cups_PPDType =
1041   {
1042     PyVarObject_HEAD_INIT(NULL, 0)
1043     "cups.PPD",                /*tp_name*/
1044     sizeof(PPD),               /*tp_basicsize*/
1045     0,                         /*tp_itemsize*/
1046     (destructor)PPD_dealloc,   /*tp_dealloc*/
1047     0,                         /*tp_print*/
1048     0,                         /*tp_getattr*/
1049     0,                         /*tp_setattr*/
1050     0,                         /*tp_compare*/
1051     0,                         /*tp_repr*/
1052     0,                         /*tp_as_number*/
1053     0,                         /*tp_as_sequence*/
1054     0,                         /*tp_as_mapping*/
1055     0,                         /*tp_hash */
1056     0,                         /*tp_call*/
1057     0,                         /*tp_str*/
1058     0,                         /*tp_getattro*/
1059     0,                         /*tp_setattro*/
1060     0,                         /*tp_as_buffer*/
1061     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
1062     "PPD file\n"
1063     "========\n"
1064     "  A PPD file.\n\n"
1065     "@type constraints: L{Constraint} list\n"
1066     "@ivar constraints: list of constraints\n"
1067     "@type attributes: L{Attribute} list\n"
1068     "@ivar attributes: list of attributes\n"
1069     "@type optionGroups: L{Group} list\n"
1070     "@ivar optionGroups: list of PPD option groups\n"
1071     "",                        /* tp_doc */
1072     0,                         /* tp_traverse */
1073     0,                         /* tp_clear */
1074     0,                         /* tp_richcompare */
1075     0,                         /* tp_weaklistoffset */
1076     0,                         /* tp_iter */
1077     0,                         /* tp_iternext */
1078     PPD_methods,               /* tp_methods */
1079     0,                         /* tp_members */
1080     PPD_getseters,             /* tp_getset */
1081     0,                         /* tp_base */
1082     0,                         /* tp_dict */
1083     0,                         /* tp_descr_get */
1084     0,                         /* tp_descr_set */
1085     0,                         /* tp_dictoffset */
1086     (initproc)PPD_init,        /* tp_init */
1087     0,                         /* tp_alloc */
1088     PPD_new,                   /* tp_new */
1089   };
1090 
1091 ////////////
1092 // Option //
1093 ////////////
1094 
1095 static PyObject *
Option_new(PyTypeObject * type,PyObject * args,PyObject * kwds)1096 Option_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
1097 {
1098   Option *self;
1099   self = (Option *) type->tp_alloc (type, 0);
1100   return (PyObject *) self;
1101 }
1102 
1103 static int
Option_init(Option * self,PyObject * args,PyObject * kwds)1104 Option_init (Option *self, PyObject *args, PyObject *kwds)
1105 {
1106   self->option = NULL;
1107   return 0;
1108 }
1109 
1110 static void
Option_dealloc(Option * self)1111 Option_dealloc (Option *self)
1112 {
1113   Py_XDECREF (self->ppd);
1114   ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
1115 }
1116 
1117 static PyObject *
Option_repr(Option * self)1118 Option_repr (Option *self)
1119 {
1120   ppd_option_t *option = self->option;
1121   if (!option)
1122     return PyUnicode_FromString ("<cups.Option>");
1123 
1124   char buffer[256];
1125   snprintf (buffer, 256, "<cups.Option %s=%s>",
1126 			  option->keyword, option->defchoice);
1127     return PyUnicode_FromString (buffer);
1128 }
1129 
1130 ////////////
1131 // Option // ATTRIBUTES
1132 ////////////
1133 
1134 static PyObject *
Option_getConflicted(Option * self,void * closure)1135 Option_getConflicted (Option *self, void *closure)
1136 {
1137   if (!self->option || self->option->conflicted)
1138     Py_RETURN_TRUE;
1139 
1140   Py_RETURN_FALSE;
1141 }
1142 
1143 static PyObject *
Option_getKeyword(Option * self,void * closure)1144 Option_getKeyword (Option *self, void *closure)
1145 {
1146   if (!self->option) {
1147     Py_RETURN_NONE;
1148   }
1149 
1150   return make_PyUnicode_from_ppd_string (self->ppd, self->option->keyword);
1151 }
1152 
1153 static PyObject *
Option_getDefchoice(Option * self,void * closure)1154 Option_getDefchoice (Option *self, void *closure)
1155 {
1156   if (!self->option) {
1157     Py_RETURN_NONE;
1158   }
1159 
1160   return make_PyUnicode_from_ppd_string (self->ppd, self->option->defchoice);
1161 }
1162 
1163 static PyObject *
Option_getText(Option * self,void * closure)1164 Option_getText (Option *self, void *closure)
1165 {
1166   if (!self->option) {
1167     Py_RETURN_NONE;
1168   }
1169 
1170   return make_PyUnicode_from_ppd_string (self->ppd, self->option->text);
1171 }
1172 
1173 static PyObject *
Option_getUI(Option * self,void * closure)1174 Option_getUI (Option *self, void *closure)
1175 {
1176   if (!self->option) {
1177     Py_RETURN_NONE;
1178   }
1179 
1180   return PyLong_FromLong (self->option->ui);
1181 }
1182 
1183 static PyObject *
Option_getChoices(Option * self,void * closure)1184 Option_getChoices (Option *self, void *closure)
1185 {
1186   PyObject *choices = PyList_New (0);
1187   ppd_choice_t *choice;
1188   bool defchoice_seen = false;
1189   int i;
1190 
1191   if (!self->option)
1192     return choices;
1193 
1194   for (i = 0, choice = self->option->choices;
1195        i < self->option->num_choices;
1196        i++, choice++) {
1197     PyObject *choice_dict = PyDict_New ();
1198     PyObject *u;
1199 
1200     u = make_PyUnicode_from_ppd_string (self->ppd, choice->choice);
1201     PyDict_SetItemString (choice_dict, "choice", u);
1202     Py_DECREF (u);
1203 
1204     u = make_PyUnicode_from_ppd_string (self->ppd, choice->text);
1205     PyDict_SetItemString (choice_dict, "text", u);
1206     Py_DECREF (u);
1207 
1208     u = PyBool_FromLong (choice->marked);
1209     PyDict_SetItemString (choice_dict, "marked", u);
1210     Py_DECREF (u);
1211 
1212     PyList_Append (choices, choice_dict);
1213     if (!strcmp (choice->choice, self->option->defchoice))
1214       defchoice_seen = true;
1215   }
1216 
1217   if (!defchoice_seen) {
1218     // This PPD option has a default choice that isn't one of the choices.
1219     // This really happens.
1220     const char *defchoice = self->option->defchoice;
1221     PyObject *choice_dict = PyDict_New ();
1222     PyObject *u;
1223 
1224     u = make_PyUnicode_from_ppd_string (self->ppd, defchoice);
1225     PyDict_SetItemString (choice_dict, "choice", u);
1226     Py_DECREF (u);
1227 
1228     u = make_PyUnicode_from_ppd_string (self->ppd, defchoice);
1229     PyDict_SetItemString (choice_dict, "text", u);
1230     Py_DECREF (u);
1231 
1232     PyList_Append (choices, choice_dict);
1233   }
1234 
1235   return choices;
1236 }
1237 
1238 PyGetSetDef Option_getseters[] =
1239   {
1240     { "conflicted",
1241       (getter) Option_getConflicted, (setter) NULL,
1242       "Whether this option is in conflict", NULL },
1243 
1244     { "keyword",
1245       (getter) Option_getKeyword, (setter) NULL,
1246       "keyword", NULL },
1247 
1248     { "defchoice",
1249       (getter) Option_getDefchoice, (setter) NULL,
1250       "defchoice", NULL },
1251 
1252     { "text",
1253       (getter) Option_getText, (setter) NULL,
1254       "text", NULL },
1255 
1256     { "ui",
1257       (getter) Option_getUI, (setter) NULL,
1258       "ui", NULL },
1259 
1260     { "choices",
1261       (getter) Option_getChoices, (setter) NULL,
1262       "choices", NULL },
1263 
1264     { NULL }
1265   };
1266 
1267 PyMethodDef Option_methods[] =
1268   {
1269     { NULL } /* Sentinel */
1270   };
1271 
1272 PyTypeObject cups_OptionType =
1273   {
1274     PyVarObject_HEAD_INIT(NULL, 0)
1275     "cups.Option",             /*tp_name*/
1276     sizeof(Option),            /*tp_basicsize*/
1277     0,                         /*tp_itemsize*/
1278     (destructor)Option_dealloc, /*tp_dealloc*/
1279     0,                         /*tp_print*/
1280     0,                         /*tp_getattr*/
1281     0,                         /*tp_setattr*/
1282     0,                         /*tp_compare*/
1283     (reprfunc)Option_repr,     /*tp_repr*/
1284     0,                         /*tp_as_number*/
1285     0,                         /*tp_as_sequence*/
1286     0,                         /*tp_as_mapping*/
1287     0,                         /*tp_hash */
1288     0,                         /*tp_call*/
1289     0,                         /*tp_str*/
1290     0,                         /*tp_getattro*/
1291     0,                         /*tp_setattro*/
1292     0,                         /*tp_as_buffer*/
1293     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
1294     "PPD option\n"
1295     "==========\n"
1296     "  A PPD option.\n\n"
1297     "@type conflicted: boolean\n"
1298     "@ivar conflicted: whether this option is in conflict\n"
1299     "@type keyword: string\n"
1300     "@ivar keyword: the option keyword e.g. Duplex\n"
1301     "@type defchoice: string\n"
1302     "@ivar defchoice: the default option choice\n"
1303     "@type text: string\n"
1304     "@ivar text: the user-presentable option name e.g. Double-sided printing\n"
1305     "@type ui: integer\n"
1306     "@ivar ui: the option type; one of L{PPD_UI_BOOLEAN}, \n"
1307     "L{PPD_UI_PICKONE}, L{PPD_UI_PICKMANY}\n"
1308     "@type choices: list\n"
1309     "@ivar choices: list of the option's choices\n"
1310     "",                        /* tp_doc */
1311     0,                         /* tp_traverse */
1312     0,                         /* tp_clear */
1313     0,                         /* tp_richcompare */
1314     0,                         /* tp_weaklistoffset */
1315     0,                         /* tp_iter */
1316     0,                         /* tp_iternext */
1317     0,                         /* tp_methods */
1318     0,                         /* tp_members */
1319     Option_getseters,          /* tp_getset */
1320     0,                         /* tp_base */
1321     0,                         /* tp_dict */
1322     0,                         /* tp_descr_get */
1323     0,                         /* tp_descr_set */
1324     0,                         /* tp_dictoffset */
1325     (initproc)Option_init,     /* tp_init */
1326     0,                         /* tp_alloc */
1327     Option_new,                /* tp_new */
1328   };
1329 
1330 ///////////
1331 // Group //
1332 ///////////
1333 
1334 static PyObject *
Group_new(PyTypeObject * type,PyObject * args,PyObject * kwds)1335 Group_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
1336 {
1337   Group *self;
1338   self = (Group *) type->tp_alloc (type, 0);
1339   return (PyObject *) self;
1340 }
1341 
1342 static int
Group_init(Group * self,PyObject * args,PyObject * kwds)1343 Group_init (Group *self, PyObject *args, PyObject *kwds)
1344 {
1345   self->group = NULL;
1346   return 0;
1347 }
1348 
1349 static void
Group_dealloc(Group * self)1350 Group_dealloc (Group *self)
1351 {
1352   Py_XDECREF (self->ppd);
1353   ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
1354 }
1355 
1356 static PyObject *
Group_repr(Group * self)1357 Group_repr (Group *self)
1358 {
1359   ppd_group_t *group = self->group;
1360   if (!group)
1361     return PyUnicode_FromString ("<cups.Group>");
1362 
1363   char buffer[256];
1364   snprintf (buffer, 256, "<cups.Group %s>", group->name);
1365     return PyUnicode_FromString (buffer);
1366 }
1367 
1368 ///////////
1369 // Group // ATTRIBUTES
1370 ///////////
1371 
1372 static PyObject *
Group_getText(Group * self,void * closure)1373 Group_getText (Group *self, void *closure)
1374 {
1375   if (!self->group) {
1376     Py_RETURN_NONE;
1377   }
1378 
1379   return make_PyUnicode_from_ppd_string (self->ppd, self->group->text);
1380 }
1381 
1382 static PyObject *
Group_getName(Group * self,void * closure)1383 Group_getName (Group *self, void *closure)
1384 {
1385   if (!self->group) {
1386     Py_RETURN_NONE;
1387   }
1388 
1389   return make_PyUnicode_from_ppd_string (self->ppd, self->group->name);
1390 }
1391 
1392 static PyObject *
Group_getOptions(Group * self,void * closure)1393 Group_getOptions (Group *self, void *closure)
1394 {
1395   PyObject *options = PyList_New (0);
1396   ppd_option_t *option;
1397   int i;
1398 
1399   if (!self->group)
1400     return options;
1401 
1402   for (i = 0, option = self->group->options;
1403        i < self->group->num_options;
1404        i++, option++) {
1405     PyObject *args = Py_BuildValue ("()");
1406     PyObject *kwlist = Py_BuildValue ("{}");
1407     Option *opt = (Option *) PyType_GenericNew (&cups_OptionType,
1408 						args, kwlist);
1409     Py_DECREF (args);
1410     Py_DECREF (kwlist);
1411     opt->option = option;
1412     opt->ppd = self->ppd;
1413     Py_INCREF (self->ppd);
1414     PyList_Append (options, (PyObject *) opt);
1415   }
1416 
1417   return options;
1418 }
1419 
1420 static PyObject *
Group_getSubgroups(Group * self,void * closure)1421 Group_getSubgroups (Group *self, void *closure)
1422 {
1423   PyObject *subgroups = PyList_New (0);
1424   ppd_group_t *subgroup;
1425   int i;
1426 
1427   if (!self->group)
1428     return subgroups;
1429 
1430   for (i = 0, subgroup = self->group->subgroups;
1431        i < self->group->num_subgroups;
1432        i++, subgroup++) {
1433     PyObject *args = Py_BuildValue ("()");
1434     PyObject *kwlist = Py_BuildValue ("{}");
1435     Group *grp = (Group *) PyType_GenericNew (&cups_GroupType,
1436 					      args, kwlist);
1437     Py_DECREF (args);
1438     Py_DECREF (kwlist);
1439     grp->group = subgroup;
1440     grp->ppd = self->ppd;
1441     Py_INCREF (self->ppd);
1442     PyList_Append (subgroups, (PyObject *) grp);
1443   }
1444 
1445   return subgroups;
1446 }
1447 
1448 PyGetSetDef Group_getseters[] =
1449   {
1450     { "text",
1451       (getter) Group_getText, (setter) NULL,
1452       "text", NULL },
1453 
1454     { "name",
1455       (getter) Group_getName, (setter) NULL,
1456       "name", NULL },
1457 
1458     { "options",
1459       (getter) Group_getOptions, (setter) NULL,
1460       "options", NULL },
1461 
1462     { "subgroups",
1463       (getter) Group_getSubgroups, (setter) NULL,
1464       "subgroups", NULL },
1465 
1466     { NULL }
1467   };
1468 
1469 PyMethodDef Group_methods[] =
1470   {
1471     { NULL } /* Sentinel */
1472   };
1473 
1474 PyTypeObject cups_GroupType =
1475   {
1476     PyVarObject_HEAD_INIT(NULL, 0)
1477     "cups.Group",              /*tp_name*/
1478     sizeof(Group),             /*tp_basicsize*/
1479     0,                         /*tp_itemsize*/
1480     (destructor)Group_dealloc, /*tp_dealloc*/
1481     0,                         /*tp_print*/
1482     0,                         /*tp_getattr*/
1483     0,                         /*tp_setattr*/
1484     0,                         /*tp_compare*/
1485     (reprfunc)Group_repr,      /*tp_repr*/
1486     0,                         /*tp_as_number*/
1487     0,                         /*tp_as_sequence*/
1488     0,                         /*tp_as_mapping*/
1489     0,                         /*tp_hash */
1490     0,                         /*tp_call*/
1491     0,                         /*tp_str*/
1492     0,                         /*tp_getattro*/
1493     0,                         /*tp_setattro*/
1494     0,                         /*tp_as_buffer*/
1495     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
1496     "PPD option group\n"
1497     "================\n\n"
1498     "  A PPD option group.\n\n"
1499     "@type text: string\n"
1500     "@ivar text: user-presentable group name\n"
1501     "@type name: string\n"
1502     "@ivar name: unique group name\n"
1503     "@type options: L{Option} list\n"
1504     "@ivar options: list of options in the group\n"
1505     "@type subgroups: L{Group} list\n"
1506     "@ivar subgroups: list of subgroups in the group\n"
1507     "",                        /* tp_doc */
1508     0,                         /* tp_traverse */
1509     0,                         /* tp_clear */
1510     0,                         /* tp_richcompare */
1511     0,                         /* tp_weaklistoffset */
1512     0,                         /* tp_iter */
1513     0,                         /* tp_iternext */
1514     0,                         /* tp_methods */
1515     0,                         /* tp_members */
1516     Group_getseters,           /* tp_getset */
1517     0,                         /* tp_base */
1518     0,                         /* tp_dict */
1519     0,                         /* tp_descr_get */
1520     0,                         /* tp_descr_set */
1521     0,                         /* tp_dictoffset */
1522     (initproc)Group_init,      /* tp_init */
1523     0,                         /* tp_alloc */
1524     Group_new,                 /* tp_new */
1525   };
1526 
1527 ////////////////
1528 // Constraint //
1529 ////////////////
1530 
1531 static PyObject *
Constraint_new(PyTypeObject * type,PyObject * args,PyObject * kwds)1532 Constraint_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
1533 {
1534   Constraint *self;
1535   self = (Constraint *) type->tp_alloc (type, 0);
1536   return (PyObject *) self;
1537 }
1538 
1539 static int
Constraint_init(Constraint * self,PyObject * args,PyObject * kwds)1540 Constraint_init (Constraint *self, PyObject *args, PyObject *kwds)
1541 {
1542   self->constraint = NULL;
1543   return 0;
1544 }
1545 
1546 static void
Constraint_dealloc(Constraint * self)1547 Constraint_dealloc (Constraint *self)
1548 {
1549   Py_XDECREF (self->ppd);
1550   ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
1551 }
1552 
1553 ////////////////
1554 // Constraint // ATTRIBUTES
1555 ////////////////
1556 
1557 static PyObject *
Constraint_getOption1(Constraint * self,void * closure)1558 Constraint_getOption1 (Constraint *self, void *closure)
1559 {
1560   if (!self->constraint) {
1561     Py_RETURN_NONE;
1562   }
1563 
1564   return make_PyUnicode_from_ppd_string (self->ppd, self->constraint->option1);
1565 }
1566 
1567 static PyObject *
Constraint_getChoice1(Constraint * self,void * closure)1568 Constraint_getChoice1 (Constraint *self, void *closure)
1569 {
1570   if (!self->constraint) {
1571     Py_RETURN_NONE;
1572   }
1573 
1574   return make_PyUnicode_from_ppd_string (self->ppd, self->constraint->choice1);
1575 }
1576 
1577 static PyObject *
Constraint_getOption2(Constraint * self,void * closure)1578 Constraint_getOption2 (Constraint *self, void *closure)
1579 {
1580   if (!self->constraint) {
1581     Py_RETURN_NONE;
1582   }
1583 
1584   return make_PyUnicode_from_ppd_string (self->ppd, self->constraint->option2);
1585 }
1586 
1587 static PyObject *
Constraint_getChoice2(Constraint * self,void * closure)1588 Constraint_getChoice2 (Constraint *self, void *closure)
1589 {
1590   if (!self->constraint) {
1591     Py_RETURN_NONE;
1592   }
1593 
1594   return make_PyUnicode_from_ppd_string (self->ppd, self->constraint->choice2);
1595 }
1596 
1597 PyGetSetDef Constraint_getseters[] =
1598   {
1599     { "option1",
1600       (getter) Constraint_getOption1, (setter) NULL,
1601       "option1", NULL },
1602 
1603     { "choice1",
1604       (getter) Constraint_getChoice1, (setter) NULL,
1605       "choice1", NULL },
1606 
1607     { "option2",
1608       (getter) Constraint_getOption2, (setter) NULL,
1609       "option2", NULL },
1610 
1611     { "choice2",
1612       (getter) Constraint_getChoice2, (setter) NULL,
1613       "choice2", NULL },
1614 
1615     { NULL }
1616   };
1617 
1618 PyTypeObject cups_ConstraintType =
1619   {
1620     PyVarObject_HEAD_INIT(NULL, 0)
1621     "cups.Constraint",         /*tp_name*/
1622     sizeof(Constraint),        /*tp_basicsize*/
1623     0,                         /*tp_itemsize*/
1624     (destructor)Constraint_dealloc, /*tp_dealloc*/
1625     0,                         /*tp_print*/
1626     0,                         /*tp_getattr*/
1627     0,                         /*tp_setattr*/
1628     0,                         /*tp_compare*/
1629     0,                         /*tp_repr*/
1630     0,                         /*tp_as_number*/
1631     0,                         /*tp_as_sequence*/
1632     0,                         /*tp_as_mapping*/
1633     0,                         /*tp_hash */
1634     0,                         /*tp_call*/
1635     0,                         /*tp_str*/
1636     0,                         /*tp_getattro*/
1637     0,                         /*tp_setattro*/
1638     0,                         /*tp_as_buffer*/
1639     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
1640     "PPD constraint\n"
1641     "==============\n\n"
1642     "  A PPD constraint.\n\n"
1643     "@type option1: string\n"
1644     "@ivar option1: first option keyword\n"
1645     "@type choice1: string\n"
1646     "@ivar choice1: first option choice\n"
1647     "@type option2: string\n"
1648     "@ivar option2: second option keyword\n"
1649     "@type choice2: string\n"
1650     "@ivar choice2: secondoption choice\n"
1651     "",                        /* tp_doc */
1652     0,                         /* tp_traverse */
1653     0,                         /* tp_clear */
1654     0,                         /* tp_richcompare */
1655     0,                         /* tp_weaklistoffset */
1656     0,                         /* tp_iter */
1657     0,                         /* tp_iternext */
1658     0,                         /* tp_methods */
1659     0,                         /* tp_members */
1660     Constraint_getseters,      /* tp_getset */
1661     0,                         /* tp_base */
1662     0,                         /* tp_dict */
1663     0,                         /* tp_descr_get */
1664     0,                         /* tp_descr_set */
1665     0,                         /* tp_dictoffset */
1666     (initproc)Constraint_init, /* tp_init */
1667     0,                         /* tp_alloc */
1668     Constraint_new,            /* tp_new */
1669   };
1670 
1671 ///////////////
1672 // Attribute //
1673 ///////////////
1674 
1675 static PyObject *
Attribute_new(PyTypeObject * type,PyObject * args,PyObject * kwds)1676 Attribute_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
1677 {
1678   Attribute *self;
1679   self = (Attribute *) type->tp_alloc (type, 0);
1680   return (PyObject *) self;
1681 }
1682 
1683 static int
Attribute_init(Attribute * self,PyObject * args,PyObject * kwds)1684 Attribute_init (Attribute *self, PyObject *args, PyObject *kwds)
1685 {
1686   self->attribute = NULL;
1687   return 0;
1688 }
1689 
1690 static void
Attribute_dealloc(Attribute * self)1691 Attribute_dealloc (Attribute *self)
1692 {
1693   Py_XDECREF (self->ppd);
1694   ((PyObject *)self)->ob_type->tp_free ((PyObject *) self);
1695 }
1696 
1697 static PyObject *
Attribute_repr(Attribute * self)1698 Attribute_repr (Attribute *self)
1699 {
1700   ppd_attr_t *attribute = self->attribute;
1701   if (!attribute)
1702     return PyUnicode_FromString ("<cups.Attribute>");
1703 
1704   char buffer[256];
1705   snprintf (buffer, 256, "<cups.Attribute *%s%s%s>",
1706 			  attribute->name,
1707 			  attribute->spec[0] != '\0' ? " ": "",
1708 			  attribute->spec);
1709     return PyUnicode_FromString (buffer);
1710 }
1711 
1712 ///////////////
1713 // Attribute // ATTRIBUTES
1714 ///////////////
1715 
1716 static PyObject *
Attribute_getName(Attribute * self,void * closure)1717 Attribute_getName (Attribute *self, void *closure)
1718 {
1719   if (!self->attribute) {
1720     Py_RETURN_NONE;
1721   }
1722 
1723   return make_PyUnicode_from_ppd_string (self->ppd, self->attribute->name);
1724 }
1725 
1726 static PyObject *
Attribute_getSpec(Attribute * self,void * closure)1727 Attribute_getSpec (Attribute *self, void *closure)
1728 {
1729   if (!self->attribute) {
1730     Py_RETURN_NONE;
1731   }
1732 
1733   return make_PyUnicode_from_ppd_string (self->ppd, self->attribute->spec);
1734 }
1735 
1736 static PyObject *
Attribute_getText(Attribute * self,void * closure)1737 Attribute_getText (Attribute *self, void *closure)
1738 {
1739   if (!self->attribute) {
1740     Py_RETURN_NONE;
1741   }
1742 
1743   return make_PyUnicode_from_ppd_string (self->ppd, self->attribute->text);
1744 }
1745 
1746 static PyObject *
Attribute_getValue(Attribute * self,void * closure)1747 Attribute_getValue (Attribute *self, void *closure)
1748 {
1749   if (!self->attribute) {
1750     Py_RETURN_NONE;
1751   }
1752 
1753   return make_PyUnicode_from_ppd_string (self->ppd, self->attribute->value);
1754 }
1755 
1756 PyGetSetDef Attribute_getseters[] =
1757   {
1758     { "name",
1759       (getter) Attribute_getName, (setter) NULL,
1760       "name", NULL },
1761 
1762     { "spec",
1763       (getter) Attribute_getSpec, (setter) NULL,
1764       "spec", NULL },
1765 
1766     { "text",
1767       (getter) Attribute_getText, (setter) NULL,
1768       "text", NULL },
1769 
1770     { "value",
1771       (getter) Attribute_getValue, (setter) NULL,
1772       "value", NULL },
1773 
1774     { NULL }
1775   };
1776 
1777 PyTypeObject cups_AttributeType =
1778   {
1779     PyVarObject_HEAD_INIT(NULL, 0)
1780     "cups.Attribute",          /*tp_name*/
1781     sizeof(Attribute),         /*tp_basicsize*/
1782     0,                         /*tp_itemsize*/
1783     (destructor)Attribute_dealloc, /*tp_dealloc*/
1784     0,                         /*tp_print*/
1785     0,                         /*tp_getattr*/
1786     0,                         /*tp_setattr*/
1787     0,                         /*tp_compare*/
1788     (reprfunc)Attribute_repr,  /*tp_repr*/
1789     0,                         /*tp_as_number*/
1790     0,                         /*tp_as_sequence*/
1791     0,                         /*tp_as_mapping*/
1792     0,                         /*tp_hash */
1793     0,                         /*tp_call*/
1794     0,                         /*tp_str*/
1795     0,                         /*tp_getattro*/
1796     0,                         /*tp_setattro*/
1797     0,                         /*tp_as_buffer*/
1798     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
1799     "PPD attribute\n"
1800     "=============\n\n"
1801     "  A PPD attribute.\n\n"
1802     "@type name: string\n"
1803     "@ivar name: attribute name\n"
1804     "@type spec: string\n"
1805     "@ivar spec: specifier string (if any)\n"
1806     "@type text: string\n"
1807     "@ivar text: human-readable text (if any)\n"
1808     "@type value: string\n"
1809     "@ivar value: attribute value\n"
1810     "",                        /* tp_doc */
1811     0,                         /* tp_traverse */
1812     0,                         /* tp_clear */
1813     0,                         /* tp_richcompare */
1814     0,                         /* tp_weaklistoffset */
1815     0,                         /* tp_iter */
1816     0,                         /* tp_iternext */
1817     0,                         /* tp_methods */
1818     0,                         /* tp_members */
1819     Attribute_getseters,      /* tp_getset */
1820     0,                         /* tp_base */
1821     0,                         /* tp_dict */
1822     0,                         /* tp_descr_get */
1823     0,                         /* tp_descr_set */
1824     0,                         /* tp_dictoffset */
1825     (initproc)Attribute_init, /* tp_init */
1826     0,                         /* tp_alloc */
1827     Attribute_new,            /* tp_new */
1828   };
1829