• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kio/kio
 

kio/kio

  • kio
  • kio
kmimemagic.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 Fritz Elfert <fritz@kde.org>
3  Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License version 2 as published by the Free Software Foundation.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 #include "kmimemagic.h"
20 #include <kdebug.h>
21 #include <kapplication.h>
22 #include <tqfile.h>
23 #include <ksimpleconfig.h>
24 #include <kstandarddirs.h>
25 #include <kstaticdeleter.h>
26 #include <klargefile.h>
27 #include <assert.h>
28 
29 static int fsmagic(struct config_rec* conf, const char *fn, KDE_struct_stat *sb);
30 static void process(struct config_rec* conf, const TQString &);
31 static int ascmagic(struct config_rec* conf, unsigned char *buf, int nbytes);
32 static int tagmagic(unsigned char *buf, int nbytes);
33 static int textmagic(struct config_rec* conf, unsigned char *, int);
34 
35 static void tryit(struct config_rec* conf, unsigned char *buf, int nb);
36 static int match(struct config_rec* conf, unsigned char *, int);
37 
38 KMimeMagic* KMimeMagic::s_pSelf;
39 static KStaticDeleter<KMimeMagic> kmimemagicsd;
40 
41 KMimeMagic* KMimeMagic::self()
42 {
43  if( !s_pSelf )
44  initStatic();
45  return s_pSelf;
46 }
47 
48 void KMimeMagic::initStatic()
49 {
50  s_pSelf = kmimemagicsd.setObject( s_pSelf, new KMimeMagic() );
51  s_pSelf->setFollowLinks( true );
52 }
53 
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <sys/wait.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 #include <errno.h>
62 #include <ctype.h>
63 #include <time.h>
64 #include <utime.h>
65 #include <stdarg.h>
66 #include <tqregexp.h>
67 #include <tqstring.h>
68 
69 //#define MIME_MAGIC_DEBUG_TABLE // untested
70 
71 // Uncomment to debug the config-file parsing phase
72 //#define DEBUG_APPRENTICE
73 // Uncomment to debug the matching phase
74 //#define DEBUG_MIMEMAGIC
75 
76 #if (defined DEBUG_MIMEMAGIC || defined DEBUG_APPRENTICE)
77 #define DEBUG_LINENUMBERS
78 #endif
79 
80 /*
81  * Buitltin Mime types
82  */
83 #define MIME_BINARY_UNKNOWN "application/octet-stream"
84 #define MIME_BINARY_UNREADABLE "application/x-unreadable"
85 #define MIME_BINARY_ZEROSIZE "application/x-zerosize"
86 #define MIME_TEXT_UNKNOWN "text/plain"
87 #define MIME_TEXT_PLAIN "text/plain"
88 #define MIME_INODE_DIR "inode/directory"
89 #define MIME_INODE_CDEV "inode/chardevice"
90 #define MIME_INODE_BDEV "inode/blockdevice"
91 #define MIME_INODE_FIFO "inode/fifo"
92 #define MIME_INODE_LINK "inode/link"
93 #define MIME_INODE_SOCK "inode/socket"
94 // Following should go in magic-file - Fritz
95 #define MIME_APPL_TROFF "application/x-troff"
96 #define MIME_APPL_TAR "application/x-tar"
97 #define MIME_TEXT_FORTRAN "text/x-fortran"
98 
99 #define MAXMIMESTRING 256
100 
101 #define HOWMANY 4000 /* big enough to recognize most WWW files, and skip GPL-headers */
102 #define MAXDESC 50 /* max leng of text description */
103 #define MAXstring 64 /* max leng of "string" types */
104 
105 typedef union VALUETYPE {
106  unsigned char b;
107  unsigned short h;
108  unsigned long l;
109  char s[MAXstring];
110  unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */
111  unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */
112 } VALUETYPE;
113 
114 struct magic {
115  struct magic *next; /* link to next entry */
116 #ifdef DEBUG_LINENUMBERS
117  int lineno; /* line number from magic file - doesn't say from which one ;) */
118 #endif
119 
120  short flag;
121 #define INDIR 1 /* if '>(...)' appears, */
122 #define UNSIGNED 2 /* comparison is unsigned */
123  short cont_level; /* level of ">" */
124  struct {
125  char type; /* byte short long */
126  long offset; /* offset from indirection */
127  } in;
128  long offset; /* offset to magic number */
129  unsigned char reln; /* relation (0=eq, '>'=gt, etc) */
130  char type; /* int, short, long or string. */
131  char vallen; /* length of string value, if any */
132 #define BYTE 1
133 #define SHORT 2
134 #define LONG 4
135 #define STRING 5
136 #define DATE 6
137 #define BESHORT 7
138 #define BELONG 8
139 #define BEDATE 9
140 #define LESHORT 10
141 #define LELONG 11
142 #define LEDATE 12
143  VALUETYPE value; /* either number or string */
144  unsigned long mask; /* mask before comparison with value */
145  char nospflag; /* suppress space character */
146 
147  /* NOTE: this string is suspected of overrunning - find it! */
148  char desc[MAXDESC]; /* description */
149 };
150 
151 /*
152  * data structures for tar file recognition
153  * --------------------------------------------------------------------------
154  * Header file for public domain tar (tape archive) program.
155  *
156  * @(#)tar.h 1.20 86/10/29 Public Domain. Created 25 August 1985 by John
157  * Gilmore, ihnp4!hoptoad!gnu.
158  *
159  * Header block on tape.
160  *
161  * I'm going to use traditional DP naming conventions here. A "block" is a big
162  * chunk of stuff that we do I/O on. A "record" is a piece of info that we
163  * care about. Typically many "record"s fit into a "block".
164  */
165 #define RECORDSIZE 512
166 #define NAMSIZ 100
167 #define TUNMLEN 32
168 #define TGNMLEN 32
169 
170 union record {
171  char charptr[RECORDSIZE];
172  struct header {
173  char name[NAMSIZ];
174  char mode[8];
175  char uid[8];
176  char gid[8];
177  char size[12];
178  char mtime[12];
179  char chksum[8];
180  char linkflag;
181  char linkname[NAMSIZ];
182  char magic[8];
183  char uname[TUNMLEN];
184  char gname[TGNMLEN];
185  char devmajor[8];
186  char devminor[8];
187  } header;
188 };
189 
190 /* The magic field is filled with this if uname and gname are valid. */
191 #define TMAGIC "ustar " /* 7 chars and a null */
192 
193 /*
194  * file-function prototypes
195  */
196 static int is_tar(unsigned char *, int);
197 static unsigned long signextend(struct magic *, unsigned long);
198 static int getvalue(struct magic *, char **);
199 static int hextoint(int);
200 static char *getstr(char *, char *, int, int *);
201 static int mget(union VALUETYPE *, unsigned char *, struct magic *, int);
202 static int mcheck(union VALUETYPE *, struct magic *);
203 static int mconvert(union VALUETYPE *, struct magic *);
204 static long from_oct(int, char *);
205 
206 /*
207  * includes for ASCII substring recognition formerly "names.h" in file
208  * command
209  *
210  * Original notes: names and types used by ascmagic in file(1).
211  * These tokens are
212  * here because they can appear anywhere in the first HOWMANY bytes, while
213  * tokens in /etc/magic must appear at fixed offsets into the file. Don't
214  * make HOWMANY too high unless you have a very fast CPU.
215  */
216 
217 /* these types are used calculate index to 'types': keep em in sync! */
218 /* HTML inserted in first because this is a web server module now */
219 /* ENG removed because stupid */
220 #define L_HTML 0x001 /* HTML */
221 #define L_C 0x002 /* first and foremost on UNIX */
222 #define L_MAKE 0x004 /* Makefiles */
223 #define L_PLI 0x008 /* PL/1 */
224 #define L_MACH 0x010 /* some kinda assembler */
225 #define L_PAS 0x020 /* Pascal */
226 #define L_JAVA 0x040 /* Java source */
227 #define L_CPP 0x080 /* C++ */
228 #define L_MAIL 0x100 /* Electronic mail */
229 #define L_NEWS 0x200 /* Usenet Netnews */
230 #define L_DIFF 0x400 /* Output of diff */
231 #define L_OBJC 0x800 /* Objective C */
232 
233 // Note: this is not a type, it's just used to mark items that should count more
234 #define FLAG_STRONG 0x1000
235 
236 #define P_HTML 0 /* HTML */
237 #define P_C 1 /* first and foremost on UNIX */
238 #define P_MAKE 2 /* Makefiles */
239 #define P_PLI 3 /* PL/1 */
240 #define P_MACH 4 /* some kinda assembler */
241 #define P_PAS 5 /* Pascal */
242 #define P_JAVA 6 /* Java source */
243 #define P_CPP 7 /* C++ */
244 #define P_MAIL 8 /* Electronic mail */
245 #define P_NEWS 9 /* Usenet Netnews */
246 #define P_DIFF 10 /* Output of diff */
247 #define P_OBJC 11 /* Objective C */
248 
249 typedef struct asc_type {
250  const char *type;
251  int kwords;
252  double weight;
253 } asc_type;
254 
255 static const asc_type types[] = {
256  { "text/html", 19, 2 }, // 10 items but 10 different words only
257  { "text/x-c", 13, 1 },
258  { "text/x-makefile", 4, 1.9 },
259  { "text/x-pli", 1, 3 },
260  { "text/x-assembler", 6, 2.1 },
261  { "text/x-pascal", 1, 1 },
262  { "text/x-java", 12, 1 },
263  { "text/x-c++", 19, 1 },
264  { "message/rfc822", 4, 1.9 },
265  { "message/news", 3, 2 },
266  { "text/x-diff", 4, 2 },
267  { "text/x-objc", 10, 1 }
268 };
269 
270 #define NTYPES (sizeof(types)/sizeof(asc_type))
271 
272 static struct names {
273  const char *name;
274  short type;
275 } const names[] = {
276  {
277  "<html", L_HTML | FLAG_STRONG
278  },
279  {
280  "<HTML", L_HTML | FLAG_STRONG
281  },
282  {
283  "<head", L_HTML
284  },
285  {
286  "<HEAD", L_HTML
287  },
288  {
289  "<body", L_HTML
290  },
291  {
292  "<BODY", L_HTML
293  },
294  {
295  "<title", L_HTML
296  },
297  {
298  "<TITLE", L_HTML
299  },
300  {
301  "<h1", L_HTML
302  },
303  {
304  "<H1", L_HTML
305  },
306  {
307  "<a", L_HTML
308  },
309  {
310  "<A", L_HTML
311  },
312  {
313  "<img", L_HTML
314  },
315  {
316  "<IMG", L_HTML
317  },
318  {
319  "<!--", L_HTML
320  },
321  {
322  "<!doctype", L_HTML
323  },
324  {
325  "<!DOCTYPE", L_HTML
326  },
327  {
328  "<div", L_HTML
329  },
330  {
331  "<DIV", L_HTML
332  },
333  {
334  "<frame", L_HTML
335  },
336  {
337  "<FRAME", L_HTML
338  },
339  {
340  "<frameset", L_HTML
341  },
342  {
343  "<FRAMESET", L_HTML
344  },
345  {
346  "<script", L_HTML | FLAG_STRONG
347  },
348  {
349  "<SCRIPT", L_HTML | FLAG_STRONG
350  },
351  {
352  "/*", L_C|L_CPP|L_JAVA|L_OBJC
353  },
354  {
355  "//", L_C|L_CPP|L_JAVA|L_OBJC
356  },
357  {
358  "#include", L_C|L_CPP
359  },
360  {
361  "#ifdef", L_C|L_CPP
362  },
363  {
364  "#ifndef", L_C|L_CPP
365  },
366  {
367  "bool", L_C|L_CPP
368  },
369  {
370  "char", L_C|L_CPP|L_JAVA|L_OBJC
371  },
372  {
373  "int", L_C|L_CPP|L_JAVA|L_OBJC
374  },
375  {
376  "float", L_C|L_CPP|L_JAVA|L_OBJC
377  },
378  {
379  "void", L_C|L_CPP|L_JAVA|L_OBJC
380  },
381  {
382  "extern", L_C|L_CPP
383  },
384  {
385  "struct", L_C|L_CPP
386  },
387  {
388  "union", L_C|L_CPP
389  },
390  {
391  "implements", L_JAVA
392  },
393  {
394  "super", L_JAVA
395  },
396  {
397  "import", L_JAVA
398  },
399  {
400  "class", L_CPP|L_JAVA
401  },
402  {
403  "public", L_CPP|L_JAVA
404  },
405  {
406  "private", L_CPP|L_JAVA
407  },
408  {
409  "explicit", L_CPP
410  },
411  {
412  "virtual", L_CPP
413  },
414  {
415  "namespace", L_CPP
416  },
417  {
418  "#import", L_OBJC
419  },
420  {
421  "@interface", L_OBJC
422  },
423  {
424  "@implementation", L_OBJC
425  },
426  {
427  "@protocol", L_OBJC
428  },
429  {
430  "CFLAGS", L_MAKE
431  },
432  {
433  "LDFLAGS", L_MAKE
434  },
435  {
436  "all:", L_MAKE
437  },
438  {
439  ".PHONY:", L_MAKE
440  },
441  {
442  "srcdir", L_MAKE
443  },
444  {
445  "exec_prefix", L_MAKE
446  },
447  /*
448  * Too many files of text have these words in them. Find another way
449  * to recognize Fortrash.
450  */
451  {
452  ".ascii", L_MACH
453  },
454  {
455  ".asciiz", L_MACH
456  },
457  {
458  ".byte", L_MACH
459  },
460  {
461  ".even", L_MACH
462  },
463  {
464  ".globl", L_MACH
465  },
466  {
467  "clr", L_MACH
468  },
469  {
470  "(input", L_PAS
471  },
472  {
473  "dcl", L_PLI
474  },
475  {
476  "Received:", L_MAIL
477  },
478  /* we now stop at '>' for tokens, so this one won't work {
479  ">From", L_MAIL
480  },*/
481  {
482  "Return-Path:", L_MAIL
483  },
484  {
485  "Cc:", L_MAIL
486  },
487  {
488  "Newsgroups:", L_NEWS
489  },
490  {
491  "Path:", L_NEWS
492  },
493  {
494  "Organization:", L_NEWS
495  },
496  {
497  "---", L_DIFF
498  },
499  {
500  "+++", L_DIFF
501  },
502  {
503  "***", L_DIFF
504  },
505  {
506  "@@", L_DIFF
507  },
508  {
509  NULL, 0
510  }
511 };
512 
523 class KMimeMagicUtimeConf
524 {
525 public:
526  KMimeMagicUtimeConf()
527  {
528  tmpDirs << TQString::fromLatin1("/tmp"); // default value
529 
530  // The trick is that we also don't want the user to override globally set
531  // directories. So we have to misuse KStandardDirs :}
532  TQStringList confDirs = KGlobal::dirs()->resourceDirs( "config" );
533  if ( !confDirs.isEmpty() )
534  {
535  TQString globalConf = confDirs.last() + "kmimemagicrc";
536  if ( TQFile::exists( globalConf ) )
537  {
538  KSimpleConfig cfg( globalConf );
539  cfg.setGroup( "Settings" );
540  tmpDirs = cfg.readListEntry( "atimeDirs" );
541  }
542  if ( confDirs.count() > 1 )
543  {
544  TQString localConf = confDirs.first() + "kmimemagicrc";
545  if ( TQFile::exists( localConf ) )
546  {
547  KSimpleConfig cfg( localConf );
548  cfg.setGroup( "Settings" );
549  tmpDirs += cfg.readListEntry( "atimeDirs" );
550  }
551  }
552  for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
553  {
554  TQString dir = *it;
555  if ( !dir.isEmpty() && dir[ dir.length()-1 ] != '/' )
556  (*it) += '/';
557  }
558  }
559 #if 0
560  // debug code
561  for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
562  kdDebug(7018) << " atimeDir: " << *it << endl;
563 #endif
564  }
565 
566  bool restoreAccessTime( const TQString & file ) const
567  {
568  TQString dir = file.left( file.findRev( '/' ) );
569  bool res = tmpDirs.contains( dir );
570  //kdDebug(7018) << "restoreAccessTime " << file << " dir=" << dir << " result=" << res << endl;
571  return res;
572  }
573  TQStringList tmpDirs;
574 };
575 
576 /* current config */
577 struct config_rec {
578  bool followLinks;
579  TQString resultBuf;
580  int accuracy;
581 
582  struct magic *magic, /* head of magic config list */
583  *last;
584  KMimeMagicUtimeConf * utimeConf;
585 };
586 
587 #ifdef MIME_MAGIC_DEBUG_TABLE
588 static void
589 test_table()
590 {
591  struct magic *m;
592  struct magic *prevm = NULL;
593 
594  kdDebug(7018) << "test_table : started" << endl;
595  for (m = conf->magic; m; m = m->next) {
596  if (isprint((((unsigned long) m) >> 24) & 255) &&
597  isprint((((unsigned long) m) >> 16) & 255) &&
598  isprint((((unsigned long) m) >> 8) & 255) &&
599  isprint(((unsigned long) m) & 255)) {
600  //debug("test_table: POINTER CLOBBERED! "
601  //"m=\"%c%c%c%c\" line=%d",
602  (((unsigned long) m) >> 24) & 255,
603  (((unsigned long) m) >> 16) & 255,
604  (((unsigned long) m) >> 8) & 255,
605  ((unsigned long) m) & 255,
606  prevm ? prevm->lineno : -1);
607  break;
608  }
609  prevm = m;
610  }
611 }
612 #endif
613 
614 #define EATAB {while (isascii((unsigned char) *l) && \
615  isspace((unsigned char) *l)) ++l;}
616 
617 int KMimeMagic::parse_line(char *line, int *rule, int lineno)
618 {
619  int ws_offset;
620 
621  /* delete newline */
622  if (line[0]) {
623  line[strlen(line) - 1] = '\0';
624  }
625  /* skip leading whitespace */
626  ws_offset = 0;
627  while (line[ws_offset] && isspace(line[ws_offset])) {
628  ws_offset++;
629  }
630 
631  /* skip blank lines */
632  if (line[ws_offset] == 0) {
633  return 0;
634  }
635  /* comment, do not parse */
636  if (line[ws_offset] == '#')
637  return 0;
638 
639  /* if we get here, we're going to use it so count it */
640  (*rule)++;
641 
642  /* parse it */
643  return (parse(line + ws_offset, lineno) != 0);
644 }
645 
646 /*
647  * apprentice - load configuration from the magic file.
648  */
649 int KMimeMagic::apprentice( const TQString& magicfile )
650 {
651  FILE *f;
652  char line[BUFSIZ + 1];
653  int errs = 0;
654  int lineno;
655  int rule = 0;
656  TQCString fname;
657 
658  if (magicfile.isEmpty())
659  return -1;
660  fname = TQFile::encodeName(magicfile);
661  f = fopen(fname, "r");
662  if (f == NULL) {
663  kdError(7018) << "can't read magic file " << fname.data() << ": " << strerror(errno) << endl;
664  return -1;
665  }
666 
667  /* parse it */
668  for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; lineno++)
669  if (parse_line(line, &rule, lineno))
670  errs++;
671 
672  fclose(f);
673 
674 #ifdef DEBUG_APPRENTICE
675  kdDebug(7018) << "apprentice: conf=" << conf << " file=" << magicfile << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
676  kdDebug(7018) << "apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
677 #endif
678 
679 #ifdef MIME_MAGIC_DEBUG_TABLE
680  test_table();
681 #endif
682 
683  return (errs ? -1 : 0);
684 }
685 
686 int KMimeMagic::buff_apprentice(char *buff)
687 {
688  char line[BUFSIZ + 2];
689  int errs = 0;
690  int lineno = 1;
691  char *start = buff;
692  char *end;
693  int count = 0;
694  int rule = 0;
695  int len = strlen(buff) + 1;
696 
697  /* parse it */
698  do {
699  count = (len > BUFSIZ-1)?BUFSIZ-1:len;
700  strncpy(line, start, count);
701  line[count] = '\0';
702  if ((end = strchr(line, '\n'))) {
703  *(++end) = '\0';
704  count = strlen(line);
705  } else
706  strcat(line, "\n");
707  start += count;
708  len -= count;
709  if (parse_line(line, &rule, lineno))
710  errs++;
711  lineno++;
712  } while (len > 0);
713 
714 #ifdef DEBUG_APPRENTICE
715  kdDebug(7018) << "buff_apprentice: conf=" << conf << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
716  kdDebug(7018) << "buff_apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
717 #endif
718 
719 #ifdef MIME_MAGIC_DEBUG_TABLE
720  test_table();
721 #endif
722 
723  return (errs ? -1 : 0);
724 }
725 
726 /*
727  * extend the sign bit if the comparison is to be signed
728  */
729 static unsigned long
730 signextend(struct magic *m, unsigned long v)
731 {
732  if (!(m->flag & UNSIGNED))
733  switch (m->type) {
734  /*
735  * Do not remove the casts below. They are vital.
736  * When later compared with the data, the sign
737  * extension must have happened.
738  */
739  case BYTE:
740  v = (char) v;
741  break;
742  case SHORT:
743  case BESHORT:
744  case LESHORT:
745  v = (short) v;
746  break;
747  case DATE:
748  case BEDATE:
749  case LEDATE:
750  case LONG:
751  case BELONG:
752  case LELONG:
753  v = (long) v;
754  break;
755  case STRING:
756  break;
757  default:
758  kdError(7018) << "" << "signextend" << ": can't happen: m->type=" << m->type << endl;
759  return 998; //good value
760  }
761  return v;
762 }
763 
764 /*
765  * parse one line from magic file, put into magic[index++] if valid
766  */
767 int KMimeMagic::parse(char *l, int
768 #ifdef DEBUG_LINENUMBERS
769  lineno
770 #endif
771  )
772 {
773  int i = 0;
774  struct magic *m;
775  char *t,
776  *s;
777  /* allocate magic structure entry */
778  if ((m = (struct magic *) calloc(1, sizeof(struct magic))) == NULL) {
779  kdError(7018) << "parse: Out of memory." << endl;
780  return -1;
781  }
782  /* append to linked list */
783  m->next = NULL;
784  if (!conf->magic || !conf->last) {
785  conf->magic = conf->last = m;
786  } else {
787  conf->last->next = m;
788  conf->last = m;
789  }
790 
791  /* set values in magic structure */
792  m->flag = 0;
793  m->cont_level = 0;
794 #ifdef DEBUG_LINENUMBERS
795  m->lineno = lineno;
796 #endif
797 
798  while (*l == '>') {
799  ++l; /* step over */
800  m->cont_level++;
801  }
802 
803  if (m->cont_level != 0 && *l == '(') {
804  ++l; /* step over */
805  m->flag |= INDIR;
806  }
807  /* get offset, then skip over it */
808  m->offset = (int) strtol(l, &t, 0);
809  if (l == t) {
810  kdError(7018) << "parse: offset " << l << " invalid" << endl;
811  }
812  l = t;
813 
814  if (m->flag & INDIR) {
815  m->in.type = LONG;
816  m->in.offset = 0;
817  /*
818  * read [.lbs][+-]nnnnn)
819  */
820  if (*l == '.') {
821  switch (*++l) {
822  case 'l':
823  m->in.type = LONG;
824  break;
825  case 's':
826  m->in.type = SHORT;
827  break;
828  case 'b':
829  m->in.type = BYTE;
830  break;
831  default:
832  kdError(7018) << "parse: indirect offset type " << *l << " invalid" << endl;
833  break;
834  }
835  l++;
836  }
837  s = l;
838  if (*l == '+' || *l == '-')
839  l++;
840  if (isdigit((unsigned char) *l)) {
841  m->in.offset = strtol(l, &t, 0);
842  if (*s == '-')
843  m->in.offset = -m->in.offset;
844  } else
845  t = l;
846  if (*t++ != ')') {
847  kdError(7018) << "parse: missing ')' in indirect offset" << endl;
848  }
849  l = t;
850  }
851  while (isascii((unsigned char) *l) && isdigit((unsigned char) *l))
852  ++l;
853  EATAB;
854 
855 #define NBYTE 4
856 #define NSHORT 5
857 #define NLONG 4
858 #define NSTRING 6
859 #define NDATE 4
860 #define NBESHORT 7
861 #define NBELONG 6
862 #define NBEDATE 6
863 #define NLESHORT 7
864 #define NLELONG 6
865 #define NLEDATE 6
866 
867  if (*l == 'u') {
868  ++l;
869  m->flag |= UNSIGNED;
870  }
871  /* get type, skip it */
872  if (strncmp(l, "byte", NBYTE) == 0) {
873  m->type = BYTE;
874  l += NBYTE;
875  } else if (strncmp(l, "short", NSHORT) == 0) {
876  m->type = SHORT;
877  l += NSHORT;
878  } else if (strncmp(l, "long", NLONG) == 0) {
879  m->type = LONG;
880  l += NLONG;
881  } else if (strncmp(l, "string", NSTRING) == 0) {
882  m->type = STRING;
883  l += NSTRING;
884  } else if (strncmp(l, "date", NDATE) == 0) {
885  m->type = DATE;
886  l += NDATE;
887  } else if (strncmp(l, "beshort", NBESHORT) == 0) {
888  m->type = BESHORT;
889  l += NBESHORT;
890  } else if (strncmp(l, "belong", NBELONG) == 0) {
891  m->type = BELONG;
892  l += NBELONG;
893  } else if (strncmp(l, "bedate", NBEDATE) == 0) {
894  m->type = BEDATE;
895  l += NBEDATE;
896  } else if (strncmp(l, "leshort", NLESHORT) == 0) {
897  m->type = LESHORT;
898  l += NLESHORT;
899  } else if (strncmp(l, "lelong", NLELONG) == 0) {
900  m->type = LELONG;
901  l += NLELONG;
902  } else if (strncmp(l, "ledate", NLEDATE) == 0) {
903  m->type = LEDATE;
904  l += NLEDATE;
905  } else {
906  kdError(7018) << "parse: type " << l << " invalid" << endl;
907  return -1;
908  }
909  /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
910  if (*l == '&') {
911  ++l;
912  m->mask = signextend(m, strtol(l, &l, 0));
913  } else
914  m->mask = (unsigned long) ~0L;
915  EATAB;
916 
917  switch (*l) {
918  case '>':
919  case '<':
920  /* Old-style anding: "0 byte &0x80 dynamically linked" */
921  case '&':
922  case '^':
923  case '=':
924  m->reln = *l;
925  ++l;
926  break;
927  case '!':
928  if (m->type != STRING) {
929  m->reln = *l;
930  ++l;
931  break;
932  }
933  /* FALL THROUGH */
934  default:
935  if (*l == 'x' && isascii((unsigned char) l[1]) &&
936  isspace((unsigned char) l[1])) {
937  m->reln = *l;
938  ++l;
939  goto GetDesc; /* Bill The Cat */
940  }
941  m->reln = '=';
942  break;
943  }
944  EATAB;
945 
946  if (getvalue(m, &l))
947  return -1;
948  /*
949  * now get last part - the description
950  */
951  GetDesc:
952  EATAB;
953  if (l[0] == '\b') {
954  ++l;
955  m->nospflag = 1;
956  } else if ((l[0] == '\\') && (l[1] == 'b')) {
957  ++l;
958  ++l;
959  m->nospflag = 1;
960  } else
961  m->nospflag = 0;
962  // Copy description - until EOL or '#' (for comments)
963  while (*l != '\0' && *l != '#' && i < MAXDESC-1)
964  m->desc[i++] = *l++;
965  m->desc[i] = '\0';
966  // Remove trailing spaces
967  while (--i>0 && isspace( m->desc[i] ))
968  m->desc[i] = '\0';
969 
970  // old code
971  //while ((m->desc[i++] = *l++) != '\0' && i < MAXDESC) /* NULLBODY */ ;
972 
973 #ifdef DEBUG_APPRENTICE
974  kdDebug(7018) << "parse: line=" << lineno << " m=" << m << " next=" << m->next << " cont=" << m->cont_level << " desc=" << (m->desc ? m->desc : "NULL") << endl;
975 #endif
976  return 0;
977 }
978 
979 /*
980  * Read a numeric value from a pointer, into the value union of a magic
981  * pointer, according to the magic type. Update the string pointer to point
982  * just after the number read. Return 0 for success, non-zero for failure.
983  */
984 static int
985 getvalue(struct magic *m, char **p)
986 {
987  int slen;
988 
989  if (m->type == STRING) {
990  *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
991  m->vallen = slen;
992  } else if (m->reln != 'x')
993  m->value.l = signextend(m, strtol(*p, p, 0));
994  return 0;
995 }
996 
997 /*
998  * Convert a string containing C character escapes. Stop at an unescaped
999  * space or tab. Copy the converted version to "p", returning its length in
1000  * *slen. Return updated scan pointer as function result.
1001  */
1002 static char *
1003 getstr(register char *s, register char *p, int plen, int *slen)
1004 {
1005  char *origs = s,
1006  *origp = p;
1007  char *pmax = p + plen - 1;
1008  register int c;
1009  register int val;
1010 
1011  while ((c = *s++) != '\0') {
1012  if (isspace((unsigned char) c))
1013  break;
1014  if (p >= pmax) {
1015  kdError(7018) << "String too long: " << origs << endl;
1016  break;
1017  }
1018  if (c == '\\') {
1019  switch (c = *s++) {
1020 
1021  case '\0':
1022  goto out;
1023 
1024  default:
1025  *p++ = (char) c;
1026  break;
1027 
1028  case 'n':
1029  *p++ = '\n';
1030  break;
1031 
1032  case 'r':
1033  *p++ = '\r';
1034  break;
1035 
1036  case 'b':
1037  *p++ = '\b';
1038  break;
1039 
1040  case 't':
1041  *p++ = '\t';
1042  break;
1043 
1044  case 'f':
1045  *p++ = '\f';
1046  break;
1047 
1048  case 'v':
1049  *p++ = '\v';
1050  break;
1051 
1052  /* \ and up to 3 octal digits */
1053  case '0':
1054  case '1':
1055  case '2':
1056  case '3':
1057  case '4':
1058  case '5':
1059  case '6':
1060  case '7':
1061  val = c - '0';
1062  c = *s++; /* try for 2 */
1063  if (c >= '0' && c <= '7') {
1064  val = (val << 3) | (c - '0');
1065  c = *s++; /* try for 3 */
1066  if (c >= '0' && c <= '7')
1067  val = (val << 3) | (c - '0');
1068  else
1069  --s;
1070  } else
1071  --s;
1072  *p++ = (char) val;
1073  break;
1074 
1075  /* \x and up to 3 hex digits */
1076  case 'x':
1077  val = 'x'; /* Default if no digits */
1078  c = hextoint(*s++); /* Get next char */
1079  if (c >= 0) {
1080  val = c;
1081  c = hextoint(*s++);
1082  if (c >= 0) {
1083  val = (val << 4) + c;
1084  c = hextoint(*s++);
1085  if (c >= 0) {
1086  val = (val << 4) + c;
1087  } else
1088  --s;
1089  } else
1090  --s;
1091  } else
1092  --s;
1093  *p++ = (char) val;
1094  break;
1095  }
1096  } else
1097  *p++ = (char) c;
1098  }
1099  out:
1100  *p = '\0';
1101  *slen = p - origp;
1102  //for ( char* foo = origp; foo < p ; ++foo )
1103  // kdDebug(7018) << " " << *foo << endl;
1104  return s;
1105 }
1106 
1107 
1108 /* Single hex char to int; -1 if not a hex char. */
1109 static int
1110 hextoint(int c)
1111 {
1112  if (!isascii((unsigned char) c))
1113  return -1;
1114  if (isdigit((unsigned char) c))
1115  return c - '0';
1116  if ((c >= 'a') && (c <= 'f'))
1117  return c + 10 - 'a';
1118  if ((c >= 'A') && (c <= 'F'))
1119  return c + 10 - 'A';
1120  return -1;
1121 }
1122 
1123 /*
1124  * Convert the byte order of the data we are looking at
1125  */
1126 static int
1127 mconvert(union VALUETYPE *p, struct magic *m)
1128 {
1129  switch (m->type) {
1130  case BYTE:
1131  return 1;
1132  case STRING:
1133  /* Null terminate */
1134  p->s[sizeof(p->s) - 1] = '\0';
1135  return 1;
1136 #ifndef WORDS_BIGENDIAN
1137  case SHORT:
1138 #endif
1139  case BESHORT:
1140  p->h = (short) ((p->hs[0] << 8) | (p->hs[1]));
1141  return 1;
1142 #ifndef WORDS_BIGENDIAN
1143  case LONG:
1144  case DATE:
1145 #endif
1146  case BELONG:
1147  case BEDATE:
1148  p->l = (long)
1149  ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3]));
1150  return 1;
1151 #ifdef WORDS_BIGENDIAN
1152  case SHORT:
1153 #endif
1154  case LESHORT:
1155  p->h = (short) ((p->hs[1] << 8) | (p->hs[0]));
1156  return 1;
1157 #ifdef WORDS_BIGENDIAN
1158  case LONG:
1159  case DATE:
1160 #endif
1161  case LELONG:
1162  case LEDATE:
1163  p->l = (long)
1164  ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0]));
1165  return 1;
1166  default:
1167  kdError(7018) << "mconvert: invalid type " << m->type << endl;
1168  return 0;
1169  }
1170 }
1171 
1172 
1173 static int
1174 mget(union VALUETYPE *p, unsigned char *s, struct magic *m,
1175  int nbytes)
1176 {
1177  long offset = m->offset;
1178  switch ( m->type )
1179  {
1180  case BYTE:
1181  if ( offset + 1 > nbytes-1 ) // nbytes = (size of file) + 1
1182  return 0;
1183  break;
1184  case SHORT:
1185  case BESHORT:
1186  case LESHORT:
1187  if ( offset + 2 > nbytes-1 )
1188  return 0;
1189  break;
1190  case LONG:
1191  case BELONG:
1192  case LELONG:
1193  case DATE:
1194  case BEDATE:
1195  case LEDATE:
1196  if ( offset + 4 > nbytes-1 )
1197  return 0;
1198  break;
1199  case STRING:
1200  break;
1201  }
1202 
1203 // The file length might be < sizeof(union VALUETYPE) (David)
1204 // -> pad with zeros (the 'file' command does it this way)
1205 // Thanks to Stan Covington <stan@calderasystems.com> for detailed report
1206  if (offset + (int)sizeof(union VALUETYPE) > nbytes)
1207  {
1208  int have = nbytes - offset;
1209  memset(p, 0, sizeof(union VALUETYPE));
1210  if (have > 0)
1211  memcpy(p, s + offset, have);
1212  } else
1213  memcpy(p, s + offset, sizeof(union VALUETYPE));
1214 
1215  if (!mconvert(p, m))
1216  return 0;
1217 
1218  if (m->flag & INDIR) {
1219 
1220  switch (m->in.type) {
1221  case BYTE:
1222  offset = p->b + m->in.offset;
1223  break;
1224  case SHORT:
1225  offset = p->h + m->in.offset;
1226  break;
1227  case LONG:
1228  offset = p->l + m->in.offset;
1229  break;
1230  }
1231 
1232  if (offset + (int)sizeof(union VALUETYPE) > nbytes)
1233  return 0;
1234 
1235  memcpy(p, s + offset, sizeof(union VALUETYPE));
1236 
1237  if (!mconvert(p, m))
1238  return 0;
1239  }
1240  return 1;
1241 }
1242 
1243 static int
1244 mcheck(union VALUETYPE *p, struct magic *m)
1245 {
1246  register unsigned long l = m->value.l;
1247  register unsigned long v;
1248  int matched;
1249 
1250  if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) {
1251  kdError(7018) << "BOINK" << endl;
1252  return 1;
1253  }
1254  switch (m->type) {
1255  case BYTE:
1256  v = p->b;
1257  break;
1258 
1259  case SHORT:
1260  case BESHORT:
1261  case LESHORT:
1262  v = p->h;
1263  break;
1264 
1265  case LONG:
1266  case BELONG:
1267  case LELONG:
1268  case DATE:
1269  case BEDATE:
1270  case LEDATE:
1271  v = p->l;
1272  break;
1273 
1274  case STRING:
1275  l = 0;
1276  /*
1277  * What we want here is: v = strncmp(m->value.s, p->s,
1278  * m->vallen); but ignoring any nulls. bcmp doesn't give
1279  * -/+/0 and isn't universally available anyway.
1280  */
1281  v = 0;
1282  {
1283  register unsigned char *a = (unsigned char *) m->value.s;
1284  register unsigned char *b = (unsigned char *) p->s;
1285  register int len = m->vallen;
1286  Q_ASSERT(len);
1287 
1288  while (--len >= 0)
1289  if ((v = *b++ - *a++) != 0)
1290  break;
1291  }
1292  break;
1293  default:
1294  kdError(7018) << "mcheck: invalid type " << m->type << endl;
1295  return 0; /* NOTREACHED */
1296  }
1297 #if 0
1298  qDebug("Before signextend %08x", v);
1299 #endif
1300  v = signextend(m, v) & m->mask;
1301 #if 0
1302  qDebug("After signextend %08x", v);
1303 #endif
1304 
1305  switch (m->reln) {
1306  case 'x':
1307  matched = 1;
1308  break;
1309 
1310  case '!':
1311  matched = v != l;
1312  break;
1313 
1314  case '=':
1315  matched = v == l;
1316  break;
1317 
1318  case '>':
1319  if (m->flag & UNSIGNED)
1320  matched = v > l;
1321  else
1322  matched = (long) v > (long) l;
1323  break;
1324 
1325  case '<':
1326  if (m->flag & UNSIGNED)
1327  matched = v < l;
1328  else
1329  matched = (long) v < (long) l;
1330  break;
1331 
1332  case '&':
1333  matched = (v & l) == l;
1334  break;
1335 
1336  case '^':
1337  matched = (v & l) != l;
1338  break;
1339 
1340  default:
1341  matched = 0;
1342  kdError(7018) << "mcheck: can't happen: invalid relation " << m->reln << "." << endl;
1343  break; /* NOTREACHED */
1344  }
1345 
1346  return matched;
1347 }
1348 
1349 /*
1350  * magic_process - process input file fn. Opens the file and reads a
1351  * fixed-size buffer to begin processing the contents.
1352  */
1353 
1354 void process(struct config_rec* conf, const TQString & fn)
1355 {
1356  int fd = 0;
1357  unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
1358  KDE_struct_stat sb;
1359  int nbytes = 0; /* number of bytes read from a datafile */
1360  int tagbytes = 0; /* size of prefixed tag */
1361  TQCString fileName = TQFile::encodeName( fn );
1362 
1363  /*
1364  * first try judging the file based on its filesystem status
1365  */
1366  if (fsmagic(conf, fileName, &sb) != 0) {
1367  //resultBuf += "\n";
1368  return;
1369  }
1370  if ((fd = KDE_open(fileName, O_RDONLY)) < 0) {
1371  /* We can't open it, but we were able to stat it. */
1372  /*
1373  * if (sb.st_mode & 0002) addResult("writable, ");
1374  * if (sb.st_mode & 0111) addResult("executable, ");
1375  */
1376  //kdDebug(7018) << "can't read `" << fn << "' (" << strerror(errno) << ")." << endl;
1377  conf->resultBuf = MIME_BINARY_UNREADABLE;
1378  return;
1379  }
1380  /*
1381  * try looking at the first HOWMANY bytes
1382  */
1383  if ((nbytes = read(fd, (char *) buf, HOWMANY)) == -1) {
1384  kdError(7018) << "" << fn << " read failed (" << strerror(errno) << ")." << endl;
1385  conf->resultBuf = MIME_BINARY_UNREADABLE;
1386  (void)close(fd);
1387  return;
1388  }
1389  if ((tagbytes = tagmagic(buf, nbytes))) {
1390  // Read buffer at new position
1391  lseek(fd, tagbytes, SEEK_SET);
1392  nbytes = read(fd, (char*)buf, HOWMANY);
1393  if (nbytes < 0) {
1394  conf->resultBuf = MIME_BINARY_UNREADABLE;
1395  (void)close(fd);
1396  return;
1397  }
1398  }
1399  if (nbytes == 0) {
1400  conf->resultBuf = MIME_BINARY_ZEROSIZE;
1401  } else {
1402  buf[nbytes++] = '\0'; /* null-terminate it */
1403  tryit(conf, buf, nbytes);
1404  }
1405 
1406  if ( conf->utimeConf && conf->utimeConf->restoreAccessTime( fn ) )
1407  {
1408  /*
1409  * Try to restore access, modification times if read it.
1410  * This changes the "change" time (ctime), but we can't do anything
1411  * about that.
1412  */
1413  struct utimbuf utbuf;
1414  utbuf.actime = sb.st_atime;
1415  utbuf.modtime = sb.st_mtime;
1416  (void) utime(fileName, &utbuf);
1417  }
1418  (void) close(fd);
1419 }
1420 
1421 
1422 static void tryit(struct config_rec* conf, unsigned char *buf, int nb)
1423 {
1424  /* try tests in /etc/magic (or surrogate magic file) */
1425  if (match(conf, buf, nb))
1426  return;
1427 
1428  /* try known keywords, check for ascii-ness too. */
1429  if (ascmagic(conf, buf, nb) == 1)
1430  return;
1431 
1432  /* see if it's plain text */
1433  if (textmagic(conf, buf, nb))
1434  return;
1435 
1436  /* abandon hope, all ye who remain here */
1437  conf->resultBuf = MIME_BINARY_UNKNOWN;
1438  conf->accuracy = 0;
1439 }
1440 
1441 static int
1442 fsmagic(struct config_rec* conf, const char *fn, KDE_struct_stat *sb)
1443 {
1444  int ret = 0;
1445 
1446  /*
1447  * Fstat is cheaper but fails for files you don't have read perms on.
1448  * On 4.2BSD and similar systems, use lstat() to identify symlinks.
1449  */
1450  ret = KDE_lstat(fn, sb); /* don't merge into if; see "ret =" above */
1451 
1452  if (ret) {
1453  return 1;
1454 
1455  }
1456  /*
1457  * if (sb->st_mode & S_ISUID) resultBuf += "setuid ";
1458  * if (sb->st_mode & S_ISGID) resultBuf += "setgid ";
1459  * if (sb->st_mode & S_ISVTX) resultBuf += "sticky ";
1460  */
1461 
1462  switch (sb->st_mode & S_IFMT) {
1463  case S_IFDIR:
1464  conf->resultBuf = MIME_INODE_DIR;
1465  return 1;
1466  case S_IFCHR:
1467  conf->resultBuf = MIME_INODE_CDEV;
1468  return 1;
1469  case S_IFBLK:
1470  conf->resultBuf = MIME_INODE_BDEV;
1471  return 1;
1472  /* TODO add code to handle V7 MUX and Blit MUX files */
1473 #ifdef S_IFIFO
1474  case S_IFIFO:
1475  conf->resultBuf = MIME_INODE_FIFO;
1476  return 1;
1477 #endif
1478 #ifdef S_IFLNK
1479  case S_IFLNK:
1480  {
1481  char buf[BUFSIZ + BUFSIZ + 4];
1482  register int nch;
1483  KDE_struct_stat tstatbuf;
1484 
1485  if ((nch = readlink(fn, buf, BUFSIZ - 1)) <= 0) {
1486  conf->resultBuf = MIME_INODE_LINK;
1487  //conf->resultBuf += "\nunreadable";
1488  return 1;
1489  }
1490  buf[nch] = '\0'; /* readlink(2) forgets this */
1491  /* If broken symlink, say so and quit early. */
1492  if (*buf == '/') {
1493  if (KDE_stat(buf, &tstatbuf) < 0) {
1494  conf->resultBuf = MIME_INODE_LINK;
1495  //conf->resultBuf += "\nbroken";
1496  return 1;
1497  }
1498  } else {
1499  char *tmp;
1500  char buf2[BUFSIZ + BUFSIZ + 4];
1501 
1502  strncpy(buf2, fn, BUFSIZ);
1503  buf2[BUFSIZ] = 0;
1504 
1505  if ((tmp = strrchr(buf2, '/')) == NULL) {
1506  tmp = buf; /* in current dir */
1507  } else {
1508  /* dir part plus (rel.) link */
1509  *++tmp = '\0';
1510  strcat(buf2, buf);
1511  tmp = buf2;
1512  }
1513  if (KDE_stat(tmp, &tstatbuf) < 0) {
1514  conf->resultBuf = MIME_INODE_LINK;
1515  //conf->resultBuf += "\nbroken";
1516  return 1;
1517  } else
1518  strcpy(buf, tmp);
1519  }
1520  if (conf->followLinks)
1521  process( conf, TQFile::decodeName( buf ) );
1522  else
1523  conf->resultBuf = MIME_INODE_LINK;
1524  return 1;
1525  }
1526  return 1;
1527 #endif
1528 #ifdef S_IFSOCK
1529 #ifndef __COHERENT__
1530  case S_IFSOCK:
1531  conf->resultBuf = MIME_INODE_SOCK;
1532  return 1;
1533 #endif
1534 #endif
1535  case S_IFREG:
1536  break;
1537  default:
1538  kdError(7018) << "KMimeMagic::fsmagic: invalid mode 0" << sb->st_mode << "." << endl;
1539  /* NOTREACHED */
1540  }
1541 
1542  /*
1543  * regular file, check next possibility
1544  */
1545  if (sb->st_size == 0) {
1546  conf->resultBuf = MIME_BINARY_ZEROSIZE;
1547  return 1;
1548  }
1549  return 0;
1550 }
1551 
1552 /*
1553  * Go through the whole list, stopping if you find a match. Process all the
1554  * continuations of that match before returning.
1555  *
1556  * We support multi-level continuations:
1557  *
1558  * At any time when processing a successful top-level match, there is a current
1559  * continuation level; it represents the level of the last successfully
1560  * matched continuation.
1561  *
1562  * Continuations above that level are skipped as, if we see one, it means that
1563  * the continuation that controls them - i.e, the lower-level continuation
1564  * preceding them - failed to match.
1565  *
1566  * Continuations below that level are processed as, if we see one, it means
1567  * we've finished processing or skipping higher-level continuations under the
1568  * control of a successful or unsuccessful lower-level continuation, and are
1569  * now seeing the next lower-level continuation and should process it. The
1570  * current continuation level reverts to the level of the one we're seeing.
1571  *
1572  * Continuations at the current level are processed as, if we see one, there's
1573  * no lower-level continuation that may have failed.
1574  *
1575  * If a continuation matches, we bump the current continuation level so that
1576  * higher-level continuations are processed.
1577  */
1578 static int
1579 match(struct config_rec* conf, unsigned char *s, int nbytes)
1580 {
1581  int cont_level = 0;
1582  union VALUETYPE p;
1583  struct magic *m;
1584 
1585 #ifdef DEBUG_MIMEMAGIC
1586  kdDebug(7018) << "match: conf=" << conf << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
1587  for (m = conf->magic; m; m = m->next) {
1588  if (isprint((((unsigned long) m) >> 24) & 255) &&
1589  isprint((((unsigned long) m) >> 16) & 255) &&
1590  isprint((((unsigned long) m) >> 8) & 255) &&
1591  isprint(((unsigned long) m) & 255)) {
1592  kdDebug(7018) << "match: POINTER CLOBBERED! " << endl;
1593  break;
1594  }
1595  }
1596 #endif
1597 
1598  for (m = conf->magic; m; m = m->next) {
1599 #ifdef DEBUG_MIMEMAGIC
1600  kdDebug(7018) << "match: line=" << m->lineno << " desc=" << m->desc << endl;
1601 #endif
1602  memset(&p, 0, sizeof(union VALUETYPE));
1603 
1604  /* check if main entry matches */
1605  if (!mget(&p, s, m, nbytes) ||
1606  !mcheck(&p, m)) {
1607  struct magic *m_cont;
1608 
1609  /*
1610  * main entry didn't match, flush its continuations
1611  */
1612  if (!m->next || (m->next->cont_level == 0)) {
1613  continue;
1614  }
1615  m_cont = m->next;
1616  while (m_cont && (m_cont->cont_level != 0)) {
1617 #ifdef DEBUG_MIMEMAGIC
1618  kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m_cont->cont_level << " mc=" << m_cont->lineno << " mc->next=" << m_cont << " " << endl;
1619 #endif
1620  /*
1621  * this trick allows us to keep *m in sync
1622  * when the continue advances the pointer
1623  */
1624  m = m_cont;
1625  m_cont = m_cont->next;
1626  }
1627  continue;
1628  }
1629  /* if we get here, the main entry rule was a match */
1630  /* this will be the last run through the loop */
1631 #ifdef DEBUG_MIMEMAGIC
1632  kdDebug(7018) << "match: rule matched, line=" << m->lineno << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
1633 #endif
1634 
1635  /* remember the match */
1636  conf->resultBuf = m->desc;
1637 
1638  cont_level++;
1639  /*
1640  * while (m && m->next && m->next->cont_level != 0 && ( m =
1641  * m->next ))
1642  */
1643  m = m->next;
1644  while (m && (m->cont_level != 0)) {
1645 #ifdef DEBUG_MIMEMAGIC
1646  kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m->cont_level << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
1647 #endif
1648  if (cont_level >= m->cont_level) {
1649  if (cont_level > m->cont_level) {
1650  /*
1651  * We're at the end of the level
1652  * "cont_level" continuations.
1653  */
1654  cont_level = m->cont_level;
1655  }
1656  if (mget(&p, s, m, nbytes) &&
1657  mcheck(&p, m)) {
1658  /*
1659  * This continuation matched. Print
1660  * its message, with a blank before
1661  * it if the previous item printed
1662  * and this item isn't empty.
1663  */
1664 #ifdef DEBUG_MIMEMAGIC
1665  kdDebug(7018) << "continuation matched" << endl;
1666 #endif
1667  conf->resultBuf = m->desc;
1668  cont_level++;
1669  }
1670  }
1671  /* move to next continuation record */
1672  m = m->next;
1673  }
1674  // KDE-specific: need an actual mimetype for a real match
1675  // If we only matched a rule with continuations but no mimetype, it's not a match
1676  if ( !conf->resultBuf.isEmpty() )
1677  {
1678 #ifdef DEBUG_MIMEMAGIC
1679  kdDebug(7018) << "match: matched" << endl;
1680 #endif
1681  return 1; /* all through */
1682  }
1683  }
1684 #ifdef DEBUG_MIMEMAGIC
1685  kdDebug(7018) << "match: failed" << endl;
1686 #endif
1687  return 0; /* no match at all */
1688 }
1689 
1690 // Try to parse prefixed tags before matching on content
1691 // Sofar only ID3v2 tags (<=.4) are handled
1692 static int tagmagic(unsigned char *buf, int nbytes)
1693 {
1694  if(nbytes<40) return 0;
1695  if(buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') {
1696  int size = 10;
1697  // Sanity (known version, no unknown flags)
1698  if(buf[3] > 4) return 0;
1699  if(buf[5] & 0x0F) return 0;
1700  // Tag has v4 footer
1701  if(buf[5] & 0x10) size += 10;
1702  // Calculated syncsafe size
1703  size += buf[9];
1704  size += buf[8] << 7;
1705  size += buf[7] << 14;
1706  size += buf[6] << 21;
1707  return size;
1708  }
1709  return 0;
1710 }
1711 
1712 struct Token {
1713  char *data;
1714  int length;
1715 };
1716 
1717 struct Tokenizer
1718 {
1719  Tokenizer(char* buf, int nbytes) {
1720  data = buf;
1721  length = nbytes;
1722  pos = 0;
1723  }
1724  bool isNewLine() {
1725  return newline;
1726  }
1727  Token* nextToken() {
1728  if (pos == 0)
1729  newline = true;
1730  else
1731  newline = false;
1732  token.data = data+pos;
1733  token.length = 0;
1734  while(pos<length) {
1735  switch (data[pos]) {
1736  case '\n':
1737  newline = true;
1738  case '\0':
1739  case '\t':
1740  case ' ':
1741  case '\r':
1742  case '\f':
1743  case ',':
1744  case ';':
1745  case '>':
1746  if (token.length == 0) token.data++;
1747  else
1748  return &token;
1749  break;
1750  default:
1751  token.length++;
1752  }
1753  pos++;
1754  }
1755  return &token;
1756  }
1757 
1758 private:
1759  Token token;
1760  char* data;
1761  int length;
1762  int pos;
1763  bool newline;
1764 };
1765 
1766 
1767 /* an optimization over plain strcmp() */
1768 //#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
1769 static inline bool STREQ(const Token *token, const char *b) {
1770  const char *a = token->data;
1771  int len = token->length;
1772  if (a == b) return true;
1773  while(*a && *b && len > 0) {
1774  if (*a != *b) return false;
1775  a++; b++; len--;
1776  }
1777  return (len == 0 && *b == 0);
1778 }
1779 
1780 static int ascmagic(struct config_rec* conf, unsigned char *buf, int nbytes)
1781 {
1782  int i;
1783  double pct, maxpct, pctsum;
1784  double pcts[NTYPES];
1785  int mostaccurate, tokencount;
1786  int typeset, jonly, conly, jconly, objconly, cpponly;
1787  int has_escapes = 0;
1788  //unsigned char *s;
1789  //char nbuf[HOWMANY + 1]; /* one extra for terminating '\0' */
1790 
1791  /* these are easy, do them first */
1792  conf->accuracy = 70;
1793 
1794  /*
1795  * for troff, look for . + letter + letter or .\"; this must be done
1796  * to disambiguate tar archives' ./file and other trash from real
1797  * troff input.
1798  */
1799  if (*buf == '.') {
1800  unsigned char *tp = buf + 1;
1801 
1802  while (isascii(*tp) && isspace(*tp))
1803  ++tp; /* skip leading whitespace */
1804  if ((isascii(*tp) && (isalnum(*tp) || *tp == '\\') &&
1805  isascii(*(tp + 1)) && (isalnum(*(tp + 1)) || *tp == '"'))) {
1806  conf->resultBuf = MIME_APPL_TROFF;
1807  return 1;
1808  }
1809  }
1810  if ((*buf == 'c' || *buf == 'C') &&
1811  isascii(*(buf + 1)) && isspace(*(buf + 1))) {
1812  /* Fortran */
1813  conf->resultBuf = MIME_TEXT_FORTRAN;
1814  return 1;
1815  }
1816  assert(nbytes-1 < HOWMANY + 1);
1817  /* look for tokens - this is expensive! */
1818  has_escapes = (memchr(buf, '\033', nbytes) != NULL);
1819  Tokenizer tokenizer((char*)buf, nbytes);
1820  const Token* token;
1821  bool linecomment = false, blockcomment = false;
1822  const struct names *p;
1823  int typecount[NTYPES];
1824 /*
1825  * Fritz:
1826  * Try a little harder on C/C++/Java.
1827  */
1828  memset(&typecount, 0, sizeof(typecount));
1829  typeset = 0;
1830  jonly = 0;
1831  conly = 0;
1832  jconly = 0;
1833  objconly = 0;
1834  cpponly = 0;
1835  tokencount = 0;
1836  bool foundClass = false; // mandatory for java
1837  // first collect all possible types and count matches
1838  // we stop at '>' too, because of "<title>blah</title>" on HTML pages
1839  while ((token = tokenizer.nextToken())->length > 0) {
1840 #ifdef DEBUG_MIMEMAGIC
1841  kdDebug(7018) << "KMimeMagic::ascmagic token=" << token << endl;
1842 #endif
1843  if (linecomment && tokenizer.isNewLine())
1844  linecomment = false;
1845  if (blockcomment && STREQ(token, "*/")) {
1846  blockcomment = false;
1847  continue;
1848  }
1849  for (p = names; p->name ; p++) {
1850  if (STREQ(token, p->name)) {
1851 #ifdef DEBUG_MIMEMAGIC
1852  kdDebug(7018) << "KMimeMagic::ascmagic token matches ! name=" << p->name << " type=" << p->type << endl;
1853 #endif
1854  tokencount++;
1855  typeset |= p->type;
1856  if(p->type & (L_C|L_CPP|L_JAVA|L_OBJC)) {
1857  if (linecomment || blockcomment) {
1858  continue;
1859  }
1860  else {
1861  switch(p->type & (L_C|L_CPP|L_JAVA|L_OBJC))
1862  {
1863  case L_JAVA:
1864  jonly++;
1865  break;
1866  case L_OBJC:
1867  objconly++;
1868  break;
1869  case L_CPP:
1870  cpponly++;
1871  break;
1872  case (L_CPP|L_JAVA):
1873  jconly++;
1874  if ( !foundClass && STREQ(token, "class") )
1875  foundClass = true;
1876  break;
1877  case (L_C|L_CPP):
1878  conly++;
1879  break;
1880  default:
1881  if (STREQ(token, "//")) linecomment = true;
1882  if (STREQ(token, "/*")) blockcomment = true;
1883  }
1884  }
1885  }
1886  for (i = 0; i < (int)NTYPES; i++) {
1887  if ((1 << i) & p->type) typecount[i]+= p->type & FLAG_STRONG ? 2 : 1;
1888  }
1889  }
1890  }
1891  }
1892 
1893  if (typeset & (L_C|L_CPP|L_JAVA|L_OBJC)) {
1894  conf->accuracy = 60;
1895  if (!(typeset & ~(L_C|L_CPP|L_JAVA|L_OBJC))) {
1896 #ifdef DEBUG_MIMEMAGIC
1897  kdDebug(7018) << "C/C++/Java/ObjC: jonly=" << jonly << " conly=" << conly << " jconly=" << jconly << " objconly=" << objconly << endl;
1898 #endif
1899  if (jonly > 1 && foundClass) {
1900  // At least two java-only tokens have matched, including "class"
1901  conf->resultBuf = TQString(types[P_JAVA].type);
1902  return 1;
1903  }
1904  if (jconly > 1) {
1905  // At least two non-C (only C++ or Java) token have matched.
1906  if (typecount[P_JAVA] < typecount[P_CPP])
1907  conf->resultBuf = TQString(types[P_CPP].type);
1908  else
1909  conf->resultBuf = TQString(types[P_JAVA].type);
1910  return 1;
1911  }
1912  if (conly + cpponly > 1) {
1913  // Either C or C++.
1914  if (cpponly > 0)
1915  conf->resultBuf = TQString(types[P_CPP].type);
1916  else
1917  conf->resultBuf = TQString(types[P_C].type);
1918  return 1;
1919  }
1920  if (objconly > 0) {
1921  conf->resultBuf = TQString(types[P_OBJC].type);
1922  return 1;
1923  }
1924  }
1925  }
1926 
1927  /* Neither C, C++ or Java (or all of them without able to distinguish):
1928  * Simply take the token-class with the highest
1929  * matchcount > 0
1930  */
1931  mostaccurate = -1;
1932  maxpct = pctsum = 0.0;
1933  for (i = 0; i < (int)NTYPES; i++) {
1934  if (typecount[i] > 1) { // one word is not enough, we need at least two
1935  pct = (double)typecount[i] / (double)types[i].kwords *
1936  (double)types[i].weight;
1937  pcts[i] = pct;
1938  pctsum += pct;
1939  if (pct > maxpct) {
1940  maxpct = pct;
1941  mostaccurate = i;
1942  }
1943 #ifdef DEBUG_MIMEMAGIC
1944  kdDebug(7018) << "" << types[i].type << " has " << typecount[i] << " hits, " << types[i].kwords << " kw, weight " << types[i].weight << ", " << pct << " -> max = " << maxpct << "\n" << endl;
1945 #endif
1946  }
1947  }
1948  if (mostaccurate >= 0) {
1949  if ( mostaccurate != P_JAVA || foundClass ) // 'class' mandatory for java
1950  {
1951  conf->accuracy = (int)(pcts[mostaccurate] / pctsum * 60);
1952 #ifdef DEBUG_MIMEMAGIC
1953  kdDebug(7018) << "mostaccurate=" << mostaccurate << " pcts=" << pcts[mostaccurate] << " pctsum=" << pctsum << " accuracy=" << conf->accuracy << endl;
1954 #endif
1955  conf->resultBuf = TQString(types[mostaccurate].type);
1956  return 1;
1957  }
1958  }
1959 
1960  switch (is_tar(buf, nbytes)) {
1961  case 1:
1962  /* V7 tar archive */
1963  conf->resultBuf = MIME_APPL_TAR;
1964  conf->accuracy = 90;
1965  return 1;
1966  case 2:
1967  /* POSIX tar archive */
1968  conf->resultBuf = MIME_APPL_TAR;
1969  conf->accuracy = 90;
1970  return 1;
1971  }
1972 
1973  for (i = 0; i < nbytes; i++) {
1974  if (!isascii(*(buf + i)))
1975  return 0; /* not all ascii */
1976  }
1977 
1978  /* all else fails, but it is ascii... */
1979  conf->accuracy = 90;
1980  if (has_escapes) {
1981  /* text with escape sequences */
1982  /* we leave this open for further differentiation later */
1983  conf->resultBuf = MIME_TEXT_UNKNOWN;
1984  } else {
1985  /* plain text */
1986  conf->resultBuf = MIME_TEXT_PLAIN;
1987  }
1988  return 1;
1989 }
1990 
1991 
1992 /* This code is taken from the "file" command, where it is licensed
1993  * in the "beer-ware license" :-)
1994  * Original author: <joerg@FreeBSD.ORG>
1995  * Simplified by David Faure to avoid the static array char[256].
1996  * Drastically simplified by Laurent Dard for the Trinity Desktop Environment
1997  * Configuration files with big lines are still text files:
1998  * line length checking is now avoided here.
1999  */
2000 static int textmagic(struct config_rec* conf, unsigned char * buf, int nbytes)
2001 {
2002  int i;
2003  unsigned char *cp;
2004 
2005  nbytes--;
2006 
2007  /* Look whether there are "unreasonable" characters. */
2008  for (i = 0, cp = buf; i < nbytes; i++, cp++)
2009  if ((*cp < 8) || (*cp>13 && *cp<32 && *cp!=27 ) || (*cp==0x7F))
2010  return 0;
2011 
2012  conf->resultBuf = MIME_TEXT_PLAIN;
2013  return 1;
2014 }
2015 
2016 
2017 /*
2018  * is_tar() -- figure out whether file is a tar archive.
2019  *
2020  * Stolen (by author of file utility) from the public domain tar program: Public
2021  * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
2022  *
2023  * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7
2024  * 1997/06/24 00:41:02 ikluft Exp ikluft $
2025  *
2026  * Comments changed and some code/comments reformatted for file command by Ian
2027  * Darwin.
2028  */
2029 
2030 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
2031 
2032 /*
2033  * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for
2034  * old UNIX tar file, 2 for Unix Std (POSIX) tar file.
2035  */
2036 
2037 static int
2038 is_tar(unsigned char *buf, int nbytes)
2039 {
2040  register union record *header = (union record *) buf;
2041  register int i;
2042  register long sum,
2043  recsum;
2044  register char *p;
2045 
2046  if (nbytes < (int)sizeof(union record))
2047  return 0;
2048 
2049  recsum = from_oct(8, header->header.chksum);
2050 
2051  sum = 0;
2052  p = header->charptr;
2053  for (i = sizeof(union record); --i >= 0;) {
2054  /*
2055  * We can't use unsigned char here because of old compilers,
2056  * e.g. V7.
2057  */
2058  sum += 0xFF & *p++;
2059  }
2060 
2061  /* Adjust checksum to count the "chksum" field as blanks. */
2062  for (i = sizeof(header->header.chksum); --i >= 0;)
2063  sum -= 0xFF & header->header.chksum[i];
2064  sum += ' ' * sizeof header->header.chksum;
2065 
2066  if (sum != recsum)
2067  return 0; /* Not a tar archive */
2068 
2069  if (0 == strcmp(header->header.magic, TMAGIC))
2070  return 2; /* Unix Standard tar archive */
2071 
2072  return 1; /* Old fashioned tar archive */
2073 }
2074 
2075 
2076 /*
2077  * Quick and dirty octal conversion.
2078  *
2079  * Result is -1 if the field is invalid (all blank, or nonoctal).
2080  */
2081 static long
2082 from_oct(int digs, char *where)
2083 {
2084  register long value;
2085 
2086  while (isspace(*where)) { /* Skip spaces */
2087  where++;
2088  if (--digs <= 0)
2089  return -1; /* All blank field */
2090  }
2091  value = 0;
2092  while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
2093  value = (value << 3) | (*where++ - '0');
2094  --digs;
2095  }
2096 
2097  if (digs > 0 && *where && !isspace(*where))
2098  return -1; /* Ended on non-space/nul */
2099 
2100  return value;
2101 }
2102 
2103 KMimeMagic::KMimeMagic()
2104 {
2105  // Magic file detection init
2106  TQString mimefile = locate( "mime", "magic" );
2107  init( mimefile );
2108  // Add snippets from share/config/magic/*
2109  TQStringList snippets = KGlobal::dirs()->findAllResources( "config", "magic/*.magic", true );
2110  for ( TQStringList::Iterator it = snippets.begin() ; it != snippets.end() ; ++it )
2111  if ( !mergeConfig( *it ) )
2112  kdWarning() << k_funcinfo << "Failed to parse " << *it << endl;
2113 }
2114 
2115 KMimeMagic::KMimeMagic(const TQString & _configfile)
2116 {
2117  init( _configfile );
2118 }
2119 
2120 void KMimeMagic::init( const TQString& _configfile )
2121 {
2122  int result;
2123  conf = new config_rec;
2124 
2125  /* set up the magic list (empty) */
2126  conf->magic = conf->last = NULL;
2127  magicResult = NULL;
2128  conf->followLinks = false;
2129 
2130  conf->utimeConf = 0L; // created on demand
2131  /* on the first time through we read the magic file */
2132  result = apprentice(_configfile);
2133  if (result == -1)
2134  return;
2135 #ifdef MIME_MAGIC_DEBUG_TABLE
2136  test_table();
2137 #endif
2138 }
2139 
2140 /*
2141  * The destructor.
2142  * Free the magic-table and other resources.
2143  */
2144 KMimeMagic::~KMimeMagic()
2145 {
2146  if (conf) {
2147  struct magic *p = conf->magic;
2148  struct magic *q;
2149  while (p) {
2150  q = p;
2151  p = p->next;
2152  free(q);
2153  }
2154  delete conf->utimeConf;
2155  delete conf;
2156  }
2157  delete magicResult;
2158 }
2159 
2160 bool
2161 KMimeMagic::mergeConfig(const TQString & _configfile)
2162 {
2163  kdDebug(7018) << k_funcinfo << _configfile << endl;
2164  int result;
2165 
2166  if (_configfile.isEmpty())
2167  return false;
2168  result = apprentice(_configfile);
2169  if (result == -1) {
2170  return false;
2171  }
2172 #ifdef MIME_MAGIC_DEBUG_TABLE
2173  test_table();
2174 #endif
2175  return true;
2176 }
2177 
2178 bool
2179 KMimeMagic::mergeBufConfig(char * _configbuf)
2180 {
2181  int result;
2182 
2183  if (conf) {
2184  result = buff_apprentice(_configbuf);
2185  if (result == -1)
2186  return false;
2187 #ifdef MIME_MAGIC_DEBUG_TABLE
2188  test_table();
2189 #endif
2190  return true;
2191  }
2192  return false;
2193 }
2194 
2195 void
2196 KMimeMagic::setFollowLinks( bool _enable )
2197 {
2198  conf->followLinks = _enable;
2199 }
2200 
2201 KMimeMagicResult *
2202 KMimeMagic::findBufferType(const TQByteArray &array)
2203 {
2204  unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
2205 
2206  conf->resultBuf = TQString::null;
2207  if ( !magicResult )
2208  magicResult = new KMimeMagicResult();
2209  magicResult->setInvalid();
2210  conf->accuracy = 100;
2211 
2212  int nbytes = array.size();
2213 
2214  if (nbytes > HOWMANY)
2215  nbytes = HOWMANY;
2216  memcpy(buf, array.data(), nbytes);
2217  if (nbytes == 0) {
2218  conf->resultBuf = MIME_BINARY_ZEROSIZE;
2219  } else {
2220  buf[nbytes++] = '\0'; /* null-terminate it */
2221  tryit(conf, buf, nbytes);
2222  }
2223  /* if we have any results, put them in the request structure */
2224  magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
2225  magicResult->setAccuracy(conf->accuracy);
2226  return magicResult;
2227 }
2228 
2229 static void
2230 refineResult(KMimeMagicResult *r, const TQString & _filename)
2231 {
2232  TQString tmp = r->mimeType();
2233  if (tmp.isEmpty())
2234  return;
2235  if ( tmp == "text/x-c" || tmp == "text/x-objc" )
2236  {
2237  if ( _filename.right(2) == ".h" )
2238  tmp += "hdr";
2239  else
2240  tmp += "src";
2241  r->setMimeType(tmp);
2242  }
2243  else
2244  if ( tmp == "text/x-c++" )
2245  {
2246  if ( _filename.endsWith(".h")
2247  || _filename.endsWith(".hh")
2248  || _filename.endsWith(".H")
2249  || !_filename.right(4).contains('.'))
2250  tmp += "hdr";
2251  else
2252  tmp += "src";
2253  r->setMimeType(tmp);
2254  }
2255  else
2256  if ( tmp == "application/x-sharedlib" )
2257  {
2258  if ( _filename.find( ".so" ) == -1 )
2259  {
2260  tmp = "application/x-executable";
2261  r->setMimeType( tmp );
2262  }
2263  }
2264 }
2265 
2266 KMimeMagicResult *
2267 KMimeMagic::findBufferFileType( const TQByteArray &data,
2268  const TQString &fn)
2269 {
2270  KMimeMagicResult * r = findBufferType( data );
2271  refineResult(r, fn);
2272  return r;
2273 }
2274 
2275 /*
2276  * Find the content-type of the given file.
2277  */
2278 KMimeMagicResult* KMimeMagic::findFileType(const TQString & fn)
2279 {
2280 #ifdef DEBUG_MIMEMAGIC
2281  kdDebug(7018) << "KMimeMagic::findFileType " << fn << endl;
2282 #endif
2283  conf->resultBuf = TQString::null;
2284 
2285  if ( !magicResult )
2286  magicResult = new KMimeMagicResult();
2287  magicResult->setInvalid();
2288  conf->accuracy = 100;
2289 
2290  if ( !conf->utimeConf )
2291  conf->utimeConf = new KMimeMagicUtimeConf();
2292 
2293  /* process it based on the file contents */
2294  process(conf, fn );
2295 
2296  /* if we have any results, put them in the request structure */
2297  //finishResult();
2298  magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
2299  magicResult->setAccuracy(conf->accuracy);
2300  refineResult(magicResult, fn);
2301  return magicResult;
2302 }

kio/kio

Skip menu "kio/kio"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kio/kio

Skip menu "kio/kio"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kio/kio by doxygen 1.8.3.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |