]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/type.c
Merge changes from CUPS 1.4svn-r8227.
[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 *
c168a833 6 * Copyright 2007-2009 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 != ',')
c168a833
MS
367 {
368 num_values ++;
ef416fc2 369 break;
c168a833 370 }
ef416fc2 371
372 rule ++;
373 }
374
375 if (*rule != ')')
376 return (-1);
377
378 rule ++;
379
380 /*
381 * Figure out the function...
382 */
383
fa73b229 384 if (!strcmp(name, "match"))
ef416fc2 385 op = MIME_MAGIC_MATCH;
fa73b229 386 else if (!strcmp(name, "ascii"))
ef416fc2 387 op = MIME_MAGIC_ASCII;
fa73b229 388 else if (!strcmp(name, "printable"))
ef416fc2 389 op = MIME_MAGIC_PRINTABLE;
fa73b229 390 else if (!strcmp(name, "string"))
ef416fc2 391 op = MIME_MAGIC_STRING;
fa73b229 392 else if (!strcmp(name, "istring"))
ef416fc2 393 op = MIME_MAGIC_ISTRING;
fa73b229 394 else if (!strcmp(name, "char"))
ef416fc2 395 op = MIME_MAGIC_CHAR;
fa73b229 396 else if (!strcmp(name, "short"))
ef416fc2 397 op = MIME_MAGIC_SHORT;
fa73b229 398 else if (!strcmp(name, "int"))
ef416fc2 399 op = MIME_MAGIC_INT;
fa73b229 400 else if (!strcmp(name, "locale"))
ef416fc2 401 op = MIME_MAGIC_LOCALE;
fa73b229 402 else if (!strcmp(name, "contains"))
ef416fc2 403 op = MIME_MAGIC_CONTAINS;
dd1abb6b
MS
404 else if (!strcmp(name, "priority") && num_values == 1)
405 {
406 mt->priority = atoi(value[0]);
407 continue;
408 }
ef416fc2 409 else
410 return (-1);
411 }
412 else
413 {
414 /*
415 * This is just a filename match on the extension...
416 */
417
418 snprintf(value[0], sizeof(value[0]), "*.%s", name);
419 length[0] = strlen(value[0]);
ef416fc2 420 op = MIME_MAGIC_MATCH;
421 }
422
423 /*
424 * Add a rule for this operation.
425 */
426
427 if ((temp = calloc(1, sizeof(mime_magic_t))) == NULL)
428 return (-1);
429
430 temp->invert = invert;
431 if (current != NULL)
432 {
433 temp->parent = current->parent;
434 current->next = temp;
435 }
436 else
437 mt->rules = temp;
438
439 temp->prev = current;
440
441 if (logic == MIME_MAGIC_NOP)
442 {
443 /*
444 * Add parenthetical grouping...
445 */
446
75bd9771
MS
447 DEBUG_printf(("mimeAddTypeRule: Making new OR group %p for "
448 "parenthesis...\n", temp));
ef416fc2 449
450 temp->op = MIME_MAGIC_OR;
451
452 if ((temp->child = calloc(1, sizeof(mime_magic_t))) == NULL)
453 return (-1);
454
455 temp->child->parent = temp;
456
457 temp = temp->child;
458 logic = MIME_MAGIC_OR;
459 }
460
75bd9771
MS
461 DEBUG_printf(("mimeAddTypeRule: adding %p: %s, op=%d, logic=%d, "
462 "invert=%d\n", temp, name, op, logic, invert));
ef416fc2 463
464 /*
465 * Fill in data for the rule...
466 */
467
468 current = temp;
469 temp->op = op;
470 invert = 0;
471
472 switch (op)
473 {
474 case MIME_MAGIC_MATCH :
475 if (length[0] > (sizeof(temp->value.matchv) - 1))
476 return (-1);
477 strcpy(temp->value.matchv, value[0]);
478 break;
479 case MIME_MAGIC_ASCII :
480 case MIME_MAGIC_PRINTABLE :
481 temp->offset = strtol(value[0], NULL, 0);
482 temp->length = strtol(value[1], NULL, 0);
483 if (temp->length > MIME_MAX_BUFFER)
484 temp->length = MIME_MAX_BUFFER;
485 break;
486 case MIME_MAGIC_STRING :
487 case MIME_MAGIC_ISTRING :
488 temp->offset = strtol(value[0], NULL, 0);
489 if (length[1] > sizeof(temp->value.stringv))
490 return (-1);
491 temp->length = length[1];
492 memcpy(temp->value.stringv, value[1], length[1]);
493 break;
494 case MIME_MAGIC_CHAR :
495 temp->offset = strtol(value[0], NULL, 0);
496 if (length[1] == 1)
497 temp->value.charv = value[1][0];
498 else
499 temp->value.charv = (char)strtol(value[1], NULL, 0);
500 break;
501 case MIME_MAGIC_SHORT :
502 temp->offset = strtol(value[0], NULL, 0);
503 temp->value.shortv = (short)strtol(value[1], NULL, 0);
504 break;
505 case MIME_MAGIC_INT :
506 temp->offset = strtol(value[0], NULL, 0);
507 temp->value.intv = (int)strtol(value[1], NULL, 0);
508 break;
509 case MIME_MAGIC_LOCALE :
510 if (length[0] > (sizeof(temp->value.localev) - 1))
511 return (-1);
512
513 strcpy(temp->value.localev, value[0]);
514 break;
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))
519 return (-1);
520 temp->length = length[2];
521 memcpy(temp->value.stringv, value[2], length[2]);
522 break;
523 }
524 }
525 else
526 break;
527 }
528
529 return (0);
530}
531
532
533/*
534 * 'mimeFileType()' - Determine the type of a file.
535 */
536
537mime_type_t * /* O - Type of file */
538mimeFileType(mime_t *mime, /* I - MIME database */
4400e98d 539 const char *pathname, /* I - Name of file to check on disk */
540 const char *filename, /* I - Original filename or NULL */
ef416fc2 541 int *compression) /* O - Is the file compressed? */
542{
4400e98d 543 _mime_filebuf_t fb; /* File buffer */
544 const char *base; /* Base filename of file */
dd1abb6b
MS
545 mime_type_t *type, /* File type */
546 *best; /* Best match */
ef416fc2 547
548
b423cd4c 549 DEBUG_printf(("mimeFileType(mime=%p, pathname=\"%s\", filename=\"%s\", "
75bd9771 550 "compression=%p)\n", mime, pathname, filename, compression));
ef416fc2 551
552 /*
553 * Range check input parameters...
554 */
555
fa73b229 556 if (!mime || !pathname)
ef416fc2 557 return (NULL);
558
559 /*
560 * Try to open the file...
561 */
562
4400e98d 563 if ((fb.fp = cupsFileOpen(pathname, "r")) == NULL)
ef416fc2 564 return (NULL);
565
4400e98d 566 fb.offset = -1;
567 fb.length = 0;
568
ef416fc2 569 /*
4400e98d 570 * Figure out the base filename (without directory portion)...
ef416fc2 571 */
572
4400e98d 573 if (filename)
574 {
575 if ((base = strrchr(filename, '/')) != NULL)
576 base ++;
577 else
91c84a35 578 base = filename;
4400e98d 579 }
580 else if ((base = strrchr(pathname, '/')) != NULL)
581 base ++;
ef416fc2 582 else
91c84a35 583 base = pathname;
ef416fc2 584
585 /*
586 * Then check it against all known types...
587 */
588
dd1abb6b 589 for (type = (mime_type_t *)cupsArrayFirst(mime->types), best = NULL;
fa73b229 590 type;
591 type = (mime_type_t *)cupsArrayNext(mime->types))
4400e98d 592 if (checkrules(base, &fb, type->rules))
dd1abb6b
MS
593 {
594 if (!best || type->priority > best->priority)
595 best = type;
596 }
ef416fc2 597
598 /*
599 * Finally, close the file and return a match (if any)...
600 */
601
602 if (compression)
4400e98d 603 *compression = cupsFileCompression(fb.fp);
ef416fc2 604
4400e98d 605 cupsFileClose(fb.fp);
ef416fc2 606
dd1abb6b 607 return (best);
ef416fc2 608}
609
610
611/*
612 * 'mimeType()' - Lookup a file type.
613 */
614
fa73b229 615mime_type_t * /* O - Matching file type definition */
616mimeType(mime_t *mime, /* I - MIME database */
617 const char *super, /* I - Super-type name */
618 const char *type) /* I - Type name */
ef416fc2 619{
fa73b229 620 mime_type_t key; /* MIME type search key*/
621
ef416fc2 622
623 /*
624 * Range check input...
625 */
626
fa73b229 627 if (!mime || !super || !type)
ef416fc2 628 return (NULL);
629
630 /*
631 * Lookup the type in the array...
632 */
633
634 strlcpy(key.super, super, sizeof(key.super));
fa73b229 635 strlcpy(key.type, type, sizeof(key.type));
ef416fc2 636
fa73b229 637 return ((mime_type_t *)cupsArrayFind(mime->types, &key));
ef416fc2 638}
639
640
641/*
fa73b229 642 * 'compare_types()' - Compare two MIME super/type names.
ef416fc2 643 */
644
fa73b229 645static int /* O - Result of comparison */
646compare_types(mime_type_t *t0, /* I - First type */
647 mime_type_t *t1) /* I - Second type */
ef416fc2 648{
fa73b229 649 int i; /* Result of comparison */
ef416fc2 650
651
080811b1
MS
652 if ((i = strcasecmp(t0->super, t1->super)) == 0)
653 i = strcasecmp(t0->type, t1->type);
ef416fc2 654
655 return (i);
656}
657
658
659/*
660 * 'checkrules()' - Check each rule in a list.
661 */
662
663static int /* O - 1 if match, 0 if no match */
4400e98d 664checkrules(const char *filename, /* I - Filename */
665 _mime_filebuf_t *fb, /* I - File to check */
666 mime_magic_t *rules) /* I - Rules to check */
ef416fc2 667{
668 int n; /* Looping var */
669 int region; /* Region to look at */
670 int logic, /* Logic to apply */
671 result, /* Result of test */
672 intv; /* Integer value */
673 short shortv; /* Short value */
4400e98d 674 unsigned char *bufptr; /* Pointer into buffer */
ef416fc2 675#ifdef DEBUG
676 const char * const debug_tests[] = /* Test names... */
677 {
678 "NOP", /* No operation */
679 "AND", /* Logical AND of all children */
680 "OR", /* Logical OR of all children */
681 "MATCH", /* Filename match */
682 "ASCII", /* ASCII characters in range */
4400e98d 683 "PRINTABLE", /* Printable characters (32-255) */
ef416fc2 684 "STRING", /* String matches */
685 "CHAR", /* Character/byte matches */
686 "SHORT", /* Short/16-bit word matches */
687 "INT", /* Integer/32-bit word matches */
688 "LOCALE" /* Current locale matches string */
689 "CONTAINS" /* File contains a string */
690 "ISTRING" /* Case-insensitive string matches */
691 };
692#endif /* DEBUG */
693
694
b423cd4c 695 DEBUG_printf(("checkrules(filename=\"%s\", fb=%p, rules=%p)\n", filename,
696 fb, rules));
ef416fc2 697
698 if (rules == NULL)
699 return (0);
700
701 if (rules->parent == NULL)
702 logic = MIME_MAGIC_OR;
703 else
704 logic = rules->parent->op;
705
4400e98d 706 result = 0;
ef416fc2 707
708 while (rules != NULL)
709 {
710 /*
711 * Compute the result of this rule...
712 */
713
714 switch (rules->op)
715 {
716 case MIME_MAGIC_MATCH :
717 result = patmatch(filename, rules->value.matchv);
718 break;
719
720 case MIME_MAGIC_ASCII :
721 /*
722 * Load the buffer if necessary...
723 */
724
4400e98d 725 if (fb->offset < 0 || rules->offset < fb->offset ||
726 (rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 727 {
728 /*
729 * Reload file buffer...
730 */
731
4400e98d 732 cupsFileSeek(fb->fp, rules->offset);
733 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
734 sizeof(fb->buffer));
735 fb->offset = rules->offset;
ef416fc2 736 }
737
738 /*
739 * Test for ASCII printable characters plus standard control chars.
740 */
741
4400e98d 742 if ((rules->offset + rules->length) > (fb->offset + fb->length))
743 n = fb->offset + fb->length - rules->offset;
ef416fc2 744 else
745 n = rules->length;
746
4400e98d 747 bufptr = fb->buffer + rules->offset - fb->offset;
ef416fc2 748 while (n > 0)
749 if ((*bufptr >= 32 && *bufptr <= 126) ||
750 (*bufptr >= 8 && *bufptr <= 13) ||
751 *bufptr == 26 || *bufptr == 27)
752 {
753 n --;
754 bufptr ++;
755 }
756 else
757 break;
758
759 result = (n == 0);
760 break;
761
762 case MIME_MAGIC_PRINTABLE :
763 /*
764 * Load the buffer if necessary...
765 */
766
4400e98d 767 if (fb->offset < 0 || rules->offset < fb->offset ||
768 (rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 769 {
770 /*
771 * Reload file buffer...
772 */
773
4400e98d 774 cupsFileSeek(fb->fp, rules->offset);
775 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
776 sizeof(fb->buffer));
777 fb->offset = rules->offset;
ef416fc2 778 }
779
780 /*
781 * Test for 8-bit printable characters plus standard control chars.
782 */
783
4400e98d 784 if ((rules->offset + rules->length) > (fb->offset + fb->length))
785 n = fb->offset + fb->length - rules->offset;
ef416fc2 786 else
787 n = rules->length;
788
4400e98d 789 bufptr = fb->buffer + rules->offset - fb->offset;
ef416fc2 790
791 while (n > 0)
792 if (*bufptr >= 128 ||
793 (*bufptr >= 32 && *bufptr <= 126) ||
794 (*bufptr >= 8 && *bufptr <= 13) ||
795 *bufptr == 26 || *bufptr == 27)
796 {
797 n --;
798 bufptr ++;
799 }
800 else
801 break;
802
803 result = (n == 0);
804 break;
805
806 case MIME_MAGIC_STRING :
75bd9771 807 DEBUG_printf(("checkrules: string(%d, \"%s\")\n", rules->offset,
ef416fc2 808 rules->value.stringv));
809
810 /*
811 * Load the buffer if necessary...
812 */
813
4400e98d 814 if (fb->offset < 0 || rules->offset < fb->offset ||
815 (rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 816 {
817 /*
818 * Reload file buffer...
819 */
820
4400e98d 821 cupsFileSeek(fb->fp, rules->offset);
822 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
823 sizeof(fb->buffer));
824 fb->offset = rules->offset;
ef416fc2 825
75bd9771 826 DEBUG_printf(("checkrules: loaded %d byte fb->buffer at %d, starts "
4400e98d 827 "with \"%c%c%c%c\"...\n",
828 fb->length, fb->offset, fb->buffer[0], fb->buffer[1],
829 fb->buffer[2], fb->buffer[3]));
ef416fc2 830 }
831
832 /*
833 * Compare the buffer against the string. If the file is too
834 * short then don't compare - it can't match...
835 */
836
4400e98d 837 if ((rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 838 result = 0;
839 else
4400e98d 840 result = (memcmp(fb->buffer + rules->offset - fb->offset,
ef416fc2 841 rules->value.stringv, rules->length) == 0);
75bd9771 842 DEBUG_printf(("checkrules: result=%d\n", result));
ef416fc2 843 break;
844
845 case MIME_MAGIC_ISTRING :
846 /*
847 * Load the buffer if necessary...
848 */
849
4400e98d 850 if (fb->offset < 0 || rules->offset < fb->offset ||
851 (rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 852 {
853 /*
854 * Reload file buffer...
855 */
856
4400e98d 857 cupsFileSeek(fb->fp, rules->offset);
858 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
859 sizeof(fb->buffer));
860 fb->offset = rules->offset;
ef416fc2 861 }
862
863 /*
864 * Compare the buffer against the string. If the file is too
865 * short then don't compare - it can't match...
866 */
867
4400e98d 868 if ((rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 869 result = 0;
870 else
4400e98d 871 result = (strncasecmp((char *)fb->buffer + rules->offset -
872 fb->offset,
ef416fc2 873 rules->value.stringv, rules->length) == 0);
874 break;
875
876 case MIME_MAGIC_CHAR :
877 /*
878 * Load the buffer if necessary...
879 */
880
4400e98d 881 if (fb->offset < 0 || rules->offset < fb->offset)
ef416fc2 882 {
883 /*
884 * Reload file buffer...
885 */
886
4400e98d 887 cupsFileSeek(fb->fp, rules->offset);
888 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
889 sizeof(fb->buffer));
890 fb->offset = rules->offset;
ef416fc2 891 }
892
893 /*
894 * Compare the character values; if the file is too short, it
895 * can't match...
896 */
897
4400e98d 898 if (fb->length < 1)
ef416fc2 899 result = 0;
900 else
4400e98d 901 result = (fb->buffer[rules->offset - fb->offset] ==
902 rules->value.charv);
ef416fc2 903 break;
904
905 case MIME_MAGIC_SHORT :
906 /*
907 * Load the buffer if necessary...
908 */
909
4400e98d 910 if (fb->offset < 0 || rules->offset < fb->offset ||
911 (rules->offset + 2) > (fb->offset + fb->length))
ef416fc2 912 {
913 /*
914 * Reload file buffer...
915 */
916
4400e98d 917 cupsFileSeek(fb->fp, rules->offset);
918 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
919 sizeof(fb->buffer));
920 fb->offset = rules->offset;
ef416fc2 921 }
922
923 /*
924 * Compare the short values; if the file is too short, it
925 * can't match...
926 */
927
4400e98d 928 if (fb->length < 2)
ef416fc2 929 result = 0;
930 else
931 {
4400e98d 932 bufptr = fb->buffer + rules->offset - fb->offset;
ef416fc2 933 shortv = (bufptr[0] << 8) | bufptr[1];
934 result = (shortv == rules->value.shortv);
935 }
936 break;
937
938 case MIME_MAGIC_INT :
939 /*
940 * Load the buffer if necessary...
941 */
942
4400e98d 943 if (fb->offset < 0 || rules->offset < fb->offset ||
944 (rules->offset + 4) > (fb->offset + fb->length))
ef416fc2 945 {
946 /*
947 * Reload file buffer...
948 */
949
4400e98d 950 cupsFileSeek(fb->fp, rules->offset);
951 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
952 sizeof(fb->buffer));
953 fb->offset = rules->offset;
ef416fc2 954 }
955
956 /*
957 * Compare the int values; if the file is too short, it
958 * can't match...
959 */
960
4400e98d 961 if (fb->length < 4)
ef416fc2 962 result = 0;
963 else
964 {
4400e98d 965 bufptr = fb->buffer + rules->offset - fb->offset;
966 intv = (((((bufptr[0] << 8) | bufptr[1]) << 8) |
967 bufptr[2]) << 8) | bufptr[3];
ef416fc2 968 result = (intv == rules->value.intv);
969 }
970 break;
971
972 case MIME_MAGIC_LOCALE :
973#if defined(WIN32) || defined(__EMX__) || defined(__APPLE__)
4400e98d 974 result = (strcmp(rules->value.localev,
975 setlocale(LC_ALL, "")) == 0);
ef416fc2 976#else
4400e98d 977 result = (strcmp(rules->value.localev,
978 setlocale(LC_MESSAGES, "")) == 0);
ef416fc2 979#endif /* __APPLE__ */
980 break;
981
982 case MIME_MAGIC_CONTAINS :
983 /*
984 * Load the buffer if necessary...
985 */
986
4400e98d 987 if (fb->offset < 0 || rules->offset < fb->offset ||
988 (rules->offset + rules->region) > (fb->offset + fb->length))
ef416fc2 989 {
990 /*
991 * Reload file buffer...
992 */
993
4400e98d 994 cupsFileSeek(fb->fp, rules->offset);
995 fb->length = cupsFileRead(fb->fp, (char *)fb->buffer,
996 sizeof(fb->buffer));
997 fb->offset = rules->offset;
ef416fc2 998 }
999
1000 /*
1001 * Compare the buffer against the string. If the file is too
1002 * short then don't compare - it can't match...
1003 */
1004
4400e98d 1005 if ((rules->offset + rules->length) > (fb->offset + fb->length))
ef416fc2 1006 result = 0;
1007 else
1008 {
4400e98d 1009 if (fb->length > rules->region)
ef416fc2 1010 region = rules->region - rules->length;
1011 else
4400e98d 1012 region = fb->length - rules->length;
ef416fc2 1013
1014 for (n = 0; n < region; n ++)
4400e98d 1015 if ((result = (memcmp(fb->buffer + rules->offset - fb->offset + n,
1016 rules->value.stringv,
1017 rules->length) == 0)) != 0)
ef416fc2 1018 break;
1019 }
1020 break;
1021
1022 default :
1023 if (rules->child != NULL)
4400e98d 1024 result = checkrules(filename, fb, rules->child);
ef416fc2 1025 else
1026 result = 0;
1027 break;
1028 }
1029
1030 /*
1031 * If the logic is inverted, invert the result...
1032 */
1033
1034 if (rules->invert)
1035 result = !result;
1036
1037 /*
1038 * OK, now if the current logic is OR and this result is true, the this
1039 * rule set is true. If the current logic is AND and this result is false,
1040 * the the rule set is false...
1041 */
1042
75bd9771
MS
1043 DEBUG_printf(("checkrules: result of test %p (MIME_MAGIC_%s) is %d\n",
1044 rules, debug_tests[rules->op], result));
ef416fc2 1045
1046 if ((result && logic == MIME_MAGIC_OR) ||
1047 (!result && logic == MIME_MAGIC_AND))
1048 return (result);
1049
1050 /*
1051 * Otherwise the jury is still out on this one, so move to the next rule.
1052 */
1053
1054 rules = rules->next;
1055 }
1056
1057 return (result);
1058}
1059
1060
1061/*
1062 * 'patmatch()' - Pattern matching...
1063 */
1064
1065static int /* O - 1 if match, 0 if no match */
1066patmatch(const char *s, /* I - String to match against */
1067 const char *pat) /* I - Pattern to match against */
1068{
1069 /*
1070 * Range check the input...
1071 */
1072
1073 if (s == NULL || pat == NULL)
1074 return (0);
1075
1076 /*
1077 * Loop through the pattern and match strings, and stop if we come to a
1078 * point where the strings don't match or we find a complete match.
1079 */
1080
1081 while (*s != '\0' && *pat != '\0')
1082 {
1083 if (*pat == '*')
1084 {
1085 /*
1086 * Wildcard - 0 or more characters...
1087 */
1088
1089 pat ++;
1090 if (*pat == '\0')
4400e98d 1091 return (1); /* Last pattern char is *, so everything matches... */
ef416fc2 1092
1093 /*
1094 * Test all remaining combinations until we get to the end of the string.
1095 */
1096
1097 while (*s != '\0')
1098 {
1099 if (patmatch(s, pat))
1100 return (1);
1101
1102 s ++;
1103 }
1104 }
1105 else if (*pat == '?')
1106 {
1107 /*
1108 * Wildcard - 1 character...
1109 */
1110
1111 pat ++;
1112 s ++;
1113 continue;
1114 }
1115 else if (*pat == '[')
1116 {
1117 /*
1118 * Match a character from the input set [chars]...
1119 */
1120
1121 pat ++;
1122 while (*pat != ']' && *pat != '\0')
1123 if (*s == *pat)
1124 break;
1125 else
1126 pat ++;
1127
1128 if (*pat == ']' || *pat == '\0')
1129 return (0);
1130
1131 while (*pat != ']' && *pat != '\0')
1132 pat ++;
1133
1134 if (*pat == ']')
1135 pat ++;
1136
1137 continue;
1138 }
1139 else if (*pat == '\\')
1140 {
1141 /*
1142 * Handle quoted characters...
1143 */
1144
1145 pat ++;
1146 }
1147
1148 /*
1149 * Stop if the pattern and string don't match...
1150 */
1151
1152 if (*pat++ != *s++)
1153 return (0);
1154 }
1155
1156 /*
4400e98d 1157 * Done parsing the pattern and string; return 1 if the last character
1158 * matches and 0 otherwise...
ef416fc2 1159 */
1160
1161 return (*s == *pat);
1162}
1163
1164
1165/*
b19ccc9e 1166 * End of "$Id: type.c 7720 2008-07-11 22:46:21Z mike $".
ef416fc2 1167 */