]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/type.c
Merge changes from CUPS 1.4svn-r7961.
[thirdparty/cups.git] / scheduler / type.c
1 /*
2 * "$Id: type.c 7720 2008-07-11 22:46:21Z mike $"
3 *
4 * MIME typing routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2006 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 * mimeAddType() - Add a MIME type to a database.
18 * mimeAddTypeRule() - Add a detection rule for a file type.
19 * mimeFileType() - Determine the type of a file.
20 * mimeType() - Lookup a file type.
21 * compare_types() - Compare two MIME super/type names.
22 * checkrules() - Check each rule in a list.
23 * patmatch() - Pattern matching...
24 */
25
26 /*
27 * Include necessary headers...
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #include <locale.h>
34
35 #include <cups/string.h>
36 #include "mime.h"
37 #include <cups/debug.h>
38
39
40 /*
41 * Local types...
42 */
43
44 typedef struct _mime_filebuf_s /**** File buffer for MIME typing ****/
45 {
46 cups_file_t *fp; /* File pointer */
47 int offset, /* Offset in file */
48 length; /* Length of buffered data */
49 unsigned char buffer[MIME_MAX_BUFFER];/* Buffered data */
50 } _mime_filebuf_t;
51
52
53 /*
54 * Local functions...
55 */
56
57 static int compare_types(mime_type_t *t0, mime_type_t *t1);
58 static int checkrules(const char *filename, _mime_filebuf_t *fb,
59 mime_magic_t *rules);
60 static int patmatch(const char *s, const char *pat);
61
62
63 /*
64 * 'mimeAddType()' - Add a MIME type to a database.
65 */
66
67 mime_type_t * /* O - New (or existing) MIME type */
68 mimeAddType(mime_t *mime, /* I - MIME database */
69 const char *super, /* I - Super-type name */
70 const char *type) /* I - Type name */
71 {
72 mime_type_t *temp; /* New MIME type */
73
74
75 /*
76 * Range check input...
77 */
78
79 if (!mime || !super || !type)
80 return (NULL);
81
82 /*
83 * See if the type already exists; if so, return the existing type...
84 */
85
86 if ((temp = mimeType(mime, super, type)) != NULL)
87 return (temp);
88
89 /*
90 * The type doesn't exist; add it...
91 */
92
93 if (!mime->types)
94 mime->types = cupsArrayNew((cups_array_func_t)compare_types, NULL);
95
96 if (!mime->types)
97 return (NULL);
98
99 if ((temp = calloc(1, sizeof(mime_type_t) - MIME_MAX_TYPE +
100 strlen(type) + 1)) == NULL)
101 return (NULL);
102
103 strlcpy(temp->super, super, sizeof(temp->super));
104 strcpy(temp->type, type); /* Safe: temp->type is allocated */
105 temp->priority = 100;
106
107 cupsArrayAdd(mime->types, temp);
108
109 return (temp);
110 }
111
112
113 /*
114 * 'mimeAddTypeRule()' - Add a detection rule for a file type.
115 */
116
117 int /* O - 0 on success, -1 on failure */
118 mimeAddTypeRule(mime_type_t *mt, /* I - Type to add to */
119 const char *rule) /* I - Rule to add */
120 {
121 int num_values, /* Number of values seen */
122 op, /* Operation code */
123 logic, /* Logic for next rule */
124 invert; /* Invert following rule? */
125 char name[255], /* Name in rule string */
126 value[3][255], /* Value in rule string */
127 *ptr, /* Position in name or value */
128 quote; /* Quote character */
129 int length[3]; /* Length of each parameter */
130 mime_magic_t *temp, /* New rule */
131 *current; /* Current rule */
132
133
134 /*
135 * Range check input...
136 */
137
138 if (!mt || !rule)
139 return (-1);
140
141 /*
142 * Find the last rule in the top-level of the rules tree.
143 */
144
145 for (current = mt->rules; current; current = current->next)
146 if (!current->next)
147 break;
148
149 /*
150 * Parse the rules string. Most rules are either a file extension or a
151 * comparison function:
152 *
153 * extension
154 * function(parameters)
155 */
156
157 logic = MIME_MAGIC_NOP;
158 invert = 0;
159
160 DEBUG_printf(("mimeAddTypeRule: %s/%s: %s\n", mt->super, mt->type, rule));
161
162 while (*rule != '\0')
163 {
164 while (isspace(*rule & 255))
165 rule ++;
166
167 if (*rule == '(')
168 {
169 DEBUG_puts("mimeAddTypeRule: New parenthesis group");
170 logic = MIME_MAGIC_NOP;
171 rule ++;
172 }
173 else if (*rule == ')')
174 {
175 DEBUG_puts("mimeAddTypeRule: Close paren...");
176 if (current == NULL || current->parent == NULL)
177 return (-1);
178
179 current = current->parent;
180
181 if (current->parent == NULL)
182 logic = MIME_MAGIC_OR;
183 else
184 logic = current->parent->op;
185
186 rule ++;
187 }
188 else if (*rule == '+' && current != NULL)
189 {
190 if (logic != MIME_MAGIC_AND &&
191 current != NULL && current->prev != NULL &&
192 current->prev->prev != NULL)
193 {
194 /*
195 * OK, we have more than 1 rule in the current tree level... Make a
196 * new group tree and move the previous rule to it...
197 */
198
199 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
200 return (-1);
201
202 temp->op = MIME_MAGIC_AND;
203 temp->child = current;
204 temp->parent = current->parent;
205 current->prev->next = temp;
206 temp->prev = current->prev;
207
208 current->prev = NULL;
209 current->parent = temp;
210
211 DEBUG_printf(("mimeAddTypeRule: Creating new AND group %p...\n", temp));
212 }
213 else
214 {
215 DEBUG_printf(("mimeAddTypeRule: Setting group %p op to AND...\n",
216 current->parent));
217 current->parent->op = MIME_MAGIC_AND;
218 }
219
220 logic = MIME_MAGIC_AND;
221 rule ++;
222 }
223 else if (*rule == ',')
224 {
225 if (logic != MIME_MAGIC_OR && current != NULL)
226 {
227 /*
228 * OK, we have two possibilities; either this is the top-level rule or
229 * we have a bunch of AND rules at this level.
230 */
231
232 if (current->parent == NULL)
233 {
234 /*
235 * This is the top-level rule; we have to move *all* of the AND rules
236 * down a level, as AND has precedence over OR.
237 */
238
239 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
240 return (-1);
241
242 DEBUG_printf(("mimeAddTypeRule: Creating new AND group %p inside OR "
243 "group\n", temp));
244
245 while (current->prev != NULL)
246 {
247 current->parent = temp;
248 current = current->prev;
249 }
250
251 current->parent = temp;
252 temp->op = MIME_MAGIC_AND;
253 temp->child = current;
254
255 mt->rules = current = temp;
256 }
257 else
258 {
259 /*
260 * This isn't the top rule, so go up one level...
261 */
262
263 DEBUG_puts("mimeAddTypeRule: Going up one level");
264 current = current->parent;
265 }
266 }
267
268 logic = MIME_MAGIC_OR;
269 rule ++;
270 }
271 else if (*rule == '!')
272 {
273 DEBUG_puts("mimeAddTypeRule: NOT");
274 invert = 1;
275 rule ++;
276 }
277 else if (isalnum(*rule & 255))
278 {
279 /*
280 * Read an extension name or a function...
281 */
282
283 ptr = name;
284 while (isalnum(*rule & 255) && (ptr - name) < (sizeof(name) - 1))
285 *ptr++ = *rule++;
286
287 *ptr = '\0';
288
289 if (*rule == '(')
290 {
291 /*
292 * Read function parameters...
293 */
294
295 rule ++;
296 for (num_values = 0;
297 num_values < (sizeof(value) / sizeof(value[0]));
298 num_values ++)
299 {
300 ptr = value[num_values];
301
302 while ((ptr - value[num_values]) < (sizeof(value[0]) - 1) &&
303 *rule != '\0' && *rule != ',' && *rule != ')')
304 {
305 if (isspace(*rule & 255))
306 {
307 /*
308 * Ignore whitespace...
309 */
310
311 rule ++;
312 continue;
313 }
314 else if (*rule == '\"' || *rule == '\'')
315 {
316 /*
317 * Copy quoted strings literally...
318 */
319
320 quote = *rule++;
321
322 while (*rule != '\0' && *rule != quote &&
323 (ptr - value[num_values]) < (sizeof(value[0]) - 1))
324 *ptr++ = *rule++;
325
326 if (*rule == quote)
327 rule ++;
328 else
329 return (-1);
330 }
331 else if (*rule == '<')
332 {
333 rule ++;
334
335 while (*rule != '>' && *rule != '\0' &&
336 (ptr - value[num_values]) < (sizeof(value[0]) - 1))
337 {
338 if (isxdigit(rule[0] & 255) && isxdigit(rule[1] & 255))
339 {
340 if (isdigit(*rule))
341 *ptr = (*rule++ - '0') << 4;
342 else
343 *ptr = (tolower(*rule++) - 'a' + 10) << 4;
344
345 if (isdigit(*rule))
346 *ptr++ |= *rule++ - '0';
347 else
348 *ptr++ |= tolower(*rule++) - 'a' + 10;
349 }
350 else
351 return (-1);
352 }
353
354 if (*rule == '>')
355 rule ++;
356 else
357 return (-1);
358 }
359 else
360 *ptr++ = *rule++;
361 }
362
363 *ptr = '\0';
364 length[num_values] = ptr - value[num_values];
365
366 if (*rule != ',')
367 break;
368
369 rule ++;
370 }
371
372 if (*rule != ')')
373 return (-1);
374
375 rule ++;
376
377 /*
378 * Figure out the function...
379 */
380
381 if (!strcmp(name, "match"))
382 op = MIME_MAGIC_MATCH;
383 else if (!strcmp(name, "ascii"))
384 op = MIME_MAGIC_ASCII;
385 else if (!strcmp(name, "printable"))
386 op = MIME_MAGIC_PRINTABLE;
387 else if (!strcmp(name, "string"))
388 op = MIME_MAGIC_STRING;
389 else if (!strcmp(name, "istring"))
390 op = MIME_MAGIC_ISTRING;
391 else if (!strcmp(name, "char"))
392 op = MIME_MAGIC_CHAR;
393 else if (!strcmp(name, "short"))
394 op = MIME_MAGIC_SHORT;
395 else if (!strcmp(name, "int"))
396 op = MIME_MAGIC_INT;
397 else if (!strcmp(name, "locale"))
398 op = MIME_MAGIC_LOCALE;
399 else if (!strcmp(name, "contains"))
400 op = MIME_MAGIC_CONTAINS;
401 else if (!strcmp(name, "priority") && num_values == 1)
402 {
403 mt->priority = atoi(value[0]);
404 continue;
405 }
406 else
407 return (-1);
408 }
409 else
410 {
411 /*
412 * This is just a filename match on the extension...
413 */
414
415 snprintf(value[0], sizeof(value[0]), "*.%s", name);
416 length[0] = strlen(value[0]);
417 op = MIME_MAGIC_MATCH;
418 }
419
420 /*
421 * Add a rule for this operation.
422 */
423
424 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
425 return (-1);
426
427 temp->invert = invert;
428 if (current != NULL)
429 {
430 temp->parent = current->parent;
431 current->next = temp;
432 }
433 else
434 mt->rules = temp;
435
436 temp->prev = current;
437
438 if (logic == MIME_MAGIC_NOP)
439 {
440 /*
441 * Add parenthetical grouping...
442 */
443
444 DEBUG_printf(("mimeAddTypeRule: Making new OR group %p for "
445 "parenthesis...\n", temp));
446
447 temp->op = MIME_MAGIC_OR;
448
449 if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
450 return (-1);
451
452 temp->child->parent = temp;
453
454 temp = temp->child;
455 logic = MIME_MAGIC_OR;
456 }
457
458 DEBUG_printf(("mimeAddTypeRule: adding %p: %s, op=%d, logic=%d, "
459 "invert=%d\n", temp, name, op, logic, invert));
460
461 /*
462 * Fill in data for the rule...
463 */
464
465 current = temp;
466 temp->op = op;
467 invert = 0;
468
469 switch (op)
470 {
471 case MIME_MAGIC_MATCH :
472 if (length[0] > (sizeof(temp->value.matchv) - 1))
473 return (-1);
474 strcpy(temp->value.matchv, value[0]);
475 break;
476 case MIME_MAGIC_ASCII :
477 case MIME_MAGIC_PRINTABLE :
478 temp->offset = strtol(value[0], NULL, 0);
479 temp->length = strtol(value[1], NULL, 0);
480 if (temp->length > MIME_MAX_BUFFER)
481 temp->length = MIME_MAX_BUFFER;
482 break;
483 case MIME_MAGIC_STRING :
484 case MIME_MAGIC_ISTRING :
485 temp->offset = strtol(value[0], NULL, 0);
486 if (length[1] > sizeof(temp->value.stringv))
487 return (-1);
488 temp->length = length[1];
489 memcpy(temp->value.stringv, value[1], length[1]);
490 break;
491 case MIME_MAGIC_CHAR :
492 temp->offset = strtol(value[0], NULL, 0);
493 if (length[1] == 1)
494 temp->value.charv = value[1][0];
495 else
496 temp->value.charv = (char)strtol(value[1], NULL, 0);
497 break;
498 case MIME_MAGIC_SHORT :
499 temp->offset = strtol(value[0], NULL, 0);
500 temp->value.shortv = (short)strtol(value[1], NULL, 0);
501 break;
502 case MIME_MAGIC_INT :
503 temp->offset = strtol(value[0], NULL, 0);
504 temp->value.intv = (int)strtol(value[1], NULL, 0);
505 break;
506 case MIME_MAGIC_LOCALE :
507 if (length[0] > (sizeof(temp->value.localev) - 1))
508 return (-1);
509
510 strcpy(temp->value.localev, value[0]);
511 break;
512 case MIME_MAGIC_CONTAINS :
513 temp->offset = strtol(value[0], NULL, 0);
514 temp->region = strtol(value[1], NULL, 0);
515 if (length[2] > sizeof(temp->value.stringv))
516 return (-1);
517 temp->length = length[2];
518 memcpy(temp->value.stringv, value[2], length[2]);
519 break;
520 }
521 }
522 else
523 break;
524 }
525
526 return (0);
527 }
528
529
530 /*
531 * 'mimeFileType()' - Determine the type of a file.
532 */
533
534 mime_type_t * /* O - Type of file */
535 mimeFileType(mime_t *mime, /* I - MIME database */
536 const char *pathname, /* I - Name of file to check on disk */
537 const char *filename, /* I - Original filename or NULL */
538 int *compression) /* O - Is the file compressed? */
539 {
540 _mime_filebuf_t fb; /* File buffer */
541 const char *base; /* Base filename of file */
542 mime_type_t *type, /* File type */
543 *best; /* Best match */
544
545
546 DEBUG_printf(("mimeFileType(mime=%p, pathname=\"%s\", filename=\"%s\", "
547 "compression=%p)\n", mime, pathname, filename, compression));
548
549 /*
550 * Range check input parameters...
551 */
552
553 if (!mime || !pathname)
554 return (NULL);
555
556 /*
557 * Try to open the file...
558 */
559
560 if ((fb.fp = cupsFileOpen(pathname, "r")) == NULL)
561 return (NULL);
562
563 fb.offset = -1;
564 fb.length = 0;
565
566 /*
567 * Figure out the base filename (without directory portion)...
568 */
569
570 if (filename)
571 {
572 if ((base = strrchr(filename, '/')) != NULL)
573 base ++;
574 else
575 base = filename;
576 }
577 else if ((base = strrchr(pathname, '/')) != NULL)
578 base ++;
579 else
580 base = pathname;
581
582 /*
583 * Then check it against all known types...
584 */
585
586 for (type = (mime_type_t *)cupsArrayFirst(mime->types), best = NULL;
587 type;
588 type = (mime_type_t *)cupsArrayNext(mime->types))
589 if (checkrules(base, &fb, type->rules))
590 {
591 if (!best || type->priority > best->priority)
592 best = type;
593 }
594
595 /*
596 * Finally, close the file and return a match (if any)...
597 */
598
599 if (compression)
600 *compression = cupsFileCompression(fb.fp);
601
602 cupsFileClose(fb.fp);
603
604 return (best);
605 }
606
607
608 /*
609 * 'mimeType()' - Lookup a file type.
610 */
611
612 mime_type_t * /* O - Matching file type definition */
613 mimeType(mime_t *mime, /* I - MIME database */
614 const char *super, /* I - Super-type name */
615 const char *type) /* I - Type name */
616 {
617 mime_type_t key; /* MIME type search key*/
618
619
620 /*
621 * Range check input...
622 */
623
624 if (!mime || !super || !type)
625 return (NULL);
626
627 /*
628 * Lookup the type in the array...
629 */
630
631 strlcpy(key.super, super, sizeof(key.super));
632 strlcpy(key.type, type, sizeof(key.type));
633
634 return ((mime_type_t *)cupsArrayFind(mime->types, &key));
635 }
636
637
638 /*
639 * 'compare_types()' - Compare two MIME super/type names.
640 */
641
642 static int /* O - Result of comparison */
643 compare_types(mime_type_t *t0, /* I - First type */
644 mime_type_t *t1) /* I - Second type */
645 {
646 int i; /* Result of comparison */
647
648
649 if ((i = strcasecmp(t0->super, t1->super)) == 0)
650 i = strcasecmp(t0->type, t1->type);
651
652 return (i);
653 }
654
655
656 /*
657 * 'checkrules()' - Check each rule in a list.
658 */
659
660 static int /* O - 1 if match, 0 if no match */
661 checkrules(const char *filename, /* I - Filename */
662 _mime_filebuf_t *fb, /* I - File to check */
663 mime_magic_t *rules) /* I - Rules to check */
664 {
665 int n; /* Looping var */
666 int region; /* Region to look at */
667 int logic, /* Logic to apply */
668 result, /* Result of test */
669 intv; /* Integer value */
670 short shortv; /* Short value */
671 unsigned char *bufptr; /* Pointer into buffer */
672 #ifdef DEBUG
673 const char * const debug_tests[] = /* Test names... */
674 {
675 "NOP", /* No operation */
676 "AND", /* Logical AND of all children */
677 "OR", /* Logical OR of all children */
678 "MATCH", /* Filename match */
679 "ASCII", /* ASCII characters in range */
680 "PRINTABLE", /* Printable characters (32-255) */
681 "STRING", /* String matches */
682 "CHAR", /* Character/byte matches */
683 "SHORT", /* Short/16-bit word matches */
684 "INT", /* Integer/32-bit word matches */
685 "LOCALE" /* Current locale matches string */
686 "CONTAINS" /* File contains a string */
687 "ISTRING" /* Case-insensitive string matches */
688 };
689 #endif /* DEBUG */
690
691
692 DEBUG_printf(("checkrules(filename=\"%s\", fb=%p, rules=%p)\n", filename,
693 fb, rules));
694
695 if (rules == NULL)
696 return (0);
697
698 if (rules->parent == NULL)
699 logic = MIME_MAGIC_OR;
700 else
701 logic = rules->parent->op;
702
703 result = 0;
704
705 while (rules != NULL)
706 {
707 /*
708 * Compute the result of this rule...
709 */
710
711 switch (rules->op)
712 {
713 case MIME_MAGIC_MATCH :
714 result = patmatch(filename, rules->value.matchv);
715 break;
716
717 case MIME_MAGIC_ASCII :
718 /*
719 * Load the buffer if necessary...
720 */
721
722 if (fb->offset < 0 || rules->offset < fb->offset ||
723 (rules->offset + rules->length) > (fb->offset + fb->length))
724 {
725 /*
726 * Reload file buffer...
727 */
728
729 cupsFileSeek(fb->fp, rules->offset);
730 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
731 sizeof(fb->buffer));
732 fb->offset = rules->offset;
733 }
734
735 /*
736 * Test for ASCII printable characters plus standard control chars.
737 */
738
739 if ((rules->offset + rules->length) > (fb->offset + fb->length))
740 n = fb->offset + fb->length - rules->offset;
741 else
742 n = rules->length;
743
744 bufptr = fb->buffer + rules->offset - fb->offset;
745 while (n > 0)
746 if ((*bufptr >= 32 && *bufptr <= 126) ||
747 (*bufptr >= 8 && *bufptr <= 13) ||
748 *bufptr == 26 || *bufptr == 27)
749 {
750 n --;
751 bufptr ++;
752 }
753 else
754 break;
755
756 result = (n == 0);
757 break;
758
759 case MIME_MAGIC_PRINTABLE :
760 /*
761 * Load the buffer if necessary...
762 */
763
764 if (fb->offset < 0 || rules->offset < fb->offset ||
765 (rules->offset + rules->length) > (fb->offset + fb->length))
766 {
767 /*
768 * Reload file buffer...
769 */
770
771 cupsFileSeek(fb->fp, rules->offset);
772 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
773 sizeof(fb->buffer));
774 fb->offset = rules->offset;
775 }
776
777 /*
778 * Test for 8-bit printable characters plus standard control chars.
779 */
780
781 if ((rules->offset + rules->length) > (fb->offset + fb->length))
782 n = fb->offset + fb->length - rules->offset;
783 else
784 n = rules->length;
785
786 bufptr = fb->buffer + rules->offset - fb->offset;
787
788 while (n > 0)
789 if (*bufptr >= 128 ||
790 (*bufptr >= 32 && *bufptr <= 126) ||
791 (*bufptr >= 8 && *bufptr <= 13) ||
792 *bufptr == 26 || *bufptr == 27)
793 {
794 n --;
795 bufptr ++;
796 }
797 else
798 break;
799
800 result = (n == 0);
801 break;
802
803 case MIME_MAGIC_STRING :
804 DEBUG_printf(("checkrules: string(%d, \"%s\")\n", rules->offset,
805 rules->value.stringv));
806
807 /*
808 * Load the buffer if necessary...
809 */
810
811 if (fb->offset < 0 || rules->offset < fb->offset ||
812 (rules->offset + rules->length) > (fb->offset + fb->length))
813 {
814 /*
815 * Reload file buffer...
816 */
817
818 cupsFileSeek(fb->fp, rules->offset);
819 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
820 sizeof(fb->buffer));
821 fb->offset = rules->offset;
822
823 DEBUG_printf(("checkrules: loaded %d byte fb->buffer at %d, starts "
824 "with \"%c%c%c%c\"...\n",
825 fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
826 fb->buffer[2], fb->buffer[3]));
827 }
828
829 /*
830 * Compare the buffer against the string. If the file is too
831 * short then don't compare - it can't match...
832 */
833
834 if ((rules->offset + rules->length) > (fb->offset + fb->length))
835 result = 0;
836 else
837 result = (memcmp(fb->buffer + rules->offset - fb->offset,
838 rules->value.stringv, rules->length) == 0);
839 DEBUG_printf(("checkrules: result=%d\n", result));
840 break;
841
842 case MIME_MAGIC_ISTRING :
843 /*
844 * Load the buffer if necessary...
845 */
846
847 if (fb->offset < 0 || rules->offset < fb->offset ||
848 (rules->offset + rules->length) > (fb->offset + fb->length))
849 {
850 /*
851 * Reload file buffer...
852 */
853
854 cupsFileSeek(fb->fp, rules->offset);
855 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
856 sizeof(fb->buffer));
857 fb->offset = rules->offset;
858 }
859
860 /*
861 * Compare the buffer against the string. If the file is too
862 * short then don't compare - it can't match...
863 */
864
865 if ((rules->offset + rules->length) > (fb->offset + fb->length))
866 result = 0;
867 else
868 result = (strncasecmp((char *)fb->buffer + rules->offset -
869 fb->offset,
870 rules->value.stringv, rules->length) == 0);
871 break;
872
873 case MIME_MAGIC_CHAR :
874 /*
875 * Load the buffer if necessary...
876 */
877
878 if (fb->offset < 0 || rules->offset < fb->offset)
879 {
880 /*
881 * Reload file buffer...
882 */
883
884 cupsFileSeek(fb->fp, rules->offset);
885 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
886 sizeof(fb->buffer));
887 fb->offset = rules->offset;
888 }
889
890 /*
891 * Compare the character values; if the file is too short, it
892 * can't match...
893 */
894
895 if (fb->length < 1)
896 result = 0;
897 else
898 result = (fb->buffer[rules->offset - fb->offset] ==
899 rules->value.charv);
900 break;
901
902 case MIME_MAGIC_SHORT :
903 /*
904 * Load the buffer if necessary...
905 */
906
907 if (fb->offset < 0 || rules->offset < fb->offset ||
908 (rules->offset + 2) > (fb->offset + fb->length))
909 {
910 /*
911 * Reload file buffer...
912 */
913
914 cupsFileSeek(fb->fp, rules->offset);
915 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
916 sizeof(fb->buffer));
917 fb->offset = rules->offset;
918 }
919
920 /*
921 * Compare the short values; if the file is too short, it
922 * can't match...
923 */
924
925 if (fb->length < 2)
926 result = 0;
927 else
928 {
929 bufptr = fb->buffer + rules->offset - fb->offset;
930 shortv = (bufptr[0] << 8) | bufptr[1];
931 result = (shortv == rules->value.shortv);
932 }
933 break;
934
935 case MIME_MAGIC_INT :
936 /*
937 * Load the buffer if necessary...
938 */
939
940 if (fb->offset < 0 || rules->offset < fb->offset ||
941 (rules->offset + 4) > (fb->offset + fb->length))
942 {
943 /*
944 * Reload file buffer...
945 */
946
947 cupsFileSeek(fb->fp, rules->offset);
948 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
949 sizeof(fb->buffer));
950 fb->offset = rules->offset;
951 }
952
953 /*
954 * Compare the int values; if the file is too short, it
955 * can't match...
956 */
957
958 if (fb->length < 4)
959 result = 0;
960 else
961 {
962 bufptr = fb->buffer + rules->offset - fb->offset;
963 intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) |
964 bufptr[2]) << 8) | bufptr[3];
965 result = (intv == rules->value.intv);
966 }
967 break;
968
969 case MIME_MAGIC_LOCALE :
970 #if defined(WIN32) || defined(__EMX__) || defined(__APPLE__)
971 result = (strcmp(rules->value.localev,
972 setlocale(LC_ALL, "")) == 0);
973 #else
974 result = (strcmp(rules->value.localev,
975 setlocale(LC_MESSAGES, "")) == 0);
976 #endif /* __APPLE__ */
977 break;
978
979 case MIME_MAGIC_CONTAINS :
980 /*
981 * Load the buffer if necessary...
982 */
983
984 if (fb->offset < 0 || rules->offset < fb->offset ||
985 (rules->offset + rules->region) > (fb->offset + fb->length))
986 {
987 /*
988 * Reload file buffer...
989 */
990
991 cupsFileSeek(fb->fp, rules->offset);
992 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
993 sizeof(fb->buffer));
994 fb->offset = rules->offset;
995 }
996
997 /*
998 * Compare the buffer against the string. If the file is too
999 * short then don't compare - it can't match...
1000 */
1001
1002 if ((rules->offset + rules->length) > (fb->offset + fb->length))
1003 result = 0;
1004 else
1005 {
1006 if (fb->length > rules->region)
1007 region = rules->region - rules->length;
1008 else
1009 region = fb->length - rules->length;
1010
1011 for (n = 0; n < region; n ++)
1012 if ((result = (memcmp(fb->buffer + rules->offset - fb->offset + n,
1013 rules->value.stringv,
1014 rules->length) == 0)) != 0)
1015 break;
1016 }
1017 break;
1018
1019 default :
1020 if (rules->child != NULL)
1021 result = checkrules(filename, fb, rules->child);
1022 else
1023 result = 0;
1024 break;
1025 }
1026
1027 /*
1028 * If the logic is inverted, invert the result...
1029 */
1030
1031 if (rules->invert)
1032 result = !result;
1033
1034 /*
1035 * OK, now if the current logic is OR and this result is true, the this
1036 * rule set is true. If the current logic is AND and this result is false,
1037 * the the rule set is false...
1038 */
1039
1040 DEBUG_printf(("checkrules: result of test %p (MIME_MAGIC_%s) is %d\n",
1041 rules, debug_tests[rules->op], result));
1042
1043 if ((result && logic == MIME_MAGIC_OR) ||
1044 (!result && logic == MIME_MAGIC_AND))
1045 return (result);
1046
1047 /*
1048 * Otherwise the jury is still out on this one, so move to the next rule.
1049 */
1050
1051 rules = rules->next;
1052 }
1053
1054 return (result);
1055 }
1056
1057
1058 /*
1059 * 'patmatch()' - Pattern matching...
1060 */
1061
1062 static int /* O - 1 if match, 0 if no match */
1063 patmatch(const char *s, /* I - String to match against */
1064 const char *pat) /* I - Pattern to match against */
1065 {
1066 /*
1067 * Range check the input...
1068 */
1069
1070 if (s == NULL || pat == NULL)
1071 return (0);
1072
1073 /*
1074 * Loop through the pattern and match strings, and stop if we come to a
1075 * point where the strings don't match or we find a complete match.
1076 */
1077
1078 while (*s != '\0' && *pat != '\0')
1079 {
1080 if (*pat == '*')
1081 {
1082 /*
1083 * Wildcard - 0 or more characters...
1084 */
1085
1086 pat ++;
1087 if (*pat == '\0')
1088 return (1); /* Last pattern char is *, so everything matches... */
1089
1090 /*
1091 * Test all remaining combinations until we get to the end of the string.
1092 */
1093
1094 while (*s != '\0')
1095 {
1096 if (patmatch(s, pat))
1097 return (1);
1098
1099 s ++;
1100 }
1101 }
1102 else if (*pat == '?')
1103 {
1104 /*
1105 * Wildcard - 1 character...
1106 */
1107
1108 pat ++;
1109 s ++;
1110 continue;
1111 }
1112 else if (*pat == '[')
1113 {
1114 /*
1115 * Match a character from the input set [chars]...
1116 */
1117
1118 pat ++;
1119 while (*pat != ']' && *pat != '\0')
1120 if (*s == *pat)
1121 break;
1122 else
1123 pat ++;
1124
1125 if (*pat == ']' || *pat == '\0')
1126 return (0);
1127
1128 while (*pat != ']' && *pat != '\0')
1129 pat ++;
1130
1131 if (*pat == ']')
1132 pat ++;
1133
1134 continue;
1135 }
1136 else if (*pat == '\\')
1137 {
1138 /*
1139 * Handle quoted characters...
1140 */
1141
1142 pat ++;
1143 }
1144
1145 /*
1146 * Stop if the pattern and string don't match...
1147 */
1148
1149 if (*pat++ != *s++)
1150 return (0);
1151 }
1152
1153 /*
1154 * Done parsing the pattern and string; return 1 if the last character
1155 * matches and 0 otherwise...
1156 */
1157
1158 return (*s == *pat);
1159 }
1160
1161
1162 /*
1163 * End of "$Id: type.c 7720 2008-07-11 22:46:21Z mike $".
1164 */