]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/classes.c
Import CUPS v1.7.1
[thirdparty/cups.git] / scheduler / classes.c
1 /*
2 * "$Id: classes.c 10996 2013-05-29 11:51:34Z msweet $"
3 *
4 * Printer class routines for the CUPS scheduler.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 * cupsdAddClass() - Add a class to the system.
18 * cupsdAddPrinterToClass() - Add a printer to a class...
19 * cupsdDeletePrinterFromClass() - Delete a printer from a class.
20 * cupsdDeletePrinterFromClasses() - Delete a printer from all classes.
21 * cupsdFindAvailablePrinter() - Find an available printer in a class.
22 * cupsdFindClass() - Find the named class.
23 * cupsdLoadAllClasses() - Load classes from the classes.conf file.
24 * cupsdSaveAllClasses() - Save classes to the classes.conf file.
25 */
26
27 /*
28 * Include necessary headers...
29 */
30
31 #include "cupsd.h"
32
33
34 /*
35 * 'cupsdAddClass()' - Add a class to the system.
36 */
37
38 cupsd_printer_t * /* O - New class */
39 cupsdAddClass(const char *name) /* I - Name of class */
40 {
41 cupsd_printer_t *c; /* New class */
42 char uri[1024]; /* Class URI */
43
44
45 /*
46 * Add the printer and set the type to "class"...
47 */
48
49 if ((c = cupsdAddPrinter(name)) != NULL)
50 {
51 /*
52 * Change from a printer to a class...
53 */
54
55 c->type = CUPS_PRINTER_CLASS;
56
57 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
58 ServerName, RemotePort, "/classes/%s", name);
59 cupsdSetString(&c->uri, uri);
60
61 cupsdSetString(&c->error_policy, "retry-current-job");
62 }
63
64 return (c);
65 }
66
67
68 /*
69 * 'cupsdAddPrinterToClass()' - Add a printer to a class...
70 */
71
72 void
73 cupsdAddPrinterToClass(
74 cupsd_printer_t *c, /* I - Class to add to */
75 cupsd_printer_t *p) /* I - Printer to add */
76 {
77 int i; /* Looping var */
78 cupsd_printer_t **temp; /* Pointer to printer array */
79
80
81 /*
82 * See if this printer is already a member of the class...
83 */
84
85 for (i = 0; i < c->num_printers; i ++)
86 if (c->printers[i] == p)
87 return;
88
89 /*
90 * Allocate memory as needed...
91 */
92
93 if (c->num_printers == 0)
94 temp = malloc(sizeof(cupsd_printer_t *));
95 else
96 temp = realloc(c->printers, sizeof(cupsd_printer_t *) * (c->num_printers + 1));
97
98 if (temp == NULL)
99 {
100 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add printer %s to class %s!",
101 p->name, c->name);
102 return;
103 }
104
105 /*
106 * Add the printer to the end of the array and update the number of printers.
107 */
108
109 c->printers = temp;
110 temp += c->num_printers;
111 c->num_printers ++;
112
113 *temp = p;
114 }
115
116
117 /*
118 * 'cupsdDeletePrinterFromClass()' - Delete a printer from a class.
119 */
120
121 int /* O - 1 if class changed, 0 otherwise */
122 cupsdDeletePrinterFromClass(
123 cupsd_printer_t *c, /* I - Class to delete from */
124 cupsd_printer_t *p) /* I - Printer to delete */
125 {
126 int i; /* Looping var */
127
128
129 /*
130 * See if the printer is in the class...
131 */
132
133 for (i = 0; i < c->num_printers; i ++)
134 if (p == c->printers[i])
135 break;
136
137 /*
138 * If it is, remove it from the list...
139 */
140
141 if (i < c->num_printers)
142 {
143 /*
144 * Yes, remove the printer...
145 */
146
147 c->num_printers --;
148 if (i < c->num_printers)
149 memmove(c->printers + i, c->printers + i + 1,
150 (c->num_printers - i) * sizeof(cupsd_printer_t *));
151 }
152 else
153 return (0);
154
155 /*
156 * Update the IPP attributes (have to do this for member-names)...
157 */
158
159 cupsdSetPrinterAttrs(c);
160
161 return (1);
162 }
163
164
165 /*
166 * 'cupsdDeletePrinterFromClasses()' - Delete a printer from all classes.
167 */
168
169 int /* O - 1 if class changed, 0 otherwise */
170 cupsdDeletePrinterFromClasses(
171 cupsd_printer_t *p) /* I - Printer to delete */
172 {
173 int changed = 0; /* Any class changed? */
174 cupsd_printer_t *c; /* Pointer to current class */
175
176
177 /*
178 * Loop through the printer/class list and remove the printer
179 * from each class listed...
180 */
181
182 for (c = (cupsd_printer_t *)cupsArrayFirst(Printers);
183 c;
184 c = (cupsd_printer_t *)cupsArrayNext(Printers))
185 if (c->type & CUPS_PRINTER_CLASS)
186 changed |= cupsdDeletePrinterFromClass(c, p);
187
188 return (changed);
189 }
190
191
192 /*
193 * 'cupsdFindAvailablePrinter()' - Find an available printer in a class.
194 */
195
196 cupsd_printer_t * /* O - Available printer or NULL */
197 cupsdFindAvailablePrinter(
198 const char *name) /* I - Class to check */
199 {
200 int i; /* Looping var */
201 cupsd_printer_t *c; /* Printer class */
202
203
204 /*
205 * Find the class...
206 */
207
208 if ((c = cupsdFindClass(name)) == NULL)
209 {
210 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to find class \"%s\"!", name);
211 return (NULL);
212 }
213
214 if (c->num_printers == 0)
215 return (NULL);
216
217 /*
218 * Make sure that the last printer is also a valid index into the printer
219 * array. If not, reset the last printer to 0...
220 */
221
222 if (c->last_printer >= c->num_printers)
223 c->last_printer = 0;
224
225 /*
226 * Loop through the printers in the class and return the first idle
227 * printer... We keep track of the last printer that we used so that
228 * a "round robin" type of scheduling is realized (otherwise the first
229 * server might be saturated with print jobs...)
230 *
231 * Thanks to Joel Fredrikson for helping us get this right!
232 */
233
234 for (i = c->last_printer + 1; ; i ++)
235 {
236 if (i >= c->num_printers)
237 i = 0;
238
239 if (c->printers[i]->accepting &&
240 (c->printers[i]->state == IPP_PRINTER_IDLE ||
241 ((c->printers[i]->type & CUPS_PRINTER_REMOTE) && !c->printers[i]->job)))
242 {
243 c->last_printer = i;
244 return (c->printers[i]);
245 }
246
247 if (i == c->last_printer)
248 break;
249 }
250
251 return (NULL);
252 }
253
254
255 /*
256 * 'cupsdFindClass()' - Find the named class.
257 */
258
259 cupsd_printer_t * /* O - Matching class or NULL */
260 cupsdFindClass(const char *name) /* I - Name of class */
261 {
262 cupsd_printer_t *c; /* Current class/printer */
263
264
265 if ((c = cupsdFindDest(name)) != NULL && (c->type & CUPS_PRINTER_CLASS))
266 return (c);
267 else
268 return (NULL);
269 }
270
271
272 /*
273 * 'cupsdLoadAllClasses()' - Load classes from the classes.conf file.
274 */
275
276 void
277 cupsdLoadAllClasses(void)
278 {
279 int i; /* Looping var */
280 cups_file_t *fp; /* classes.conf file */
281 int linenum; /* Current line number */
282 char line[4096], /* Line from file */
283 *value, /* Pointer to value */
284 *valueptr; /* Pointer into value */
285 cupsd_printer_t *p, /* Current printer class */
286 *temp; /* Temporary pointer to printer */
287
288
289 /*
290 * Open the classes.conf file...
291 */
292
293 snprintf(line, sizeof(line), "%s/classes.conf", ServerRoot);
294 if ((fp = cupsdOpenConfFile(line)) == NULL)
295 return;
296
297 /*
298 * Read class configurations until we hit EOF...
299 */
300
301 linenum = 0;
302 p = NULL;
303
304 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
305 {
306 /*
307 * Decode the directive...
308 */
309
310 if (!_cups_strcasecmp(line, "<Class") ||
311 !_cups_strcasecmp(line, "<DefaultClass"))
312 {
313 /*
314 * <Class name> or <DefaultClass name>
315 */
316
317 if (p == NULL && value)
318 {
319 cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
320
321 /*
322 * Since prior classes may have implicitly defined this class,
323 * see if it already exists...
324 */
325
326 if ((p = cupsdFindDest(value)) != NULL)
327 {
328 p->type = CUPS_PRINTER_CLASS;
329 cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
330 LocalPort, value);
331 cupsdSetString(&p->error_policy, "retry-job");
332 }
333 else
334 p = cupsdAddClass(value);
335
336 p->accepting = 1;
337 p->state = IPP_PRINTER_IDLE;
338
339 if (!_cups_strcasecmp(line, "<DefaultClass"))
340 DefaultPrinter = p;
341 }
342 else
343 cupsdLogMessage(CUPSD_LOG_ERROR,
344 "Syntax error on line %d of classes.conf.", linenum);
345 }
346 else if (!_cups_strcasecmp(line, "</Class>"))
347 {
348 if (p != NULL)
349 {
350 cupsdSetPrinterAttrs(p);
351 p = NULL;
352 }
353 else
354 cupsdLogMessage(CUPSD_LOG_ERROR,
355 "Syntax error on line %d of classes.conf.", linenum);
356 }
357 else if (!p)
358 {
359 cupsdLogMessage(CUPSD_LOG_ERROR,
360 "Syntax error on line %d of classes.conf.", linenum);
361 }
362 else if (!_cups_strcasecmp(line, "UUID"))
363 {
364 if (value && !strncmp(value, "urn:uuid:", 9))
365 cupsdSetString(&(p->uuid), value);
366 else
367 cupsdLogMessage(CUPSD_LOG_ERROR,
368 "Bad UUID on line %d of classes.conf.", linenum);
369 }
370 else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
371 {
372 if (!cupsdSetAuthInfoRequired(p, value, NULL))
373 cupsdLogMessage(CUPSD_LOG_ERROR,
374 "Bad AuthInfoRequired on line %d of classes.conf.",
375 linenum);
376 }
377 else if (!_cups_strcasecmp(line, "Info"))
378 {
379 if (value)
380 cupsdSetString(&p->info, value);
381 }
382 else if (!_cups_strcasecmp(line, "Location"))
383 {
384 if (value)
385 cupsdSetString(&p->location, value);
386 }
387 else if (!_cups_strcasecmp(line, "Option") && value)
388 {
389 /*
390 * Option name value
391 */
392
393 for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
394
395 if (!*valueptr)
396 cupsdLogMessage(CUPSD_LOG_ERROR,
397 "Syntax error on line %d of classes.conf.", linenum);
398 else
399 {
400 for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
401
402 p->num_options = cupsAddOption(value, valueptr, p->num_options,
403 &(p->options));
404 }
405 }
406 else if (!_cups_strcasecmp(line, "Printer"))
407 {
408 if (!value)
409 {
410 cupsdLogMessage(CUPSD_LOG_ERROR,
411 "Syntax error on line %d of classes.conf.", linenum);
412 continue;
413 }
414 else if ((temp = cupsdFindPrinter(value)) == NULL)
415 {
416 cupsdLogMessage(CUPSD_LOG_WARN,
417 "Unknown printer %s on line %d of classes.conf.",
418 value, linenum);
419
420 /*
421 * Add the missing remote printer...
422 */
423
424 if ((temp = cupsdAddPrinter(value)) != NULL)
425 {
426 cupsdSetString(&temp->make_model, "Remote Printer on unknown");
427
428 temp->state = IPP_PRINTER_STOPPED;
429 temp->type |= CUPS_PRINTER_REMOTE;
430
431 cupsdSetString(&temp->location, "Location Unknown");
432 cupsdSetString(&temp->info, "No Information Available");
433 temp->hostname[0] = '\0';
434
435 cupsdSetPrinterAttrs(temp);
436 }
437 }
438
439 if (temp)
440 cupsdAddPrinterToClass(p, temp);
441 }
442 else if (!_cups_strcasecmp(line, "State"))
443 {
444 /*
445 * Set the initial queue state...
446 */
447
448 if (!_cups_strcasecmp(value, "idle"))
449 p->state = IPP_PRINTER_IDLE;
450 else if (!_cups_strcasecmp(value, "stopped"))
451 {
452 p->state = IPP_PRINTER_STOPPED;
453
454 for (i = 0 ; i < p->num_reasons; i ++)
455 if (!strcmp("paused", p->reasons[i]))
456 break;
457
458 if (i >= p->num_reasons &&
459 p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
460 {
461 p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
462 p->num_reasons ++;
463 }
464 }
465 else
466 cupsdLogMessage(CUPSD_LOG_ERROR,
467 "Syntax error on line %d of classes.conf.",
468 linenum);
469 }
470 else if (!_cups_strcasecmp(line, "StateMessage"))
471 {
472 /*
473 * Set the initial queue state message...
474 */
475
476 if (value)
477 strlcpy(p->state_message, value, sizeof(p->state_message));
478 }
479 else if (!_cups_strcasecmp(line, "StateTime"))
480 {
481 /*
482 * Set the state time...
483 */
484
485 if (value)
486 p->state_time = atoi(value);
487 }
488 else if (!_cups_strcasecmp(line, "Accepting"))
489 {
490 /*
491 * Set the initial accepting state...
492 */
493
494 if (value &&
495 (!_cups_strcasecmp(value, "yes") ||
496 !_cups_strcasecmp(value, "on") ||
497 !_cups_strcasecmp(value, "true")))
498 p->accepting = 1;
499 else if (value &&
500 (!_cups_strcasecmp(value, "no") ||
501 !_cups_strcasecmp(value, "off") ||
502 !_cups_strcasecmp(value, "false")))
503 p->accepting = 0;
504 else
505 cupsdLogMessage(CUPSD_LOG_ERROR,
506 "Syntax error on line %d of classes.conf.",
507 linenum);
508 }
509 else if (!_cups_strcasecmp(line, "Shared"))
510 {
511 /*
512 * Set the initial shared state...
513 */
514
515 if (value &&
516 (!_cups_strcasecmp(value, "yes") ||
517 !_cups_strcasecmp(value, "on") ||
518 !_cups_strcasecmp(value, "true")))
519 p->shared = 1;
520 else if (value &&
521 (!_cups_strcasecmp(value, "no") ||
522 !_cups_strcasecmp(value, "off") ||
523 !_cups_strcasecmp(value, "false")))
524 p->shared = 0;
525 else
526 cupsdLogMessage(CUPSD_LOG_ERROR,
527 "Syntax error on line %d of classes.conf.",
528 linenum);
529 }
530 else if (!_cups_strcasecmp(line, "JobSheets"))
531 {
532 /*
533 * Set the initial job sheets...
534 */
535
536 if (value)
537 {
538 for (valueptr = value;
539 *valueptr && !isspace(*valueptr & 255);
540 valueptr ++);
541
542 if (*valueptr)
543 *valueptr++ = '\0';
544
545 cupsdSetString(&p->job_sheets[0], value);
546
547 while (isspace(*valueptr & 255))
548 valueptr ++;
549
550 if (*valueptr)
551 {
552 for (value = valueptr;
553 *valueptr && !isspace(*valueptr & 255);
554 valueptr ++);
555
556 if (*valueptr)
557 *valueptr = '\0';
558
559 cupsdSetString(&p->job_sheets[1], value);
560 }
561 }
562 else
563 cupsdLogMessage(CUPSD_LOG_ERROR,
564 "Syntax error on line %d of classes.conf.", linenum);
565 }
566 else if (!_cups_strcasecmp(line, "AllowUser"))
567 {
568 if (value)
569 {
570 p->deny_users = 0;
571 cupsdAddString(&(p->users), value);
572 }
573 else
574 cupsdLogMessage(CUPSD_LOG_ERROR,
575 "Syntax error on line %d of classes.conf.", linenum);
576 }
577 else if (!_cups_strcasecmp(line, "DenyUser"))
578 {
579 if (value)
580 {
581 p->deny_users = 1;
582 cupsdAddString(&(p->users), value);
583 }
584 else
585 cupsdLogMessage(CUPSD_LOG_ERROR,
586 "Syntax error on line %d of classes.conf.", linenum);
587 }
588 else if (!_cups_strcasecmp(line, "QuotaPeriod"))
589 {
590 if (value)
591 p->quota_period = atoi(value);
592 else
593 cupsdLogMessage(CUPSD_LOG_ERROR,
594 "Syntax error on line %d of classes.conf.", linenum);
595 }
596 else if (!_cups_strcasecmp(line, "PageLimit"))
597 {
598 if (value)
599 p->page_limit = atoi(value);
600 else
601 cupsdLogMessage(CUPSD_LOG_ERROR,
602 "Syntax error on line %d of classes.conf.", linenum);
603 }
604 else if (!_cups_strcasecmp(line, "KLimit"))
605 {
606 if (value)
607 p->k_limit = atoi(value);
608 else
609 cupsdLogMessage(CUPSD_LOG_ERROR,
610 "Syntax error on line %d of classes.conf.", linenum);
611 }
612 else if (!_cups_strcasecmp(line, "OpPolicy"))
613 {
614 if (value)
615 {
616 cupsd_policy_t *pol; /* Policy */
617
618
619 if ((pol = cupsdFindPolicy(value)) != NULL)
620 {
621 cupsdSetString(&p->op_policy, value);
622 p->op_policy_ptr = pol;
623 }
624 else
625 cupsdLogMessage(CUPSD_LOG_ERROR,
626 "Bad policy \"%s\" on line %d of classes.conf",
627 value, linenum);
628 }
629 else
630 cupsdLogMessage(CUPSD_LOG_ERROR,
631 "Syntax error on line %d of classes.conf.", linenum);
632 }
633 else if (!_cups_strcasecmp(line, "ErrorPolicy"))
634 {
635 if (value)
636 {
637 if (strcmp(value, "retry-current-job") && strcmp(value, "retry-job"))
638 cupsdLogMessage(CUPSD_LOG_WARN,
639 "ErrorPolicy %s ignored on line %d of classes.conf",
640 value, linenum);
641 }
642 else
643 cupsdLogMessage(CUPSD_LOG_ERROR,
644 "Syntax error on line %d of classes.conf.", linenum);
645 }
646 else
647 {
648 /*
649 * Something else we don't understand...
650 */
651
652 cupsdLogMessage(CUPSD_LOG_ERROR,
653 "Unknown configuration directive %s on line %d of classes.conf.",
654 line, linenum);
655 }
656 }
657
658 cupsFileClose(fp);
659 }
660
661
662 /*
663 * 'cupsdSaveAllClasses()' - Save classes to the classes.conf file.
664 */
665
666 void
667 cupsdSaveAllClasses(void)
668 {
669 cups_file_t *fp; /* classes.conf file */
670 char filename[1024], /* classes.conf filename */
671 temp[1024], /* Temporary string */
672 value[2048], /* Value string */
673 *name; /* Current user name */
674 cupsd_printer_t *pclass; /* Current printer class */
675 int i; /* Looping var */
676 time_t curtime; /* Current time */
677 struct tm *curdate; /* Current date */
678 cups_option_t *option; /* Current option */
679
680
681 /*
682 * Create the classes.conf file...
683 */
684
685 snprintf(filename, sizeof(filename), "%s/classes.conf", ServerRoot);
686
687 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
688 return;
689
690 cupsdLogMessage(CUPSD_LOG_INFO, "Saving classes.conf...");
691
692 /*
693 * Write a small header to the file...
694 */
695
696 curtime = time(NULL);
697 curdate = localtime(&curtime);
698 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
699
700 cupsFilePuts(fp, "# Class configuration file for " CUPS_SVERSION "\n");
701 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
702 cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
703
704 /*
705 * Write each local class known to the system...
706 */
707
708 for (pclass = (cupsd_printer_t *)cupsArrayFirst(Printers);
709 pclass;
710 pclass = (cupsd_printer_t *)cupsArrayNext(Printers))
711 {
712 /*
713 * Skip remote destinations and regular printers...
714 */
715
716 if ((pclass->type & CUPS_PRINTER_REMOTE) ||
717 !(pclass->type & CUPS_PRINTER_CLASS))
718 continue;
719
720 /*
721 * Write printers as needed...
722 */
723
724 if (pclass == DefaultPrinter)
725 cupsFilePrintf(fp, "<DefaultClass %s>\n", pclass->name);
726 else
727 cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
728
729 cupsFilePrintf(fp, "UUID %s\n", pclass->uuid);
730
731 if (pclass->num_auth_info_required > 0)
732 {
733 switch (pclass->num_auth_info_required)
734 {
735 case 1 :
736 strlcpy(value, pclass->auth_info_required[0], sizeof(value));
737 break;
738
739 case 2 :
740 snprintf(value, sizeof(value), "%s,%s",
741 pclass->auth_info_required[0],
742 pclass->auth_info_required[1]);
743 break;
744
745 case 3 :
746 default :
747 snprintf(value, sizeof(value), "%s,%s,%s",
748 pclass->auth_info_required[0],
749 pclass->auth_info_required[1],
750 pclass->auth_info_required[2]);
751 break;
752 }
753
754 cupsFilePutConf(fp, "AuthInfoRequired", value);
755 }
756
757 if (pclass->info)
758 cupsFilePutConf(fp, "Info", pclass->info);
759
760 if (pclass->location)
761 cupsFilePutConf(fp, "Location", pclass->location);
762
763 if (pclass->state == IPP_PRINTER_STOPPED)
764 cupsFilePuts(fp, "State Stopped\n");
765 else
766 cupsFilePuts(fp, "State Idle\n");
767
768 cupsFilePrintf(fp, "StateTime %d\n", (int)pclass->state_time);
769
770 if (pclass->accepting)
771 cupsFilePuts(fp, "Accepting Yes\n");
772 else
773 cupsFilePuts(fp, "Accepting No\n");
774
775 if (pclass->shared)
776 cupsFilePuts(fp, "Shared Yes\n");
777 else
778 cupsFilePuts(fp, "Shared No\n");
779
780 snprintf(value, sizeof(value), "%s %s", pclass->job_sheets[0],
781 pclass->job_sheets[1]);
782 cupsFilePutConf(fp, "JobSheets", value);
783
784 for (i = 0; i < pclass->num_printers; i ++)
785 cupsFilePrintf(fp, "Printer %s\n", pclass->printers[i]->name);
786
787 cupsFilePrintf(fp, "QuotaPeriod %d\n", pclass->quota_period);
788 cupsFilePrintf(fp, "PageLimit %d\n", pclass->page_limit);
789 cupsFilePrintf(fp, "KLimit %d\n", pclass->k_limit);
790
791 for (name = (char *)cupsArrayFirst(pclass->users);
792 name;
793 name = (char *)cupsArrayNext(pclass->users))
794 cupsFilePutConf(fp, pclass->deny_users ? "DenyUser" : "AllowUser", name);
795
796 if (pclass->op_policy)
797 cupsFilePutConf(fp, "OpPolicy", pclass->op_policy);
798 if (pclass->error_policy)
799 cupsFilePutConf(fp, "ErrorPolicy", pclass->error_policy);
800
801 for (i = pclass->num_options, option = pclass->options;
802 i > 0;
803 i --, option ++)
804 {
805 snprintf(value, sizeof(value), "%s %s", option->name, option->value);
806 cupsFilePutConf(fp, "Option", value);
807 }
808
809 cupsFilePuts(fp, "</Class>\n");
810 }
811
812 cupsdCloseCreatedConfFile(fp, filename);
813 }
814
815
816 /*
817 * End of "$Id: classes.c 10996 2013-05-29 11:51:34Z msweet $".
818 */