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