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