]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/type.c
37841cb95ff220ec6e9588efe2f24d4eb24c3795
2 * "$Id: type.c,v 1.15 2002/05/14 01:14:30 mike Exp $"
4 * MIME typing routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2002 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * mimeAddType() - Add a MIME type to a database.
27 * mimeAddRule() - Add a detection rule for a file type.
28 * mimeFileType() - Determine the type of a file.
29 * mimeType() - Lookup a file type.
30 * compare() - Compare two MIME super/type names.
31 * checkrules() - Check each rule in a list.
32 * patmatch() - Pattern matching...
36 * Include necessary headers...
44 #include <cups/string.h>
46 #include <cups/debug.h>
53 static int compare(mime_type_t
**, mime_type_t
**);
54 static int checkrules(const char *, FILE *, mime_magic_t
*);
55 static int patmatch(const char *, const char *);
59 * 'mimeAddType()' - Add a MIME type to a database.
62 mime_type_t
* /* O - New (or existing) MIME type */
63 mimeAddType(mime_t
*mime
, /* I - MIME database */
64 const char *super
, /* I - Super-type name */
65 const char *type
) /* I - Type name */
67 mime_type_t
*temp
, /* New MIME type */
68 **types
; /* New MIME types array */
72 * Range check input...
75 if (mime
== NULL
|| super
== NULL
|| type
== NULL
)
78 if (strlen(super
) > (MIME_MAX_SUPER
- 1) ||
79 strlen(type
) > (MIME_MAX_TYPE
- 1))
83 * See if the type already exists; if so, return the existing type...
86 if ((temp
= mimeType(mime
, super
, type
)) != NULL
)
90 * The type doesn't exist; add it...
93 if ((temp
= calloc(1, sizeof(mime_type_t
))) == NULL
)
96 if (mime
->num_types
== 0)
97 types
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
99 types
= (mime_type_t
**)realloc(mime
->types
, sizeof(mime_type_t
*) * (mime
->num_types
+ 1));
108 types
+= mime
->num_types
;
112 strncpy(temp
->super
, super
, sizeof(temp
->super
) - 1);
113 if ((temp
->type
= strdup(type
)) == NULL
)
119 if (mime
->num_types
> 1)
120 qsort(mime
->types
, mime
->num_types
, sizeof(mime_type_t
*),
121 (int (*)(const void *, const void *))compare
);
128 * 'mimeAddRule()' - Add a detection rule for a file type.
131 int /* O - 0 on success, -1 on failure */
132 mimeAddTypeRule(mime_type_t
*mt
, /* I - Type to add to */
133 const char *rule
) /* I - Rule to add */
135 int num_values
, /* Number of values seen */
136 op
, /* Operation code */
137 logic
, /* Logic for next rule */
138 invert
; /* Invert following rule? */
139 char name
[255], /* Name in rule string */
140 value
[3][255], /* Value in rule string */
141 *ptr
, /* Position in name or value */
142 quote
; /* Quote character */
143 int length
[3]; /* Length of each parameter */
144 mime_magic_t
*temp
, /* New rule */
145 *current
; /* Current rule */
149 * Range check input...
152 if (mt
== NULL
|| rule
== NULL
)
156 * Find the last rule in the top-level of the rules tree.
159 for (current
= mt
->rules
; current
!= NULL
; current
= current
->next
)
160 if (current
->next
== NULL
)
164 * Parse the rules string. Most rules are either a file extension or a
165 * comparison function:
168 * function(parameters)
171 logic
= MIME_MAGIC_NOP
;
174 DEBUG_printf(("%s/%s: %s\n", mt
->super
, mt
->type
, rule
));
176 while (*rule
!= '\0')
178 while (isspace(*rule
))
183 DEBUG_puts("new parenthesis group");
184 logic
= MIME_MAGIC_NOP
;
187 else if (*rule
== ')')
189 DEBUG_puts("close paren...");
190 if (current
== NULL
|| current
->parent
== NULL
)
193 current
= current
->parent
;
195 if (current
->parent
== NULL
)
196 logic
= MIME_MAGIC_OR
;
198 logic
= current
->parent
->op
;
202 else if (*rule
== '+' && current
!= NULL
)
204 if (logic
!= MIME_MAGIC_AND
&&
205 current
!= NULL
&& current
->prev
!= NULL
&& current
->prev
->prev
!= NULL
)
208 * OK, we have more than 1 rule in the current tree level... Make a
209 * new group tree and move the previous rule to it...
212 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
215 temp
->op
= MIME_MAGIC_AND
;
216 temp
->child
= current
;
217 temp
->parent
= current
->parent
;
218 current
->prev
->next
= temp
;
219 temp
->prev
= current
->prev
;
221 current
->prev
= NULL
;
222 current
->parent
= temp
;
224 DEBUG_printf(("creating new AND group %p...\n", temp
));
228 DEBUG_printf(("setting group %p op to AND...\n", current
->parent
));
229 current
->parent
->op
= MIME_MAGIC_AND
;
232 logic
= MIME_MAGIC_AND
;
235 else if (*rule
== ',')
237 if (logic
!= MIME_MAGIC_OR
&& current
!= NULL
)
240 * OK, we have two possibilities; either this is the top-level rule or
241 * we have a bunch of AND rules at this level.
244 if (current
->parent
== NULL
)
247 * This is the top-level rule; we have to move *all* of the AND rules
248 * down a level, as AND has precedence over OR.
251 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
254 DEBUG_printf(("creating new AND group %p inside OR group\n", temp
));
256 while (current
->prev
!= NULL
)
258 current
->parent
= temp
;
259 current
= current
->prev
;
262 current
->parent
= temp
;
263 temp
->op
= MIME_MAGIC_AND
;
264 temp
->child
= current
;
266 mt
->rules
= current
= temp
;
271 * This isn't the top rule, so go up one level...
274 DEBUG_puts("going up one level");
275 current
= current
->parent
;
279 logic
= MIME_MAGIC_OR
;
282 else if (*rule
== '!')
288 else if (isalnum(*rule
))
291 * Read an extension name or a function...
294 for (ptr
= name
; isalnum(*rule
) && (ptr
- name
) < (sizeof(name
) - 1);)
303 * Read function parameters...
308 num_values
< (sizeof(value
) / sizeof(value
[0]));
311 ptr
= value
[num_values
];
313 while ((ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1) &&
314 *rule
!= '\0' && *rule
!= ',' && *rule
!= ')')
319 * Ignore whitespace...
325 else if (*rule
== '\"' || *rule
== '\'')
328 * Copy quoted strings literally...
333 while (*rule
!= '\0' && *rule
!= quote
&&
334 (ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1))
342 else if (*rule
== '<')
346 while (*rule
!= '>' && *rule
!= '\0' &&
347 (ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1))
349 if (isxdigit(rule
[0]) && isxdigit(rule
[1]))
352 *ptr
= (*rule
++ - '0') << 4;
354 *ptr
= (tolower(*rule
++) - 'a' + 10) << 4;
357 *ptr
++ |= *rule
++ - '0';
359 *ptr
++ |= tolower(*rule
++) - 'a' + 10;
375 length
[num_values
] = ptr
- value
[num_values
];
389 * Figure out the function...
392 if (strcmp(name
, "match") == 0)
393 op
= MIME_MAGIC_MATCH
;
394 else if (strcmp(name
, "ascii") == 0)
395 op
= MIME_MAGIC_ASCII
;
396 else if (strcmp(name
, "printable") == 0)
397 op
= MIME_MAGIC_PRINTABLE
;
398 else if (strcmp(name
, "string") == 0)
399 op
= MIME_MAGIC_STRING
;
400 else if (strcmp(name
, "char") == 0)
401 op
= MIME_MAGIC_CHAR
;
402 else if (strcmp(name
, "short") == 0)
403 op
= MIME_MAGIC_SHORT
;
404 else if (strcmp(name
, "int") == 0)
406 else if (strcmp(name
, "locale") == 0)
407 op
= MIME_MAGIC_LOCALE
;
408 else if (strcmp(name
, "contains") == 0)
409 op
= MIME_MAGIC_CONTAINS
;
416 * This is just a filename match on the extension...
419 snprintf(value
[0], sizeof(value
[0]), "*.%s", name
);
420 length
[0] = strlen(value
[0]);
422 op
= MIME_MAGIC_MATCH
;
426 * Add a rule for this operation.
429 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
432 temp
->invert
= invert
;
435 temp
->parent
= current
->parent
;
436 current
->next
= temp
;
441 temp
->prev
= current
;
443 if (logic
== MIME_MAGIC_NOP
)
446 * Add parenthetical grouping...
449 DEBUG_printf(("making new OR group %p for parenthesis...\n", temp
));
451 temp
->op
= MIME_MAGIC_OR
;
453 if ((temp
->child
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
456 temp
->child
->parent
= temp
;
459 logic
= MIME_MAGIC_OR
;
462 DEBUG_printf(("adding %p: %s, op = %d, logic = %d, invert = %d\n",
463 temp
, name
, op
, logic
, invert
));
466 * Fill in data for the rule...
475 case MIME_MAGIC_MATCH
:
476 if (length
[0] > (sizeof(temp
->value
.matchv
) - 1))
478 strcpy(temp
->value
.matchv
, value
[0]);
480 case MIME_MAGIC_ASCII
:
481 case MIME_MAGIC_PRINTABLE
:
482 temp
->offset
= strtol(value
[0], NULL
, 0);
483 temp
->length
= strtol(value
[1], NULL
, 0);
484 if (temp
->length
> MIME_MAX_BUFFER
)
485 temp
->length
= MIME_MAX_BUFFER
;
487 case MIME_MAGIC_STRING
:
488 temp
->offset
= strtol(value
[0], NULL
, 0);
489 if (length
[1] > sizeof(temp
->value
.stringv
))
491 temp
->length
= length
[1];
492 memcpy(temp
->value
.stringv
, value
[1], length
[1]);
494 case MIME_MAGIC_CHAR
:
495 temp
->offset
= strtol(value
[0], NULL
, 0);
497 temp
->value
.charv
= value
[1][0];
499 temp
->value
.charv
= strtol(value
[1], NULL
, 0);
501 case MIME_MAGIC_SHORT
:
502 temp
->offset
= strtol(value
[0], NULL
, 0);
503 temp
->value
.shortv
= strtol(value
[1], NULL
, 0);
505 case MIME_MAGIC_INT
:
506 temp
->offset
= strtol(value
[0], NULL
, 0);
507 temp
->value
.intv
= strtol(value
[1], NULL
, 0);
509 case MIME_MAGIC_LOCALE
:
510 if (length
[0] > (sizeof(temp
->value
.localev
) - 1))
513 strcpy(temp
->value
.localev
, value
[0]);
515 case MIME_MAGIC_CONTAINS
:
516 temp
->offset
= strtol(value
[0], NULL
, 0);
517 temp
->region
= strtol(value
[1], NULL
, 0);
518 if (length
[2] > sizeof(temp
->value
.stringv
))
520 temp
->length
= length
[2];
521 memcpy(temp
->value
.stringv
, value
[2], length
[2]);
534 * 'mimeFileType()' - Determine the type of a file.
537 mime_type_t
* /* O - Type of file */
538 mimeFileType(mime_t
*mime
, /* I - MIME database */
539 const char *pathname
) /* I - Name of file to check */
541 int i
; /* Looping var */
542 FILE *fp
; /* File pointer */
543 mime_type_t
**types
; /* File types */
544 const char *filename
; /* Base filename of file */
548 * Range check input parameters...
551 if (mime
== NULL
|| pathname
== NULL
)
555 * Try to open the file...
558 if ((fp
= fopen(pathname
, "r")) == NULL
)
562 * Figure out the filename (without directory portion)...
565 if ((filename
= strrchr(pathname
, '/')) != NULL
)
571 * Then check it against all known types...
574 for (i
= mime
->num_types
, types
= mime
->types
; i
> 0; i
--, types
++)
575 if (checkrules(filename
, fp
, (*types
)->rules
))
579 * Finally, close the file and return a match (if any)...
592 * 'mimeType()' - Lookup a file type.
595 mime_type_t
* /* O - Matching file type definition */
596 mimeType(mime_t
*mime
, /* I - MIME database */
597 const char *super
, /* I - Super-type name */
598 const char *type
) /* I - Type name */
600 mime_type_t key
, /* MIME type search key*/
601 *keyptr
, /* Key pointer... */
602 **match
; /* Matching pointer */
605 * Range check input...
608 if (mime
== NULL
|| super
== NULL
|| type
== NULL
)
611 if (strlen(super
) > (MIME_MAX_SUPER
- 1) ||
612 strlen(type
) > (MIME_MAX_TYPE
- 1))
615 if (mime
->num_types
== 0)
619 * Lookup the type in the array...
622 strncpy(key
.super
, super
, sizeof(key
.super
) - 1);
623 key
.super
[sizeof(key
.super
) - 1] = '\0';
624 key
.type
= (char *)type
;
628 match
= (mime_type_t
**)bsearch(&keyptr
, mime
->types
, mime
->num_types
,
629 sizeof(mime_type_t
*),
630 (int (*)(const void *, const void *))compare
);
640 * 'compare()' - Compare two MIME super/type names.
643 static int /* O - Result of comparison */
644 compare(mime_type_t
**t0
, /* I - First type */
645 mime_type_t
**t1
) /* I - Second type */
647 int i
; /* Result of comparison */
650 if ((i
= strcasecmp((*t0
)->super
, (*t1
)->super
)) == 0)
651 i
= strcasecmp((*t0
)->type
, (*t1
)->type
);
658 * 'checkrules()' - Check each rule in a list.
661 static int /* O - 1 if match, 0 if no match */
662 checkrules(const char *filename
, /* I - Filename */
663 FILE *fp
, /* I - File to check */
664 mime_magic_t
*rules
) /* I - Rules to check */
666 int n
; /* Looping var */
667 int region
; /* Region to look at */
668 int logic
, /* Logic to apply */
669 result
, /* Result of test */
670 intv
; /* Integer value */
671 short shortv
; /* Short value */
672 unsigned char buffer
[MIME_MAX_BUFFER
],/* Input buffer */
673 *bufptr
; /* Current buffer position */
674 int bufoffset
, /* Offset in file for buffer */
675 buflength
; /* Length of data in buffer */
681 if (rules
->parent
== NULL
)
682 logic
= MIME_MAGIC_OR
;
684 logic
= rules
->parent
->op
;
690 while (rules
!= NULL
)
693 * Compute the result of this rule...
698 case MIME_MAGIC_MATCH
:
699 result
= patmatch(filename
, rules
->value
.matchv
);
702 case MIME_MAGIC_ASCII
:
704 * Load the buffer if necessary...
707 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
708 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
711 * Reload file buffer...
714 fseek(fp
, rules
->offset
, SEEK_SET
);
715 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
716 bufoffset
= rules
->offset
;
720 * Test for ASCII printable characters plus standard control chars.
723 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
724 n
= bufoffset
+ buflength
- rules
->offset
;
728 bufptr
= buffer
+ rules
->offset
- bufoffset
;
730 if ((*bufptr
>= 32 && *bufptr
<= 126) ||
731 (*bufptr
>= 8 && *bufptr
<= 13) ||
732 *bufptr
== 26 || *bufptr
== 27)
743 case MIME_MAGIC_PRINTABLE
:
745 * Load the buffer if necessary...
748 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
749 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
752 * Reload file buffer...
755 fseek(fp
, rules
->offset
, SEEK_SET
);
756 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
757 bufoffset
= rules
->offset
;
761 * Test for 8-bit printable characters plus standard control chars.
764 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
765 n
= bufoffset
+ buflength
- rules
->offset
;
769 bufptr
= buffer
+ rules
->offset
- bufoffset
;
772 if (*bufptr
>= 128 ||
773 (*bufptr
>= 32 && *bufptr
<= 126) ||
774 (*bufptr
>= 8 && *bufptr
<= 13) ||
775 *bufptr
== 26 || *bufptr
== 27)
786 case MIME_MAGIC_STRING
:
788 * Load the buffer if necessary...
791 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
792 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
795 * Reload file buffer...
798 fseek(fp
, rules
->offset
, SEEK_SET
);
799 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
800 bufoffset
= rules
->offset
;
804 * Compare the buffer against the string. If the file is too
805 * short then don't compare - it can't match...
808 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
811 result
= (memcmp(buffer
+ rules
->offset
- bufoffset
,
812 rules
->value
.stringv
, rules
->length
) == 0);
815 case MIME_MAGIC_CHAR
:
817 * Load the buffer if necessary...
820 if (bufoffset
< 0 || rules
->offset
< bufoffset
)
823 * Reload file buffer...
826 fseek(fp
, rules
->offset
, SEEK_SET
);
827 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
828 bufoffset
= rules
->offset
;
832 * Compare the character values; if the file is too short, it
839 result
= (buffer
[rules
->offset
- bufoffset
] == rules
->value
.charv
);
842 case MIME_MAGIC_SHORT
:
844 * Load the buffer if necessary...
847 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
848 (rules
->offset
+ 2) > (bufoffset
+ buflength
))
851 * Reload file buffer...
854 fseek(fp
, rules
->offset
, SEEK_SET
);
855 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
856 bufoffset
= rules
->offset
;
860 * Compare the short values; if the file is too short, it
868 bufptr
= buffer
+ rules
->offset
- bufoffset
;
869 shortv
= (bufptr
[0] << 8) | bufptr
[1];
870 result
= (shortv
== rules
->value
.shortv
);
874 case MIME_MAGIC_INT
:
876 * Load the buffer if necessary...
879 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
880 (rules
->offset
+ 4) > (bufoffset
+ buflength
))
883 * Reload file buffer...
886 fseek(fp
, rules
->offset
, SEEK_SET
);
887 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
888 bufoffset
= rules
->offset
;
892 * Compare the int values; if the file is too short, it
900 bufptr
= buffer
+ rules
->offset
- bufoffset
;
901 intv
= (((((bufptr
[0] << 8) | bufptr
[1]) << 8) | bufptr
[2]) << 8) |
903 result
= (intv
== rules
->value
.intv
);
907 case MIME_MAGIC_LOCALE
:
909 result
= (strcmp(rules
->value
.localev
, setlocale(LC_ALL
, NULL
)) == 0);
911 result
= (strcmp(rules
->value
.localev
, setlocale(LC_MESSAGES
, NULL
)) == 0);
912 #endif /* __APPLE__ */
915 case MIME_MAGIC_CONTAINS
:
917 * Load the buffer if necessary...
920 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
921 (rules
->offset
+ rules
->region
) > (bufoffset
+ buflength
))
924 * Reload file buffer...
927 fseek(fp
, rules
->offset
, SEEK_SET
);
928 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
929 bufoffset
= rules
->offset
;
933 * Compare the buffer against the string. If the file is too
934 * short then don't compare - it can't match...
937 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
941 if (buflength
> rules
->region
)
942 region
= rules
->region
- rules
->length
;
944 region
= buflength
- rules
->length
;
946 for (n
= 0; n
< region
; n
++)
947 if ((result
= (memcmp(buffer
+ rules
->offset
- bufoffset
+ n
,
948 rules
->value
.stringv
, rules
->length
) == 0)) != 0)
954 if (rules
->child
!= NULL
)
955 result
= checkrules(filename
, fp
, rules
->child
);
962 * If the logic is inverted, invert the result...
969 * OK, now if the current logic is OR and this result is true, the this
970 * rule set is true. If the current logic is AND and this result is false,
971 * the the rule set is false...
974 DEBUG_printf(("result of test %p is %d\n", rules
, result
));
976 if ((result
&& logic
== MIME_MAGIC_OR
) ||
977 (!result
&& logic
== MIME_MAGIC_AND
))
981 * Otherwise the jury is still out on this one, so move to the next rule.
992 * 'patmatch()' - Pattern matching...
995 static int /* O - 1 if match, 0 if no match */
996 patmatch(const char *s
, /* I - String to match against */
997 const char *pat
) /* I - Pattern to match against */
1000 * Range check the input...
1003 if (s
== NULL
|| pat
== NULL
)
1007 * Loop through the pattern and match strings, and stop if we come to a
1008 * point where the strings don't match or we find a complete match.
1011 while (*s
!= '\0' && *pat
!= '\0')
1016 * Wildcard - 0 or more characters...
1021 return (1); /* Last pattern char is *, so everything matches now... */
1024 * Test all remaining combinations until we get to the end of the string.
1029 if (patmatch(s
, pat
))
1035 else if (*pat
== '?')
1038 * Wildcard - 1 character...
1045 else if (*pat
== '[')
1048 * Match a character from the input set [chars]...
1052 while (*pat
!= ']' && *pat
!= '\0')
1058 if (*pat
== ']' || *pat
== '\0')
1061 while (*pat
!= ']' && *pat
!= '\0')
1069 else if (*pat
== '\\')
1072 * Handle quoted characters...
1079 * Stop if the pattern and string don't match...
1087 * Done parsing the pattern and string; return 1 if the last character matches
1088 * and 0 otherwise...
1091 return (*s
== *pat
);
1096 * End of "$Id: type.c,v 1.15 2002/05/14 01:14:30 mike Exp $".