]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/type.c
Fixed all constant arrays to use "const" modifier.
[thirdparty/cups.git] / cups / type.c
1 /*
2 * "$Id: type.c,v 1.9 1999/07/12 16:09:41 mike Exp $"
3 *
4 * MIME typing routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-1999 by Easy Software Products, all rights reserved.
7 *
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
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
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...
33 */
34
35 /*
36 * Include necessary headers...
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <locale.h>
43
44 #include "string.h"
45 #include "mime.h"
46
47
48 /*
49 * Local functions...
50 */
51
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 *);
55
56
57 /*
58 * 'mimeAddType()' - Add a MIME type to a database.
59 */
60
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 */
65 {
66 mime_type_t *temp, /* New MIME type */
67 **types; /* New MIME types array */
68
69
70 /*
71 * Range check input...
72 */
73
74 if (mime == NULL || super == NULL || type == NULL)
75 return (NULL);
76
77 if (strlen(super) > (MIME_MAX_SUPER - 1) ||
78 strlen(type) > (MIME_MAX_TYPE - 1))
79 return (NULL);
80
81 /*
82 * See if the type already exists; if so, return the existing type...
83 */
84
85 if ((temp = mimeType(mime, super, type)) != NULL)
86 return (temp);
87
88 /*
89 * The type doesn't exist; add it...
90 */
91
92 if ((temp = calloc(1, sizeof(mime_type_t))) == NULL)
93 return (NULL);
94
95 if (mime->num_types == 0)
96 types = (mime_type_t **)malloc(sizeof(mime_type_t *));
97 else
98 types = (mime_type_t **)realloc(mime->types, sizeof(mime_type_t *) * (mime->num_types + 1));
99
100 if (types == NULL)
101 {
102 free(temp);
103 return (NULL);
104 }
105
106 mime->types = types;
107 types += mime->num_types;
108 mime->num_types ++;
109
110 *types = temp;
111 strcpy(temp->super, super);
112 strcpy(temp->type, type);
113
114 if (mime->num_types > 1)
115 qsort(mime->types, mime->num_types, sizeof(mime_type_t *),
116 (int (*)(const void *, const void *))compare);
117
118 return (temp);
119 }
120
121
122 /*
123 * 'mimeAddRule()' - Add a detection rule for a file type.
124 */
125
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 */
129 {
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 */
141
142
143 /*
144 * Range check input...
145 */
146
147 if (mt == NULL || rule == NULL)
148 return (-1);
149
150 /*
151 * Find the last rule in the top-level of the rules tree.
152 */
153
154 for (current = mt->rules; current != NULL; current = current->next)
155 if (current->next == NULL)
156 break;
157
158 /*
159 * Parse the rules string. Most rules are either a file extension or a
160 * comparison function:
161 *
162 * extension
163 * function(parameters)
164 */
165
166 logic = MIME_MAGIC_NOP;
167 invert = 0;
168
169 while (*rule != '\0')
170 {
171 while (isspace(*rule))
172 rule ++;
173
174 if (*rule == '(')
175 {
176 logic = MIME_MAGIC_NOP;
177 rule ++;
178 }
179 else if (*rule == ')')
180 {
181 if (current == NULL || current->parent == NULL)
182 return (-1);
183
184 current = current->parent;
185
186 if (current->parent == NULL)
187 logic = MIME_MAGIC_OR;
188 else
189 logic = current->parent->op;
190
191 rule ++;
192 }
193 else if (*rule == '+' && current != NULL)
194 {
195 if (logic != MIME_MAGIC_AND &&
196 current != NULL && current->prev != NULL && current->prev->prev != NULL)
197 {
198 /*
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...
201 */
202
203 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
204 return (-1);
205
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;
211
212 current->prev = NULL;
213 current->parent = temp;
214 }
215 else
216 current->parent->op = MIME_MAGIC_AND;
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 while (current->prev != NULL)
241 {
242 current->parent = temp;
243 current = current->prev;
244 }
245
246 current->parent = temp;
247 temp->op = MIME_MAGIC_AND;
248 temp->child = current;
249
250 mt->rules = current = temp;
251 }
252 else
253 {
254 /*
255 * This isn't the top rule, so go up one level...
256 */
257
258 current = current->parent;
259 }
260 }
261
262 logic = MIME_MAGIC_OR;
263 rule ++;
264 }
265 else if (*rule == '!')
266 {
267 invert = 1;
268 rule ++;
269 }
270 else if (isalnum(*rule))
271 {
272 /*
273 * Read an extension name or a function...
274 */
275
276 for (ptr = name; isalnum(*rule) && (ptr - name) < (sizeof(name) - 1);)
277 *ptr++ = *rule++;
278
279 *ptr = '\0';
280 num_values = 0;
281
282 if (*rule == '(')
283 {
284 /*
285 * Read function parameters...
286 */
287
288 rule ++;
289 for (num_values = 0; num_values < 2; num_values ++)
290 {
291 ptr = value[num_values];
292
293 while ((ptr - value[num_values]) < (sizeof(value[0]) - 1) &&
294 *rule != '\0' && *rule != ',' && *rule != ')')
295 {
296 if (isspace(*rule))
297 {
298 /*
299 * Ignore whitespace...
300 */
301
302 rule ++;
303 continue;
304 }
305 else if (*rule == '\"' || *rule == '\'')
306 {
307 /*
308 * Copy quoted strings literally...
309 */
310
311 quote = *rule++;
312
313 while (*rule != '\0' && *rule != quote)
314 *ptr++ = *rule++;
315
316 if (*rule == quote)
317 rule ++;
318 else
319 return (-1);
320 }
321 else if (*rule == '<')
322 {
323 rule ++;
324
325 while (*rule != '>' && *rule != '\0')
326 {
327 if (isxdigit(rule[0]) && isxdigit(rule[1]))
328 {
329 if (isdigit(*rule))
330 *ptr = (*rule++ - '0') << 4;
331 else
332 *ptr = (tolower(*rule++) - 'a' + 10) << 4;
333
334 if (isdigit(*rule))
335 *ptr++ |= *rule++ - '0';
336 else
337 *ptr++ |= tolower(*rule++) - 'a' + 10;
338 }
339 else
340 return (-1);
341 }
342
343 if (*rule == '>')
344 rule ++;
345 else
346 return (-1);
347 }
348 else
349 *ptr++ = *rule++;
350 }
351
352 *ptr = '\0';
353 length[num_values] = ptr - value[num_values];
354
355 if (*rule != ',')
356 break;
357
358 rule ++;
359 }
360
361 if (*rule != ')')
362 return (-1);
363
364 rule ++;
365
366 /*
367 * Figure out the function...
368 */
369
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)
383 op = MIME_MAGIC_INT;
384 else if (strcmp(name, "locale") == 0)
385 op = MIME_MAGIC_LOCALE;
386 else
387 return (-1);
388 }
389 else
390 {
391 /*
392 * This is just a filename match on the extension...
393 */
394
395 sprintf(value[0], "*.%s", name);
396 length[0] = strlen(value[0]);
397 num_values = 1;
398 op = MIME_MAGIC_MATCH;
399 }
400
401 /*
402 * Add a rule for this operation.
403 */
404
405 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
406 return (-1);
407
408 temp->invert = invert;
409 if (current != NULL)
410 {
411 temp->parent = current->parent;
412 current->next = temp;
413 }
414 else
415 mt->rules = temp;
416
417 temp->prev = current;
418
419 if (logic == MIME_MAGIC_NOP)
420 {
421 /*
422 * Add parenthetical grouping...
423 */
424
425 temp->op = MIME_MAGIC_OR;
426
427 if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
428 return (-1);
429
430 temp->child->parent = temp;
431
432 temp = temp->child;
433 logic = MIME_MAGIC_OR;
434 }
435
436 /*
437 * Fill in data for the rule...
438 */
439
440 current = temp;
441 temp->op = op;
442 invert = 0;
443
444 switch (op)
445 {
446 case MIME_MAGIC_MATCH :
447 if (length[0] > (sizeof(temp->value.matchv) - 1))
448 return (-1);
449 strcpy(temp->value.matchv, value[0]);
450 break;
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;
457 break;
458 case MIME_MAGIC_STRING :
459 temp->offset = atoi(value[0]);
460 if (length[1] > sizeof(temp->value.stringv))
461 return (-1);
462 temp->length = length[1];
463 memcpy(temp->value.stringv, value[1], length[1]);
464 break;
465 case MIME_MAGIC_CHAR :
466 temp->offset = atoi(value[0]);
467 if (length[1] == 1)
468 temp->value.charv = value[1][0];
469 else
470 temp->value.charv = atoi(value[1]);
471 break;
472 case MIME_MAGIC_SHORT :
473 temp->offset = atoi(value[0]);
474 temp->value.shortv = atoi(value[1]);
475 break;
476 case MIME_MAGIC_INT :
477 temp->offset = atoi(value[0]);
478 temp->value.intv = atoi(value[1]);
479 break;
480 case MIME_MAGIC_LOCALE :
481 if (length[0] > (sizeof(temp->value.localev) - 1))
482 return (-1);
483
484 strcpy(temp->value.localev, value[0]);
485 break;
486 }
487 }
488 else
489 break;
490 }
491
492 return (0);
493 }
494
495
496 /*
497 * 'mimeFileType()' - Determine the type of a file.
498 */
499
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 */
503 {
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 */
508
509
510 /*
511 * Range check input parameters...
512 */
513
514 if (mime == NULL || pathname == NULL)
515 return (NULL);
516
517 /*
518 * Try to open the file...
519 */
520
521 if ((fp = fopen(pathname, "r")) == NULL)
522 return (NULL);
523
524 /*
525 * Figure out the filename (without directory portion)...
526 */
527
528 if ((filename = strrchr(pathname, '/')) != NULL)
529 filename ++;
530 else
531 filename = pathname;
532
533 /*
534 * Then check it against all known types...
535 */
536
537 for (i = mime->num_types, types = mime->types; i > 0; i --, types ++)
538 if (checkrules(filename, fp, (*types)->rules))
539 break;
540
541 /*
542 * Finally, close the file and return a match (if any)...
543 */
544
545 fclose(fp);
546
547 if (i > 0)
548 return (*types);
549 else
550 return (NULL);
551 }
552
553
554 /*
555 * 'mimeType()' - Lookup a file type.
556 */
557
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 */
562 {
563 mime_type_t key, /* MIME type search key*/
564 *keyptr, /* Key pointer... */
565 **match; /* Matching pointer */
566
567 /*
568 * Range check input...
569 */
570
571 if (mime == NULL || super == NULL || type == NULL)
572 return (NULL);
573
574 if (strlen(super) > (MIME_MAX_SUPER - 1) ||
575 strlen(type) > (MIME_MAX_TYPE - 1))
576 return (NULL);
577
578 if (mime->num_types == 0)
579 return (NULL);
580
581 /*
582 * Lookup the type in the array...
583 */
584
585 strcpy(key.super, super);
586 strcpy(key.type, type);
587 keyptr = &key;
588
589 match = (mime_type_t **)bsearch(&keyptr, mime->types, mime->num_types,
590 sizeof(mime_type_t *),
591 (int (*)(const void *, const void *))compare);
592
593 if (match == NULL)
594 return (NULL);
595 else
596 return (*match);
597 }
598
599
600 /*
601 * 'compare()' - Compare two MIME super/type names.
602 */
603
604 static int /* O - Result of comparison */
605 compare(mime_type_t **t0, /* I - First type */
606 mime_type_t **t1) /* I - Second type */
607 {
608 int i; /* Result of comparison */
609
610
611 if ((i = strcasecmp((*t0)->super, (*t1)->super)) == 0)
612 i = strcasecmp((*t0)->type, (*t1)->type);
613
614 return (i);
615 }
616
617
618 /*
619 * 'checkrules()' - Check each rule in a list.
620 */
621
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 */
626 {
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 */
636
637
638 if (rules == NULL)
639 return (0);
640
641 if (rules->parent == NULL)
642 logic = MIME_MAGIC_OR;
643 else
644 logic = rules->parent->op;
645
646 bufoffset = -1;
647
648 while (rules != NULL)
649 {
650 /*
651 * Compute the result of this rule...
652 */
653
654 switch (rules->op)
655 {
656 case MIME_MAGIC_MATCH :
657 result = patmatch(filename, rules->value.matchv);
658 break;
659
660 case MIME_MAGIC_ASCII :
661 /*
662 * Load the buffer if necessary...
663 */
664
665 if (bufoffset < 0 || rules->offset < bufoffset ||
666 (rules->offset + rules->length) > (bufoffset + buflength))
667 {
668 /*
669 * Reload file buffer...
670 */
671
672 fseek(fp, rules->offset, SEEK_SET);
673 buflength = fread(buffer, 1, sizeof(buffer), fp);
674 bufoffset = rules->offset;
675 }
676
677 /*
678 * Test for ASCII printable characters plus standard control chars.
679 */
680
681 if ((rules->offset + rules->length) > (bufoffset + buflength))
682 n = bufoffset + buflength - rules->offset;
683 else
684 n = rules->length;
685
686 bufptr = buffer + rules->offset - bufoffset;
687 while (n > 0)
688 if ((*bufptr >= 32 && *bufptr <= 126) ||
689 (*bufptr >= 8 && *bufptr <= 10) ||
690 *bufptr == 13 || *bufptr == 26)
691 {
692 n --;
693 bufptr ++;
694 }
695 else
696 break;
697
698 result = (n == 0);
699 break;
700
701 case MIME_MAGIC_PRINTABLE :
702 /*
703 * Load the buffer if necessary...
704 */
705
706 if (bufoffset < 0 || rules->offset < bufoffset ||
707 (rules->offset + rules->length) > (bufoffset + buflength))
708 {
709 /*
710 * Reload file buffer...
711 */
712
713 fseek(fp, rules->offset, SEEK_SET);
714 buflength = fread(buffer, 1, sizeof(buffer), fp);
715 bufoffset = rules->offset;
716 }
717
718 /*
719 * Test for ASCII printable characters plus standard control chars.
720 */
721
722 if ((rules->offset + rules->length) > (bufoffset + buflength))
723 n = bufoffset + buflength - rules->offset;
724 else
725 n = rules->length;
726
727 bufptr = buffer + rules->offset - bufoffset;
728
729 while (n > 0)
730 if ((*bufptr >= 160 && *bufptr <= 254) ||
731 (*bufptr >= 32 && *bufptr <= 126) ||
732 (*bufptr >= 8 && *bufptr <= 10) ||
733 *bufptr == 13 || *bufptr == 26)
734 {
735 n --;
736 bufptr ++;
737 }
738 else
739 break;
740
741 result = (n == 0);
742 break;
743
744 case MIME_MAGIC_STRING :
745 /*
746 * Load the buffer if necessary...
747 */
748
749 if (bufoffset < 0 || rules->offset < bufoffset ||
750 (rules->offset + rules->length) > (bufoffset + buflength))
751 {
752 /*
753 * Reload file buffer...
754 */
755
756 fseek(fp, rules->offset, SEEK_SET);
757 buflength = fread(buffer, 1, sizeof(buffer), fp);
758 bufoffset = rules->offset;
759 }
760
761 /*
762 * Compare the buffer against the string. If the file is too
763 * short then don't compare - it can't match...
764 */
765
766 if ((rules->offset + rules->length) > (bufoffset + buflength))
767 result = 0;
768 else
769 result = (memcmp(buffer + rules->offset - bufoffset,
770 rules->value.stringv, rules->length) == 0);
771 break;
772
773 case MIME_MAGIC_CHAR :
774 /*
775 * Load the buffer if necessary...
776 */
777
778 if (bufoffset < 0 || rules->offset < bufoffset)
779 {
780 /*
781 * Reload file buffer...
782 */
783
784 fseek(fp, rules->offset, SEEK_SET);
785 buflength = fread(buffer, 1, sizeof(buffer), fp);
786 bufoffset = rules->offset;
787 }
788
789 /*
790 * Compare the character values; if the file is too short, it
791 * can't match...
792 */
793
794 if (buflength < 1)
795 result = 0;
796 else
797 result = (buffer[rules->offset - bufoffset] == rules->value.charv);
798 break;
799
800 case MIME_MAGIC_SHORT :
801 /*
802 * Load the buffer if necessary...
803 */
804
805 if (bufoffset < 0 || rules->offset < bufoffset ||
806 (rules->offset + 2) > (bufoffset + buflength))
807 {
808 /*
809 * Reload file buffer...
810 */
811
812 fseek(fp, rules->offset, SEEK_SET);
813 buflength = fread(buffer, 1, sizeof(buffer), fp);
814 bufoffset = rules->offset;
815 }
816
817 /*
818 * Compare the short values; if the file is too short, it
819 * can't match...
820 */
821
822 if (buflength < 2)
823 result = 0;
824 else
825 {
826 bufptr = buffer + rules->offset - bufoffset;
827 shortv = (bufptr[0] << 8) | bufptr[1];
828 result = (shortv == rules->value.shortv);
829 }
830 break;
831
832 case MIME_MAGIC_INT :
833 /*
834 * Load the buffer if necessary...
835 */
836
837 if (bufoffset < 0 || rules->offset < bufoffset ||
838 (rules->offset + 4) > (bufoffset + buflength))
839 {
840 /*
841 * Reload file buffer...
842 */
843
844 fseek(fp, rules->offset, SEEK_SET);
845 buflength = fread(buffer, 1, sizeof(buffer), fp);
846 bufoffset = rules->offset;
847 }
848
849 /*
850 * Compare the int values; if the file is too short, it
851 * can't match...
852 */
853
854 if (buflength < 4)
855 result = 0;
856 else
857 {
858 bufptr = buffer + rules->offset - bufoffset;
859 intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) | bufptr[2]) << 8) |
860 bufptr[3];;
861 result = (intv == rules->value.intv);
862 }
863 break;
864
865 case MIME_MAGIC_LOCALE :
866 result = (strcmp(rules->value.localev, setlocale(LC_ALL, NULL)) == 0);
867 break;
868
869 default :
870 if (rules->child != NULL)
871 result = checkrules(filename, fp, rules->child);
872 else
873 result = 0;
874 break;
875 }
876
877 /*
878 * If the logic is inverted, invert the result...
879 */
880
881 if (rules->invert)
882 result = !result;
883
884 /*
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...
888 */
889
890 if ((result && logic == MIME_MAGIC_OR) ||
891 (!result && logic == MIME_MAGIC_AND))
892 return (result);
893
894 /*
895 * Otherwise the jury is still out on this one, so move to the next rule.
896 */
897
898 rules = rules->next;
899 }
900
901 return (result);
902 }
903
904
905 /*
906 * 'patmatch()' - Pattern matching...
907 */
908
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 */
912 {
913 /*
914 * Range check the input...
915 */
916
917 if (s == NULL || pat == NULL)
918 return (0);
919
920 /*
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.
923 */
924
925 while (*s != '\0' && *pat != '\0')
926 {
927 if (*pat == '*')
928 {
929 /*
930 * Wildcard - 0 or more characters...
931 */
932
933 pat ++;
934 if (*pat == '\0')
935 return (1); /* Last pattern char is *, so everything matches now... */
936
937 /*
938 * Test all remaining combinations until we get to the end of the string.
939 */
940
941 while (*s != '\0')
942 {
943 if (patmatch(s, pat))
944 return (1);
945
946 s ++;
947 }
948 }
949 else if (*pat == '?')
950 {
951 /*
952 * Wildcard - 1 character...
953 */
954
955 pat ++;
956 s ++;
957 continue;
958 }
959 else if (*pat == '[')
960 {
961 /*
962 * Match a character from the input set [chars]...
963 */
964
965 pat ++;
966 while (*pat != ']' && *pat != '\0')
967 if (*s == *pat)
968 break;
969 else
970 pat ++;
971
972 if (*pat == ']' || *pat == '\0')
973 return (0);
974
975 while (*pat != ']' && *pat != '\0')
976 pat ++;
977
978 if (*pat == ']')
979 pat ++;
980
981 continue;
982 }
983 else if (*pat == '\\')
984 {
985 /*
986 * Handle quoted characters...
987 */
988
989 pat ++;
990 }
991
992 /*
993 * Stop if the pattern and string don't match...
994 */
995
996 if (*pat++ != *s++)
997 return (0);
998 }
999
1000 /*
1001 * Done parsing the pattern and string; return 1 if the last character matches
1002 * and 0 otherwise...
1003 */
1004
1005 return (*s == *pat);
1006 }
1007
1008
1009 /*
1010 * End of "$Id: type.c,v 1.9 1999/07/12 16:09:41 mike Exp $".
1011 */