]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/type.c
2 * "$Id: type.c 6649 2007-07-11 21:46:42Z mike $"
4 * MIME typing routines for the Common UNIX Printing System (CUPS).
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
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/".
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...
27 * Include necessary headers...
35 #include <cups/string.h>
37 #include <cups/debug.h>
44 typedef struct _mime_filebuf_s
/**** File buffer for MIME typing ****/
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 */
57 static int compare_types(mime_type_t
*t0
, mime_type_t
*t1
);
58 static int checkrules(const char *filename
, _mime_filebuf_t
*fb
,
60 static int patmatch(const char *s
, const char *pat
);
64 * 'mimeAddType()' - Add a MIME type to a database.
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 */
72 mime_type_t
*temp
; /* New MIME type */
76 * Range check input...
79 if (!mime
|| !super
|| !type
)
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...
94 mime
->types
= cupsArrayNew((cups_array_func_t
)compare_types
, NULL
);
99 if ((temp
= calloc(1, sizeof(mime_type_t
) - MIME_MAX_TYPE
+
100 strlen(type
) + 1)) == NULL
)
103 strlcpy(temp
->super
, super
, sizeof(temp
->super
));
104 strcpy(temp
->type
, type
); /* Safe: temp->type is allocated */
106 cupsArrayAdd(mime
->types
, temp
);
113 * 'mimeAddTypeRule()' - Add a detection rule for a file type.
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 */
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 */
134 * Range check input...
141 * Find the last rule in the top-level of the rules tree.
144 for (current
= mt
->rules
; current
; current
= current
->next
)
149 * Parse the rules string. Most rules are either a file extension or a
150 * comparison function:
153 * function(parameters)
156 logic
= MIME_MAGIC_NOP
;
159 DEBUG_printf(("%s/%s: %s\n", mt
->super
, mt
->type
, rule
));
161 while (*rule
!= '\0')
163 while (isspace(*rule
& 255))
168 DEBUG_puts("new parenthesis group");
169 logic
= MIME_MAGIC_NOP
;
172 else if (*rule
== ')')
174 DEBUG_puts("close paren...");
175 if (current
== NULL
|| current
->parent
== NULL
)
178 current
= current
->parent
;
180 if (current
->parent
== NULL
)
181 logic
= MIME_MAGIC_OR
;
183 logic
= current
->parent
->op
;
187 else if (*rule
== '+' && current
!= NULL
)
189 if (logic
!= MIME_MAGIC_AND
&&
190 current
!= NULL
&& current
->prev
!= NULL
&&
191 current
->prev
->prev
!= NULL
)
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...
198 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
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
;
207 current
->prev
= NULL
;
208 current
->parent
= temp
;
210 DEBUG_printf(("creating new AND group %p...\n", temp
));
214 DEBUG_printf(("setting group %p op to AND...\n", current
->parent
));
215 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 DEBUG_printf(("creating new AND group %p inside OR group\n", temp
));
242 while (current
->prev
!= NULL
)
244 current
->parent
= temp
;
245 current
= current
->prev
;
248 current
->parent
= temp
;
249 temp
->op
= MIME_MAGIC_AND
;
250 temp
->child
= current
;
252 mt
->rules
= current
= temp
;
257 * This isn't the top rule, so go up one level...
260 DEBUG_puts("going up one level");
261 current
= current
->parent
;
265 logic
= MIME_MAGIC_OR
;
268 else if (*rule
== '!')
274 else if (isalnum(*rule
& 255))
277 * Read an extension name or a function...
281 while (isalnum(*rule
& 255) && (ptr
- name
) < (sizeof(name
) - 1))
290 * Read function parameters...
295 num_values
< (sizeof(value
) / sizeof(value
[0]));
298 ptr
= value
[num_values
];
300 while ((ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1) &&
301 *rule
!= '\0' && *rule
!= ',' && *rule
!= ')')
303 if (isspace(*rule
& 255))
306 * Ignore whitespace...
312 else if (*rule
== '\"' || *rule
== '\'')
315 * Copy quoted strings literally...
320 while (*rule
!= '\0' && *rule
!= quote
&&
321 (ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1))
329 else if (*rule
== '<')
333 while (*rule
!= '>' && *rule
!= '\0' &&
334 (ptr
- value
[num_values
]) < (sizeof(value
[0]) - 1))
336 if (isxdigit(rule
[0] & 255) && isxdigit(rule
[1] & 255))
339 *ptr
= (*rule
++ - '0') << 4;
341 *ptr
= (tolower(*rule
++) - 'a' + 10) << 4;
344 *ptr
++ |= *rule
++ - '0';
346 *ptr
++ |= tolower(*rule
++) - 'a' + 10;
362 length
[num_values
] = ptr
- value
[num_values
];
376 * Figure out the function...
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"))
395 else if (!strcmp(name
, "locale"))
396 op
= MIME_MAGIC_LOCALE
;
397 else if (!strcmp(name
, "contains"))
398 op
= MIME_MAGIC_CONTAINS
;
405 * This is just a filename match on the extension...
408 snprintf(value
[0], sizeof(value
[0]), "*.%s", name
);
409 length
[0] = strlen(value
[0]);
411 op
= MIME_MAGIC_MATCH
;
415 * Add a rule for this operation.
418 if ((temp
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
421 temp
->invert
= invert
;
424 temp
->parent
= current
->parent
;
425 current
->next
= temp
;
430 temp
->prev
= current
;
432 if (logic
== MIME_MAGIC_NOP
)
435 * Add parenthetical grouping...
438 DEBUG_printf(("making new OR group %p for parenthesis...\n", temp
));
440 temp
->op
= MIME_MAGIC_OR
;
442 if ((temp
->child
= calloc(1, sizeof(mime_magic_t
))) == NULL
)
445 temp
->child
->parent
= temp
;
448 logic
= MIME_MAGIC_OR
;
451 DEBUG_printf(("adding %p: %s, op = %d, logic = %d, invert = %d\n",
452 temp
, name
, op
, logic
, invert
));
455 * Fill in data for the rule...
464 case MIME_MAGIC_MATCH
:
465 if (length
[0] > (sizeof(temp
->value
.matchv
) - 1))
467 strcpy(temp
->value
.matchv
, value
[0]);
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
;
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
))
481 temp
->length
= length
[1];
482 memcpy(temp
->value
.stringv
, value
[1], length
[1]);
484 case MIME_MAGIC_CHAR
:
485 temp
->offset
= strtol(value
[0], NULL
, 0);
487 temp
->value
.charv
= value
[1][0];
489 temp
->value
.charv
= (char)strtol(value
[1], NULL
, 0);
491 case MIME_MAGIC_SHORT
:
492 temp
->offset
= strtol(value
[0], NULL
, 0);
493 temp
->value
.shortv
= (short)strtol(value
[1], NULL
, 0);
495 case MIME_MAGIC_INT
:
496 temp
->offset
= strtol(value
[0], NULL
, 0);
497 temp
->value
.intv
= (int)strtol(value
[1], NULL
, 0);
499 case MIME_MAGIC_LOCALE
:
500 if (length
[0] > (sizeof(temp
->value
.localev
) - 1))
503 strcpy(temp
->value
.localev
, value
[0]);
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
))
510 temp
->length
= length
[2];
511 memcpy(temp
->value
.stringv
, value
[2], length
[2]);
524 * 'mimeFileType()' - Determine the type of a file.
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? */
533 _mime_filebuf_t fb
; /* File buffer */
534 const char *base
; /* Base filename of file */
535 mime_type_t
*type
; /* File type */
538 DEBUG_printf(("mimeFileType(mime=%p, pathname=\"%s\", filename=\"%s\", "
540 mime
, pathname
? pathname
: "(nil)",
541 filename
? filename
: "(nil)",
545 * Range check input parameters...
548 if (!mime
|| !pathname
)
552 * Try to open the file...
555 if ((fb
.fp
= cupsFileOpen(pathname
, "r")) == NULL
)
562 * Figure out the base filename (without directory portion)...
567 if ((base
= strrchr(filename
, '/')) != NULL
)
572 else if ((base
= strrchr(pathname
, '/')) != NULL
)
578 * Then check it against all known types...
581 for (type
= (mime_type_t
*)cupsArrayFirst(mime
->types
);
583 type
= (mime_type_t
*)cupsArrayNext(mime
->types
))
584 if (checkrules(base
, &fb
, type
->rules
))
588 * Finally, close the file and return a match (if any)...
592 *compression
= cupsFileCompression(fb
.fp
);
594 cupsFileClose(fb
.fp
);
601 * 'mimeType()' - Lookup a file type.
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 */
609 mime_type_t key
; /* MIME type search key*/
613 * Range check input...
616 if (!mime
|| !super
|| !type
)
620 * Lookup the type in the array...
623 strlcpy(key
.super
, super
, sizeof(key
.super
));
624 strlcpy(key
.type
, type
, sizeof(key
.type
));
626 return ((mime_type_t
*)cupsArrayFind(mime
->types
, &key
));
631 * 'compare_types()' - Compare two MIME super/type names.
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 */
638 int i
; /* Result of comparison */
641 if ((i
= strcasecmp(t0
->super
, t1
->super
)) == 0)
642 i
= strcasecmp(t0
->type
, t1
->type
);
649 * 'checkrules()' - Check each rule in a list.
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 */
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 */
665 const char * const debug_tests
[] = /* Test names... */
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 */
684 DEBUG_printf(("checkrules(filename=\"%s\", fb=%p, rules=%p)\n", filename
,
690 if (rules
->parent
== NULL
)
691 logic
= MIME_MAGIC_OR
;
693 logic
= rules
->parent
->op
;
697 while (rules
!= NULL
)
700 * Compute the result of this rule...
705 case MIME_MAGIC_MATCH
:
706 result
= patmatch(filename
, rules
->value
.matchv
);
709 case MIME_MAGIC_ASCII
:
711 * Load the buffer if necessary...
714 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
715 (rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
718 * Reload file buffer...
721 cupsFileSeek(fb
->fp
, rules
->offset
);
722 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
724 fb
->offset
= rules
->offset
;
728 * Test for ASCII printable characters plus standard control chars.
731 if ((rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
732 n
= fb
->offset
+ fb
->length
- rules
->offset
;
736 bufptr
= fb
->buffer
+ rules
->offset
- fb
->offset
;
738 if ((*bufptr
>= 32 && *bufptr
<= 126) ||
739 (*bufptr
>= 8 && *bufptr
<= 13) ||
740 *bufptr
== 26 || *bufptr
== 27)
751 case MIME_MAGIC_PRINTABLE
:
753 * Load the buffer if necessary...
756 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
757 (rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
760 * Reload file buffer...
763 cupsFileSeek(fb
->fp
, rules
->offset
);
764 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
766 fb
->offset
= rules
->offset
;
770 * Test for 8-bit printable characters plus standard control chars.
773 if ((rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
774 n
= fb
->offset
+ fb
->length
- rules
->offset
;
778 bufptr
= fb
->buffer
+ rules
->offset
- fb
->offset
;
781 if (*bufptr
>= 128 ||
782 (*bufptr
>= 32 && *bufptr
<= 126) ||
783 (*bufptr
>= 8 && *bufptr
<= 13) ||
784 *bufptr
== 26 || *bufptr
== 27)
795 case MIME_MAGIC_STRING
:
796 DEBUG_printf((" string(%d, \"%s\")\n", rules
->offset
,
797 rules
->value
.stringv
));
800 * Load the buffer if necessary...
803 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
804 (rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
807 * Reload file buffer...
810 cupsFileSeek(fb
->fp
, rules
->offset
);
811 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
813 fb
->offset
= rules
->offset
;
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]));
822 * Compare the buffer against the string. If the file is too
823 * short then don't compare - it can't match...
826 if ((rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
829 result
= (memcmp(fb
->buffer
+ rules
->offset
- fb
->offset
,
830 rules
->value
.stringv
, rules
->length
) == 0);
831 DEBUG_printf((" result=%d\n", result
));
834 case MIME_MAGIC_ISTRING
:
836 * Load the buffer if necessary...
839 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
840 (rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
843 * Reload file buffer...
846 cupsFileSeek(fb
->fp
, rules
->offset
);
847 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
849 fb
->offset
= rules
->offset
;
853 * Compare the buffer against the string. If the file is too
854 * short then don't compare - it can't match...
857 if ((rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
860 result
= (strncasecmp((char *)fb
->buffer
+ rules
->offset
-
862 rules
->value
.stringv
, rules
->length
) == 0);
865 case MIME_MAGIC_CHAR
:
867 * Load the buffer if necessary...
870 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
)
873 * Reload file buffer...
876 cupsFileSeek(fb
->fp
, rules
->offset
);
877 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
879 fb
->offset
= rules
->offset
;
883 * Compare the character values; if the file is too short, it
890 result
= (fb
->buffer
[rules
->offset
- fb
->offset
] ==
894 case MIME_MAGIC_SHORT
:
896 * Load the buffer if necessary...
899 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
900 (rules
->offset
+ 2) > (fb
->offset
+ fb
->length
))
903 * Reload file buffer...
906 cupsFileSeek(fb
->fp
, rules
->offset
);
907 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
909 fb
->offset
= rules
->offset
;
913 * Compare the short values; if the file is too short, it
921 bufptr
= fb
->buffer
+ rules
->offset
- fb
->offset
;
922 shortv
= (bufptr
[0] << 8) | bufptr
[1];
923 result
= (shortv
== rules
->value
.shortv
);
927 case MIME_MAGIC_INT
:
929 * Load the buffer if necessary...
932 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
933 (rules
->offset
+ 4) > (fb
->offset
+ fb
->length
))
936 * Reload file buffer...
939 cupsFileSeek(fb
->fp
, rules
->offset
);
940 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
942 fb
->offset
= rules
->offset
;
946 * Compare the int values; if the file is too short, it
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
);
961 case MIME_MAGIC_LOCALE
:
962 #if defined(WIN32) || defined(__EMX__) || defined(__APPLE__)
963 result
= (strcmp(rules
->value
.localev
,
964 setlocale(LC_ALL
, "")) == 0);
966 result
= (strcmp(rules
->value
.localev
,
967 setlocale(LC_MESSAGES
, "")) == 0);
968 #endif /* __APPLE__ */
971 case MIME_MAGIC_CONTAINS
:
973 * Load the buffer if necessary...
976 if (fb
->offset
< 0 || rules
->offset
< fb
->offset
||
977 (rules
->offset
+ rules
->region
) > (fb
->offset
+ fb
->length
))
980 * Reload file buffer...
983 cupsFileSeek(fb
->fp
, rules
->offset
);
984 fb
->length
= cupsFileRead(fb
->fp
, (char *)fb
->buffer
,
986 fb
->offset
= rules
->offset
;
990 * Compare the buffer against the string. If the file is too
991 * short then don't compare - it can't match...
994 if ((rules
->offset
+ rules
->length
) > (fb
->offset
+ fb
->length
))
998 if (fb
->length
> rules
->region
)
999 region
= rules
->region
- rules
->length
;
1001 region
= fb
->length
- rules
->length
;
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)
1012 if (rules
->child
!= NULL
)
1013 result
= checkrules(filename
, fb
, rules
->child
);
1020 * If the logic is inverted, invert the result...
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...
1032 DEBUG_printf((" result of test %p (MIME_MAGIC_%s) is %d\n", rules
,
1033 debug_tests
[rules
->op
], result
));
1035 if ((result
&& logic
== MIME_MAGIC_OR
) ||
1036 (!result
&& logic
== MIME_MAGIC_AND
))
1040 * Otherwise the jury is still out on this one, so move to the next rule.
1043 rules
= rules
->next
;
1051 * 'patmatch()' - Pattern matching...
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 */
1059 * Range check the input...
1062 if (s
== NULL
|| pat
== NULL
)
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.
1070 while (*s
!= '\0' && *pat
!= '\0')
1075 * Wildcard - 0 or more characters...
1080 return (1); /* Last pattern char is *, so everything matches... */
1083 * Test all remaining combinations until we get to the end of the string.
1088 if (patmatch(s
, pat
))
1094 else if (*pat
== '?')
1097 * Wildcard - 1 character...
1104 else if (*pat
== '[')
1107 * Match a character from the input set [chars]...
1111 while (*pat
!= ']' && *pat
!= '\0')
1117 if (*pat
== ']' || *pat
== '\0')
1120 while (*pat
!= ']' && *pat
!= '\0')
1128 else if (*pat
== '\\')
1131 * Handle quoted characters...
1138 * Stop if the pattern and string don't match...
1146 * Done parsing the pattern and string; return 1 if the last character
1147 * matches and 0 otherwise...
1150 return (*s
== *pat
);
1155 * End of "$Id: type.c 6649 2007-07-11 21:46:42Z mike $".