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