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