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