]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/type.c
2 * "$Id: type.c,v 1.9 1999/07/12 16:09:41 mike Exp $"
4 * MIME typing routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-1999 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...
52 static int compare(mime_type_t
**, mime_type_t
**);
53 static int checkrules(const char *, FILE *, mime_magic_t
*);
54 static int patmatch(const char *, const char *);
58 * 'mimeAddType()' - Add a MIME type to a database.
61 mime_type_t
* /* O - New (or existing) MIME type */
62 mimeAddType(mime_t
*mime
, /* I - MIME database */
63 const char *super
, /* I - Super-type name */
64 const char *type
) /* I - Type name */
66 mime_type_t
*temp
, /* New MIME type */
67 **types
; /* New MIME types array */
71 * Range check input...
74 if (mime
== NULL
|| super
== NULL
|| type
== NULL
)
77 if (strlen(super
) > (MIME_MAX_SUPER
- 1) ||
78 strlen(type
) > (MIME_MAX_TYPE
- 1))
82 * See if the type already exists; if so, return the existing type...
85 if ((temp
= mimeType(mime
, super
, type
)) != NULL
)
89 * The type doesn't exist; add it...
92 if ((temp
= calloc(1, sizeof(mime_type_t
))) == NULL
)
95 if (mime
->num_types
== 0)
96 types
= (mime_type_t
**)malloc(sizeof(mime_type_t
*));
98 types
= (mime_type_t
**)realloc(mime
->types
, sizeof(mime_type_t
*) * (mime
->num_types
+ 1));
107 types
+= mime
->num_types
;
111 strcpy(temp
->super
, super
);
112 strcpy(temp
->type
, type
);
114 if (mime
->num_types
> 1)
115 qsort(mime
->types
, mime
->num_types
, sizeof(mime_type_t
*),
116 (int (*)(const void *, const void *))compare
);
123 * 'mimeAddRule()' - Add a detection rule for a file type.
126 int /* O - 0 on success, -1 on failure */
127 mimeAddTypeRule(mime_type_t
*mt
, /* I - Type to add to */
128 const char *rule
) /* I - Rule to add */
130 int num_values
, /* Number of values seen */
131 op
, /* Operation code */
132 logic
, /* Logic for next rule */
133 invert
; /* Invert following rule? */
134 char name
[255], /* Name in rule string */
135 value
[2][255], /* Value in rule string */
136 *ptr
, /* Position in name or value */
137 quote
; /* Quote character */
138 int length
[2]; /* Length of each parameter */
139 mime_magic_t
*temp
, /* New rule */
140 *current
; /* Current rule */
144 * Range check input...
147 if (mt
== NULL
|| rule
== NULL
)
151 * Find the last rule in the top-level of the rules tree.
154 for (current
= mt
->rules
; current
!= NULL
; current
= current
->next
)
155 if (current
->next
== NULL
)
159 * Parse the rules string. Most rules are either a file extension or a
160 * comparison function:
163 * function(parameters)
166 logic
= MIME_MAGIC_NOP
;
169 while (*rule
!= '\0')
171 while (isspace(*rule
))
176 logic
= MIME_MAGIC_NOP
;
179 else if (*rule
== ')')
181 if (current
== NULL
|| current
->parent
== NULL
)
184 current
= current
->parent
;
186 if (current
->parent
== NULL
)
187 logic
= MIME_MAGIC_OR
;
189 logic
= current
->parent
->op
;
193 else if (*rule
== '+' && current
!= NULL
)
195 if (logic
!= MIME_MAGIC_AND
&&
196 current
!= NULL
&& current
->prev
!= NULL
&& current
->prev
->prev
!= NULL
)
199 * OK, we have more than 1 rule in the current tree level... Make a
200 * new group tree and move the previous rule to it...
203 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
206 temp
->op
= MIME_MAGIC_AND
;
207 temp
->child
= current
;
208 temp
->parent
= current
->parent
;
209 current
->prev
->next
= temp
;
210 temp
->prev
= current
->prev
;
212 current
->prev
= NULL
;
213 current
->parent
= temp
;
216 current
->parent
->op
= MIME_MAGIC_AND
;
218 logic
= MIME_MAGIC_AND
;
221 else if (*rule
== ',')
223 if (logic
!= MIME_MAGIC_OR
&& current
!= NULL
)
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.
230 if (current
->parent
== NULL
)
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.
237 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
240 while (current
->prev
!= NULL
)
242 current
->parent
= temp
;
243 current
= current
->prev
;
246 current
->parent
= temp
;
247 temp
->op
= MIME_MAGIC_AND
;
248 temp
->child
= current
;
250 mt
->rules
= current
= temp
;
255 * This isn't the top rule, so go up one level...
258 current
= current
->parent
;
262 logic
= MIME_MAGIC_OR
;
265 else if (*rule
== '!')
270 else if (isalnum(*rule
))
273 * Read an extension name or a function...
276 for (ptr
= name
; isalnum(*rule
) && (ptr
- name
) < (sizeof(name
) - 1);)
285 * Read function parameters...
289 for (num_values
= 0; num_values
< 2; num_values
++)
291 ptr
= value
[num_values
];
293 while ((ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1) &&
294 *rule
!= '\0' && *rule
!= ',' && *rule
!= ')')
299 * Ignore whitespace...
305 else if (*rule
== '\"' || *rule
== '\'')
308 * Copy quoted strings literally...
313 while (*rule
!= '\0' && *rule
!= quote
)
321 else if (*rule
== '<')
325 while (*rule
!= '>' && *rule
!= '\0')
327 if (isxdigit(rule
[0]) && isxdigit(rule
[1]))
330 *ptr
= (*rule
++ - '0') << 4;
332 *ptr
= (tolower(*rule
++) - 'a' + 10) << 4;
335 *ptr
++ |= *rule
++ - '0';
337 *ptr
++ |= tolower(*rule
++) - 'a' + 10;
353 length
[num_values
] = ptr
- value
[num_values
];
367 * Figure out the function...
370 if (strcmp(name
, "match") == 0)
371 op
= MIME_MAGIC_MATCH
;
372 else if (strcmp(name
, "ascii") == 0)
373 op
= MIME_MAGIC_ASCII
;
374 else if (strcmp(name
, "printable") == 0)
375 op
= MIME_MAGIC_PRINTABLE
;
376 else if (strcmp(name
, "string") == 0)
377 op
= MIME_MAGIC_STRING
;
378 else if (strcmp(name
, "char") == 0)
379 op
= MIME_MAGIC_CHAR
;
380 else if (strcmp(name
, "short") == 0)
381 op
= MIME_MAGIC_SHORT
;
382 else if (strcmp(name
, "int") == 0)
384 else if (strcmp(name
, "locale") == 0)
385 op
= MIME_MAGIC_LOCALE
;
392 * This is just a filename match on the extension...
395 sprintf(value
[0], "*.%s", name
);
396 length
[0] = strlen(value
[0]);
398 op
= MIME_MAGIC_MATCH
;
402 * Add a rule for this operation.
405 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
408 temp
->invert
= invert
;
411 temp
->parent
= current
->parent
;
412 current
->next
= temp
;
417 temp
->prev
= current
;
419 if (logic
== MIME_MAGIC_NOP
)
422 * Add parenthetical grouping...
425 temp
->op
= MIME_MAGIC_OR
;
427 if ((temp
->child
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
430 temp
->child
->parent
= temp
;
433 logic
= MIME_MAGIC_OR
;
437 * Fill in data for the rule...
446 case MIME_MAGIC_MATCH
:
447 if (length
[0] > (sizeof(temp
->value
.matchv
) - 1))
449 strcpy(temp
->value
.matchv
, value
[0]);
451 case MIME_MAGIC_ASCII
:
452 case MIME_MAGIC_PRINTABLE
:
453 temp
->offset
= atoi(value
[0]);
454 temp
->length
= atoi(value
[1]);
455 if (temp
->length
> MIME_MAX_BUFFER
)
456 temp
->length
= MIME_MAX_BUFFER
;
458 case MIME_MAGIC_STRING
:
459 temp
->offset
= atoi(value
[0]);
460 if (length
[1] > sizeof(temp
->value
.stringv
))
462 temp
->length
= length
[1];
463 memcpy(temp
->value
.stringv
, value
[1], length
[1]);
465 case MIME_MAGIC_CHAR
:
466 temp
->offset
= atoi(value
[0]);
468 temp
->value
.charv
= value
[1][0];
470 temp
->value
.charv
= atoi(value
[1]);
472 case MIME_MAGIC_SHORT
:
473 temp
->offset
= atoi(value
[0]);
474 temp
->value
.shortv
= atoi(value
[1]);
476 case MIME_MAGIC_INT
:
477 temp
->offset
= atoi(value
[0]);
478 temp
->value
.intv
= atoi(value
[1]);
480 case MIME_MAGIC_LOCALE
:
481 if (length
[0] > (sizeof(temp
->value
.localev
) - 1))
484 strcpy(temp
->value
.localev
, value
[0]);
497 * 'mimeFileType()' - Determine the type of a file.
500 mime_type_t
* /* O - Type of file */
501 mimeFileType(mime_t
*mime
, /* I - MIME database */
502 const char *pathname
) /* I - Name of file to check */
504 int i
; /* Looping var */
505 FILE *fp
; /* File pointer */
506 mime_type_t
**types
; /* File types */
507 const char *filename
; /* Base filename of file */
511 * Range check input parameters...
514 if (mime
== NULL
|| pathname
== NULL
)
518 * Try to open the file...
521 if ((fp
= fopen(pathname
, "r")) == NULL
)
525 * Figure out the filename (without directory portion)...
528 if ((filename
= strrchr(pathname
, '/')) != NULL
)
534 * Then check it against all known types...
537 for (i
= mime
->num_types
, types
= mime
->types
; i
> 0; i
--, types
++)
538 if (checkrules(filename
, fp
, (*types
)->rules
))
542 * Finally, close the file and return a match (if any)...
555 * 'mimeType()' - Lookup a file type.
558 mime_type_t
* /* O - Matching file type definition */
559 mimeType(mime_t
*mime
, /* I - MIME database */
560 const char *super
, /* I - Super-type name */
561 const char *type
) /* I - Type name */
563 mime_type_t key
, /* MIME type search key*/
564 *keyptr
, /* Key pointer... */
565 **match
; /* Matching pointer */
568 * Range check input...
571 if (mime
== NULL
|| super
== NULL
|| type
== NULL
)
574 if (strlen(super
) > (MIME_MAX_SUPER
- 1) ||
575 strlen(type
) > (MIME_MAX_TYPE
- 1))
578 if (mime
->num_types
== 0)
582 * Lookup the type in the array...
585 strcpy(key
.super
, super
);
586 strcpy(key
.type
, type
);
589 match
= (mime_type_t
**)bsearch(&keyptr
, mime
->types
, mime
->num_types
,
590 sizeof(mime_type_t
*),
591 (int (*)(const void *, const void *))compare
);
601 * 'compare()' - Compare two MIME super/type names.
604 static int /* O - Result of comparison */
605 compare(mime_type_t
**t0
, /* I - First type */
606 mime_type_t
**t1
) /* I - Second type */
608 int i
; /* Result of comparison */
611 if ((i
= strcasecmp((*t0
)->super
, (*t1
)->super
)) == 0)
612 i
= strcasecmp((*t0
)->type
, (*t1
)->type
);
619 * 'checkrules()' - Check each rule in a list.
622 static int /* O - 1 if match, 0 if no match */
623 checkrules(const char *filename
, /* I - Filename */
624 FILE *fp
, /* I - File to check */
625 mime_magic_t
*rules
) /* I - Rules to check */
627 int n
; /* Looping var */
628 int logic
, /* Logic to apply */
629 result
, /* Result of test */
630 intv
; /* Integer value */
631 short shortv
; /* Short value */
632 unsigned char buffer
[MIME_MAX_BUFFER
],/* Input buffer */
633 *bufptr
; /* Current buffer position */
634 int bufoffset
, /* Offset in file for buffer */
635 buflength
; /* Length of data in buffer */
641 if (rules
->parent
== NULL
)
642 logic
= MIME_MAGIC_OR
;
644 logic
= rules
->parent
->op
;
648 while (rules
!= NULL
)
651 * Compute the result of this rule...
656 case MIME_MAGIC_MATCH
:
657 result
= patmatch(filename
, rules
->value
.matchv
);
660 case MIME_MAGIC_ASCII
:
662 * Load the buffer if necessary...
665 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
666 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
669 * Reload file buffer...
672 fseek(fp
, rules
->offset
, SEEK_SET
);
673 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
674 bufoffset
= rules
->offset
;
678 * Test for ASCII printable characters plus standard control chars.
681 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
682 n
= bufoffset
+ buflength
- rules
->offset
;
686 bufptr
= buffer
+ rules
->offset
- bufoffset
;
688 if ((*bufptr
>= 32 && *bufptr
<= 126) ||
689 (*bufptr
>= 8 && *bufptr
<= 10) ||
690 *bufptr
== 13 || *bufptr
== 26)
701 case MIME_MAGIC_PRINTABLE
:
703 * Load the buffer if necessary...
706 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
707 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
710 * Reload file buffer...
713 fseek(fp
, rules
->offset
, SEEK_SET
);
714 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
715 bufoffset
= rules
->offset
;
719 * Test for ASCII printable characters plus standard control chars.
722 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
723 n
= bufoffset
+ buflength
- rules
->offset
;
727 bufptr
= buffer
+ rules
->offset
- bufoffset
;
730 if ((*bufptr
>= 160 && *bufptr
<= 254) ||
731 (*bufptr
>= 32 && *bufptr
<= 126) ||
732 (*bufptr
>= 8 && *bufptr
<= 10) ||
733 *bufptr
== 13 || *bufptr
== 26)
744 case MIME_MAGIC_STRING
:
746 * Load the buffer if necessary...
749 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
750 (rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
753 * Reload file buffer...
756 fseek(fp
, rules
->offset
, SEEK_SET
);
757 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
758 bufoffset
= rules
->offset
;
762 * Compare the buffer against the string. If the file is too
763 * short then don't compare - it can't match...
766 if ((rules
->offset
+ rules
->length
) > (bufoffset
+ buflength
))
769 result
= (memcmp(buffer
+ rules
->offset
- bufoffset
,
770 rules
->value
.stringv
, rules
->length
) == 0);
773 case MIME_MAGIC_CHAR
:
775 * Load the buffer if necessary...
778 if (bufoffset
< 0 || rules
->offset
< bufoffset
)
781 * Reload file buffer...
784 fseek(fp
, rules
->offset
, SEEK_SET
);
785 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
786 bufoffset
= rules
->offset
;
790 * Compare the character values; if the file is too short, it
797 result
= (buffer
[rules
->offset
- bufoffset
] == rules
->value
.charv
);
800 case MIME_MAGIC_SHORT
:
802 * Load the buffer if necessary...
805 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
806 (rules
->offset
+ 2) > (bufoffset
+ buflength
))
809 * Reload file buffer...
812 fseek(fp
, rules
->offset
, SEEK_SET
);
813 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
814 bufoffset
= rules
->offset
;
818 * Compare the short values; if the file is too short, it
826 bufptr
= buffer
+ rules
->offset
- bufoffset
;
827 shortv
= (bufptr
[0] << 8) | bufptr
[1];
828 result
= (shortv
== rules
->value
.shortv
);
832 case MIME_MAGIC_INT
:
834 * Load the buffer if necessary...
837 if (bufoffset
< 0 || rules
->offset
< bufoffset
||
838 (rules
->offset
+ 4) > (bufoffset
+ buflength
))
841 * Reload file buffer...
844 fseek(fp
, rules
->offset
, SEEK_SET
);
845 buflength
= fread(buffer
, 1, sizeof(buffer
), fp
);
846 bufoffset
= rules
->offset
;
850 * Compare the int values; if the file is too short, it
858 bufptr
= buffer
+ rules
->offset
- bufoffset
;
859 intv
= (((((bufptr
[0] << 8) | bufptr
[1]) << 8) | bufptr
[2]) << 8) |
861 result
= (intv
== rules
->value
.intv
);
865 case MIME_MAGIC_LOCALE
:
866 result
= (strcmp(rules
->value
.localev
, setlocale(LC_ALL
, NULL
)) == 0);
870 if (rules
->child
!= NULL
)
871 result
= checkrules(filename
, fp
, rules
->child
);
878 * If the logic is inverted, invert the result...
885 * OK, now if the current logic is OR and this result is true, the this
886 * rule set is true. If the current logic is AND and this result is false,
887 * the the rule set is false...
890 if ((result
&& logic
== MIME_MAGIC_OR
) ||
891 (!result
&& logic
== MIME_MAGIC_AND
))
895 * Otherwise the jury is still out on this one, so move to the next rule.
906 * 'patmatch()' - Pattern matching...
909 static int /* O - 1 if match, 0 if no match */
910 patmatch(const char *s
, /* I - String to match against */
911 const char *pat
) /* I - Pattern to match against */
914 * Range check the input...
917 if (s
== NULL
|| pat
== NULL
)
921 * Loop through the pattern and match strings, and stop if we come to a
922 * point where the strings don't match or we find a complete match.
925 while (*s
!= '\0' && *pat
!= '\0')
930 * Wildcard - 0 or more characters...
935 return (1); /* Last pattern char is *, so everything matches now... */
938 * Test all remaining combinations until we get to the end of the string.
943 if (patmatch(s
, pat
))
949 else if (*pat
== '?')
952 * Wildcard - 1 character...
959 else if (*pat
== '[')
962 * Match a character from the input set [chars]...
966 while (*pat
!= ']' && *pat
!= '\0')
972 if (*pat
== ']' || *pat
== '\0')
975 while (*pat
!= ']' && *pat
!= '\0')
983 else if (*pat
== '\\')
986 * Handle quoted characters...
993 * Stop if the pattern and string don't match...
1001 * Done parsing the pattern and string; return 1 if the last character matches
1002 * and 0 otherwise...
1005 return (*s
== *pat
);
1010 * End of "$Id: type.c,v 1.9 1999/07/12 16:09:41 mike Exp $".