kio Library API Documentation

kmimemagic.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 Fritz Elfert <fritz@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00016    Boston, MA 02111-1307, USA.
00017 */
00018 #include "kmimemagic.h"
00019 #include <kdebug.h>
00020 #include <kapplication.h>
00021 #include <qfile.h>
00022 #include <ksimpleconfig.h>
00023 #include <kstandarddirs.h>
00024 #include <kstaticdeleter.h>
00025 #include <assert.h>
00026 
00027 KMimeMagic* KMimeMagic::s_pSelf = 0L;
00028 KStaticDeleter<KMimeMagic> kmimemagicsd;
00029 
00030 KMimeMagic* KMimeMagic::self()
00031 {
00032   if( !s_pSelf )
00033     initStatic();
00034   return s_pSelf;
00035 }
00036 
00037 void KMimeMagic::initStatic()
00038 {
00039   s_pSelf = kmimemagicsd.setObject( new KMimeMagic() );
00040   s_pSelf->setFollowLinks( TRUE );
00041 }
00042 
00043 #include <stdio.h>
00044 #include <unistd.h>
00045 #include <stdlib.h>
00046 #include <sys/wait.h>
00047 #include <sys/types.h>
00048 #include <sys/stat.h>
00049 #include <fcntl.h>
00050 #include <errno.h>
00051 #include <ctype.h>
00052 #include <time.h>
00053 #include <utime.h>
00054 #include <stdarg.h>
00055 #include <qregexp.h>
00056 #include <qstring.h>
00057 
00058 //#define MIME_MAGIC_DEBUG_TABLE // untested
00059 
00060 // Uncomment to debug the config-file parsing phase
00061 //#define DEBUG_APPRENTICE
00062 // Uncomment to debug the matching phase
00063 //#define DEBUG_MIMEMAGIC
00064 
00065 #if (defined DEBUG_MIMEMAGIC || defined DEBUG_APPRENTICE)
00066 #define DEBUG_LINENUMBERS
00067 #endif
00068 
00069 /*
00070  * data structures and related constants
00071  */
00072 #define DECLINED 999
00073 #define ERROR    998
00074 #define OK         0
00075 
00076 /*
00077  * Buitltin Mime types
00078  */
00079 #define MIME_BINARY_UNKNOWN    "application/octet-stream"
00080 #define MIME_BINARY_UNREADABLE "application/x-unreadable"
00081 #define MIME_BINARY_ZEROSIZE   "application/x-zerosize"
00082 #define MIME_TEXT_UNKNOWN      "text/plain"
00083 #define MIME_TEXT_PLAIN        "text/plain"
00084 #define MIME_INODE_DIR         "inode/directory"
00085 #define MIME_INODE_CDEV        "inode/chardevice"
00086 #define MIME_INODE_BDEV        "inode/blockdevice"
00087 #define MIME_INODE_FIFO        "inode/fifo"
00088 #define MIME_INODE_LINK        "inode/link"
00089 #define MIME_INODE_SOCK        "inode/socket"
00090 // Following should go in magic-file - Fritz
00091 #define MIME_APPL_TROFF        "application/x-troff"
00092 #define MIME_APPL_TAR          "application/x-tar"
00093 #define MIME_TEXT_FORTRAN      "text/x-fortran"
00094 
00095 #define MAXMIMESTRING        256
00096 
00097 #define HOWMANY 1024            /* big enough to recognize most WWW files */
00098 #define MAXDESC   50            /* max leng of text description */
00099 #define MAXstring 64            /* max leng of "string" types */
00100 
00101 typedef union VALUETYPE {
00102     unsigned char b;
00103     unsigned short h;
00104     unsigned long l;
00105     char s[MAXstring];
00106     unsigned char hs[2];    /* 2 bytes of a fixed-endian "short" */
00107     unsigned char hl[4];    /* 2 bytes of a fixed-endian "long" */
00108 } VALUETYPE;
00109 
00110 struct magic {
00111     struct magic *next;     /* link to next entry */
00112 #ifdef DEBUG_LINENUMBERS
00113     int lineno;             /* line number from magic file - doesn't say from which one ;) */
00114 #endif
00115 
00116     short flag;
00117 #define INDIR    1              /* if '>(...)' appears,  */
00118 #define UNSIGNED 2              /* comparison is unsigned */
00119     short cont_level;       /* level of ">" */
00120     struct {
00121         char type;      /* byte short long */
00122         long offset;    /* offset from indirection */
00123     } in;
00124     long offset;            /* offset to magic number */
00125     unsigned char reln;     /* relation (0=eq, '>'=gt, etc) */
00126     char type;              /* int, short, long or string. */
00127     char vallen;            /* length of string value, if any */
00128 #define BYTE       1
00129 #define SHORT      2
00130 #define LONG       4
00131 #define STRING     5
00132 #define DATE       6
00133 #define BESHORT    7
00134 #define BELONG     8
00135 #define BEDATE     9
00136 #define LESHORT   10
00137 #define LELONG    11
00138 #define LEDATE    12
00139     VALUETYPE value;        /* either number or string */
00140     unsigned long mask;     /* mask before comparison with value */
00141     char nospflag;          /* supress space character */
00142 
00143     /* NOTE: this string is suspected of overrunning - find it! */
00144     char desc[MAXDESC];     /* description */
00145 };
00146 
00147 /*
00148  * data structures for tar file recognition
00149  * --------------------------------------------------------------------------
00150  * Header file for public domain tar (tape archive) program.
00151  *
00152  * @(#)tar.h 1.20 86/10/29    Public Domain. Created 25 August 1985 by John
00153  * Gilmore, ihnp4!hoptoad!gnu.
00154  *
00155  * Header block on tape.
00156  *
00157  * I'm going to use traditional DP naming conventions here. A "block" is a big
00158  * chunk of stuff that we do I/O on. A "record" is a piece of info that we
00159  * care about. Typically many "record"s fit into a "block".
00160  */
00161 #define RECORDSIZE    512
00162 #define NAMSIZ    100
00163 #define TUNMLEN    32
00164 #define TGNMLEN    32
00165 
00166 union record {
00167     char charptr[RECORDSIZE];
00168     struct header {
00169         char name[NAMSIZ];
00170         char mode[8];
00171         char uid[8];
00172         char gid[8];
00173         char size[12];
00174         char mtime[12];
00175         char chksum[8];
00176         char linkflag;
00177         char linkname[NAMSIZ];
00178         char magic[8];
00179         char uname[TUNMLEN];
00180         char gname[TGNMLEN];
00181         char devmajor[8];
00182         char devminor[8];
00183     } header;
00184 };
00185 
00186 /* The magic field is filled with this if uname and gname are valid. */
00187 #define    TMAGIC        "ustar  "  /* 7 chars and a null */
00188 
00189 /*
00190  * file-function prototypes
00191  */
00192 static int is_tar(unsigned char *, int);
00193 static unsigned long signextend(struct magic *, unsigned long);
00194 static int getvalue(struct magic *, char **);
00195 static int hextoint(int);
00196 static char *getstr(char *, char *, int, int *);
00197 static int mget(union VALUETYPE *, unsigned char *, struct magic *, int);
00198 static int mcheck(union VALUETYPE *, struct magic *);
00199 static int mconvert(union VALUETYPE *, struct magic *);
00200 static long from_oct(int, char *);
00201 
00202 /*
00203  * includes for ASCII substring recognition formerly "names.h" in file
00204  * command
00205  *
00206  * Original notes: names and types used by ascmagic in file(1).
00207  * These tokens are
00208  * here because they can appear anywhere in the first HOWMANY bytes, while
00209  * tokens in /etc/magic must appear at fixed offsets into the file. Don't
00210  * make HOWMANY too high unless you have a very fast CPU.
00211  */
00212 
00213 /* these types are used calculate index to 'types': keep em in sync! */
00214 /* HTML inserted in first because this is a web server module now */
00215 /* ENG removed because stupid */
00216 #define L_HTML   0x001          /* HTML */
00217 #define L_C      0x002          /* first and foremost on UNIX */
00218 #define L_MAKE   0x004          /* Makefiles */
00219 #define L_PLI    0x008          /* PL/1 */
00220 #define L_MACH   0x010          /* some kinda assembler */
00221 #define L_PAS    0x020          /* Pascal */
00222 #define L_JAVA   0x040          /* Java source */
00223 #define L_CPP    0x080          /* C++ */
00224 #define L_MAIL   0x100          /* Electronic mail */
00225 #define L_NEWS   0x200          /* Usenet Netnews */
00226 #define L_DIFF   0x400          /* Output of diff */
00227 
00228 #define P_HTML   0          /* HTML */
00229 #define P_C      1          /* first and foremost on UNIX */
00230 #define P_MAKE   2          /* Makefiles */
00231 #define P_PLI    3          /* PL/1 */
00232 #define P_MACH   4          /* some kinda assembler */
00233 #define P_PAS    5          /* Pascal */
00234 #define P_JAVA   6          /* Java source */
00235 #define P_CPP    7          /* C++ */
00236 #define P_MAIL   8          /* Electronic mail */
00237 #define P_NEWS   9          /* Usenet Netnews */
00238 #define P_DIFF  10          /* Output of diff */
00239 
00240 typedef struct asc_type {
00241     const char *type;
00242     int  kwords;
00243     double  weight;
00244 } asc_type;
00245 
00246 static const asc_type types[] = {
00247     { "text/html",          19, 2 }, // 10 items but 10 different words only
00248     { "text/x-c",           9, 1.3 },
00249     { "text/x-makefile",    4, 1.9 },
00250     { "text/x-pli",         1, 3 },
00251     { "text/x-assembler",   6, 2.1 },
00252     { "text/x-pascal",      1, 1 },
00253     { "text/x-java",       14, 1 },
00254     { "text/x-c++",        14, 1 },
00255     { "message/rfc822",     4, 1.9 },
00256     { "message/news",       3, 2 },
00257         { "text/x-diff",        4, 2 }
00258 };
00259 
00260 #define NTYPES (sizeof(types)/sizeof(asc_type))
00261 
00262 static struct names {
00263     const char *name;
00264     short type;
00265 } const names[] = {
00266     {
00267         "<html", L_HTML
00268     },
00269     {
00270         "<HTML", L_HTML
00271     },
00272     {
00273         "<head", L_HTML
00274     },
00275     {
00276         "<HEAD", L_HTML
00277     },
00278     {
00279         "<body", L_HTML
00280     },
00281     {
00282         "<BODY", L_HTML
00283     },
00284     {
00285         "<title", L_HTML
00286     },
00287     {
00288         "<TITLE", L_HTML
00289     },
00290     {
00291         "<h1", L_HTML
00292     },
00293     {
00294         "<H1", L_HTML
00295     },
00296     {
00297         "<a", L_HTML
00298     },
00299     {
00300         "<A", L_HTML
00301     },
00302     {
00303         "<img", L_HTML
00304     },
00305     {
00306         "<IMG", L_HTML
00307     },
00308     {
00309         "<!--", L_HTML
00310     },
00311     {
00312         "<!doctype", L_HTML
00313     },
00314     {
00315         "<!DOCTYPE", L_HTML
00316     },
00317     {
00318         "<div", L_HTML
00319     },
00320     {
00321         "<DIV", L_HTML
00322     },
00323     {
00324         "<frame", L_HTML
00325     },
00326     {
00327         "<FRAME", L_HTML
00328     },
00329     {
00330         "<frameset", L_HTML
00331     },
00332     {
00333         "<FRAMESET", L_HTML
00334     },
00335         {
00336                 "<script", L_HTML
00337         },
00338         {
00339                 "<SCRIPT", L_HTML
00340         },
00341     {
00342         "/*", L_C|L_CPP|L_JAVA
00343     },                      /* must precede "The", "the", etc. */
00344     {
00345         "//", L_CPP|L_JAVA
00346     },                      /* must precede "The", "the", etc. */
00347     {
00348         "#include", L_C|L_CPP
00349     },
00350     {
00351         "char", L_C|L_CPP|L_JAVA
00352     },
00353     {
00354         "double", L_C|L_CPP|L_JAVA
00355     },
00356     {
00357         "extern", L_C|L_CPP
00358     },
00359     {
00360         "float", L_C|L_CPP|L_JAVA
00361     },
00362     {
00363         "real", L_C|L_CPP|L_JAVA
00364     },
00365     {
00366         "struct", L_C|L_CPP
00367     },
00368     {
00369         "union", L_C|L_CPP
00370     },
00371     {
00372         "implements", L_JAVA
00373     },
00374     {
00375         "super", L_JAVA
00376     },
00377     {
00378         "import", L_JAVA
00379     },
00380     {
00381         "class", L_CPP|L_JAVA
00382     },
00383     {
00384         "public", L_CPP|L_JAVA
00385     },
00386     {
00387         "private", L_CPP|L_JAVA
00388     },
00389     {
00390         "CFLAGS", L_MAKE
00391     },
00392     {
00393         "LDFLAGS", L_MAKE
00394     },
00395     {
00396         "all:", L_MAKE
00397     },
00398     {
00399         ".PRECIOUS", L_MAKE
00400     },
00401     /*
00402      * Too many files of text have these words in them.  Find another way
00403      * to recognize Fortrash.
00404      */
00405     {
00406         ".ascii", L_MACH
00407     },
00408     {
00409         ".asciiz", L_MACH
00410     },
00411     {
00412         ".byte", L_MACH
00413     },
00414     {
00415         ".even", L_MACH
00416     },
00417     {
00418         ".globl", L_MACH
00419     },
00420     {
00421         "clr", L_MACH
00422     },
00423     {
00424         "(input", L_PAS
00425     },
00426     {
00427         "dcl", L_PLI
00428     },
00429     {
00430         "Received:", L_MAIL
00431     },
00432     /* we now stop at '>' for tokens, so this one won't work {
00433         ">From", L_MAIL
00434         },*/
00435     {
00436         "Return-Path:", L_MAIL
00437     },
00438     {
00439         "Cc:", L_MAIL
00440     },
00441     {
00442         "Newsgroups:", L_NEWS
00443     },
00444     {
00445         "Path:", L_NEWS
00446     },
00447     {
00448         "Organization:", L_NEWS
00449     },
00450     {
00451         "---", L_DIFF
00452     },
00453     {
00454         "+++", L_DIFF
00455     },
00456     {
00457         "***", L_DIFF
00458     },
00459     {
00460         "@@", L_DIFF
00461     },
00462     {
00463         NULL, 0
00464     }
00465 };
00466 
00477 class KMimeMagicUtimeConf
00478 {
00479 public:
00480     KMimeMagicUtimeConf()
00481     {
00482         tmpDirs << QString::fromLatin1("/tmp"); // default value
00483 
00484         // The trick is that we also don't want the user to override globally set
00485         // directories. So we have to misuse KStandardDirs :}
00486         QStringList confDirs = KGlobal::dirs()->resourceDirs( "config" );
00487         if ( !confDirs.isEmpty() )
00488         {
00489             QString globalConf = confDirs.last() + "kmimemagicrc";
00490             if ( QFile::exists( globalConf ) )
00491             {
00492                 KSimpleConfig cfg( globalConf );
00493                 cfg.setGroup( "Settings" );
00494                 tmpDirs = cfg.readListEntry( "atimeDirs" );
00495             }
00496             if ( confDirs.count() > 1 )
00497             {
00498                 QString localConf = confDirs.first() + "kmimemagicrc";
00499                 if ( QFile::exists( localConf ) )
00500                 {
00501                     KSimpleConfig cfg( localConf );
00502                     cfg.setGroup( "Settings" );
00503                     tmpDirs += cfg.readListEntry( "atimeDirs" );
00504                 }
00505             }
00506             for ( QStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
00507             {
00508                 QString dir = *it;
00509                 if ( !dir.isEmpty() && dir[ dir.length()-1 ] != '/' )
00510                     (*it) += '/';
00511             }
00512         }
00513 #if 0
00514         // debug code
00515         for ( QStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
00516             kdDebug(7018) << " atimeDir: " << *it << endl;
00517 #endif
00518     }
00519 
00520     bool restoreAccessTime( const QString & file ) const
00521     {
00522         QString dir = file.left( file.findRev( '/' ) );
00523         bool res = tmpDirs.contains( dir );
00524         //kdDebug(7018) << "restoreAccessTime " << file << " dir=" << dir << " result=" << res << endl;
00525         return res;
00526     }
00527     QStringList tmpDirs;
00528 };
00529 
00530 /* current config */
00531 struct config_rec {
00532     struct magic *magic,    /* head of magic config list */
00533     *last;
00534         KMimeMagicUtimeConf * utimeConf;
00535 };
00536 
00537 #ifdef MIME_MAGIC_DEBUG_TABLE
00538 static void
00539 test_table()
00540 {
00541     struct magic *m;
00542     struct magic *prevm = NULL;
00543 
00544     kdDebug(7018) << "test_table : started" << endl;
00545     for (m = conf->magic; m; m = m->next) {
00546         if (isprint((((unsigned long) m) >> 24) & 255) &&
00547             isprint((((unsigned long) m) >> 16) & 255) &&
00548             isprint((((unsigned long) m) >> 8) & 255) &&
00549             isprint(((unsigned long) m) & 255)) {
00550             //debug("test_table: POINTER CLOBBERED! "
00551             //"m=\"%c%c%c%c\" line=%d",
00552                   (((unsigned long) m) >> 24) & 255,
00553                   (((unsigned long) m) >> 16) & 255,
00554                   (((unsigned long) m) >> 8) & 255,
00555                   ((unsigned long) m) & 255,
00556                   prevm ? prevm->lineno : -1);
00557             break;
00558         }
00559         prevm = m;
00560     }
00561 }
00562 #endif
00563 
00564 #define    EATAB {while (isascii((unsigned char) *l) && \
00565           isspace((unsigned char) *l))  ++l;}
00566 
00567 int KMimeMagic::parse_line(char *line, int *rule, int lineno)
00568 {
00569     int ws_offset;
00570 
00571     /* delete newline */
00572     if (line[0]) {
00573         line[strlen(line) - 1] = '\0';
00574     }
00575     /* skip leading whitespace */
00576     ws_offset = 0;
00577     while (line[ws_offset] && isspace(line[ws_offset])) {
00578         ws_offset++;
00579     }
00580 
00581     /* skip blank lines */
00582     if (line[ws_offset] == 0) {
00583         return 0;
00584     }
00585     /* comment, do not parse */
00586     if (line[ws_offset] == '#')
00587         return 0;
00588 
00589     /* if we get here, we're going to use it so count it */
00590     (*rule)++;
00591 
00592     /* parse it */
00593     return (parse(line + ws_offset, lineno) != 0);
00594 }
00595 
00596 /*
00597  * apprentice - load configuration from the magic file.
00598  */
00599 int KMimeMagic::apprentice( const QString& magicfile )
00600 {
00601     FILE *f;
00602     char line[BUFSIZ + 1];
00603     int errs = 0;
00604     int lineno;
00605     int rule = 0;
00606     QCString fname;
00607 
00608     if (magicfile.isEmpty())
00609         return -1;
00610     fname = QFile::encodeName(magicfile);
00611     f = fopen(fname, "r");
00612     if (f == NULL) {
00613         kdError(7018) << "can't read magic file " << fname.data() << ": " << strerror(errno) << endl;
00614         return -1;
00615     }
00616 
00617     /* parse it */
00618     for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; lineno++)
00619         if (parse_line(line, &rule, lineno))
00620             errs++;
00621 
00622     fclose(f);
00623 
00624 #ifdef DEBUG_APPRENTICE
00625     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;
00626     kdDebug(7018) << "apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
00627 #endif
00628 
00629 #ifdef MIME_MAGIC_DEBUG_TABLE
00630     test_table();
00631 #endif
00632 
00633     return (errs ? -1 : 0);
00634 }
00635 
00636 int KMimeMagic::buff_apprentice(char *buff)
00637 {
00638     char line[BUFSIZ + 2];
00639     int errs = 0;
00640     int lineno = 1;
00641     char *start = buff;
00642     char *end;
00643     int count = 0;
00644     int rule = 0;
00645     int len = strlen(buff) + 1;
00646 
00647     /* parse it */
00648     do {
00649         count = (len > BUFSIZ-1)?BUFSIZ-1:len;
00650         strncpy(line, start, count);
00651         line[count] = '\0';
00652         if ((end = strchr(line, '\n'))) {
00653             *(++end) = '\0';
00654             count = strlen(line);
00655         } else
00656           strcat(line, "\n");
00657         start += count;
00658         len -= count;
00659         if (parse_line(line, &rule, lineno))
00660             errs++;
00661         lineno++;
00662     } while (len > 0);
00663 
00664 #ifdef DEBUG_APPRENTICE
00665     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;
00666     kdDebug(7018) << "buff_apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
00667 #endif
00668 
00669 #ifdef MIME_MAGIC_DEBUG_TABLE
00670     test_table();
00671 #endif
00672 
00673     return (errs ? -1 : 0);
00674 }
00675 
00676 /*
00677  * extend the sign bit if the comparison is to be signed
00678  */
00679 static unsigned long
00680 signextend(struct magic *m, unsigned long v)
00681 {
00682     if (!(m->flag & UNSIGNED))
00683         switch (m->type) {
00684                 /*
00685                  * Do not remove the casts below.  They are vital.
00686                  * When later compared with the data, the sign
00687                  * extension must have happened.
00688                  */
00689             case BYTE:
00690                 v = (char) v;
00691                 break;
00692             case SHORT:
00693             case BESHORT:
00694             case LESHORT:
00695                 v = (short) v;
00696                 break;
00697             case DATE:
00698             case BEDATE:
00699             case LEDATE:
00700             case LONG:
00701             case BELONG:
00702             case LELONG:
00703                 v = (long) v;
00704                 break;
00705             case STRING:
00706                 break;
00707             default:
00708                 kdError(7018) << "" << "signextend" << ": can't happen: m->type=" << m->type << endl;
00709                 return ERROR;
00710         }
00711     return v;
00712 }
00713 
00714 /*
00715  * parse one line from magic file, put into magic[index++] if valid
00716  */
00717 int KMimeMagic::parse(char *l, int
00718 #ifdef DEBUG_LINENUMBERS
00719     lineno
00720 #endif
00721         )
00722 {
00723     int i = 0;
00724     struct magic *m;
00725     char *t,
00726     *s;
00727     /* allocate magic structure entry */
00728     if ((m = (struct magic *) calloc(1, sizeof(struct magic))) == NULL) {
00729         kdError(7018) << "parse: Out of memory." << endl;
00730         return -1;
00731     }
00732     /* append to linked list */
00733     m->next = NULL;
00734     if (!conf->magic || !conf->last) {
00735         conf->magic = conf->last = m;
00736     } else {
00737         conf->last->next = m;
00738         conf->last = m;
00739     }
00740 
00741     /* set values in magic structure */
00742     m->flag = 0;
00743     m->cont_level = 0;
00744 #ifdef DEBUG_LINENUMBERS
00745     m->lineno = lineno;
00746 #endif
00747 
00748     while (*l == '>') {
00749         ++l;            /* step over */
00750         m->cont_level++;
00751     }
00752 
00753     if (m->cont_level != 0 && *l == '(') {
00754         ++l;            /* step over */
00755         m->flag |= INDIR;
00756     }
00757     /* get offset, then skip over it */
00758     m->offset = (int) strtol(l, &t, 0);
00759     if (l == t) {
00760             kdError(7018) << "parse: offset " << l << " invalid" << endl;
00761     }
00762     l = t;
00763 
00764     if (m->flag & INDIR) {
00765         m->in.type = LONG;
00766         m->in.offset = 0;
00767         /*
00768          * read [.lbs][+-]nnnnn)
00769          */
00770         if (*l == '.') {
00771             switch (*++l) {
00772                 case 'l':
00773                     m->in.type = LONG;
00774                     break;
00775                 case 's':
00776                     m->in.type = SHORT;
00777                     break;
00778                 case 'b':
00779                     m->in.type = BYTE;
00780                     break;
00781                 default:
00782                     kdError(7018) << "parse: indirect offset type " << *l << " invalid" << endl;
00783                     break;
00784             }
00785             l++;
00786         }
00787         s = l;
00788         if (*l == '+' || *l == '-')
00789             l++;
00790         if (isdigit((unsigned char) *l)) {
00791             m->in.offset = strtol(l, &t, 0);
00792             if (*s == '-')
00793                 m->in.offset = -m->in.offset;
00794         } else
00795             t = l;
00796         if (*t++ != ')') {
00797             kdError(7018) << "parse: missing ')' in indirect offset" << endl;
00798         }
00799         l = t;
00800     }
00801     while (isascii((unsigned char) *l) && isdigit((unsigned char) *l))
00802         ++l;
00803     EATAB;
00804 
00805 #define NBYTE       4
00806 #define NSHORT      5
00807 #define NLONG       4
00808 #define NSTRING     6
00809 #define NDATE       4
00810 #define NBESHORT    7
00811 #define NBELONG     6
00812 #define NBEDATE     6
00813 #define NLESHORT    7
00814 #define NLELONG     6
00815 #define NLEDATE     6
00816 
00817     if (*l == 'u') {
00818         ++l;
00819         m->flag |= UNSIGNED;
00820     }
00821     /* get type, skip it */
00822     if (strncmp(l, "byte", NBYTE) == 0) {
00823         m->type = BYTE;
00824         l += NBYTE;
00825     } else if (strncmp(l, "short", NSHORT) == 0) {
00826         m->type = SHORT;
00827         l += NSHORT;
00828     } else if (strncmp(l, "long", NLONG) == 0) {
00829         m->type = LONG;
00830         l += NLONG;
00831     } else if (strncmp(l, "string", NSTRING) == 0) {
00832         m->type = STRING;
00833         l += NSTRING;
00834     } else if (strncmp(l, "date", NDATE) == 0) {
00835         m->type = DATE;
00836         l += NDATE;
00837     } else if (strncmp(l, "beshort", NBESHORT) == 0) {
00838         m->type = BESHORT;
00839         l += NBESHORT;
00840     } else if (strncmp(l, "belong", NBELONG) == 0) {
00841         m->type = BELONG;
00842         l += NBELONG;
00843     } else if (strncmp(l, "bedate", NBEDATE) == 0) {
00844         m->type = BEDATE;
00845         l += NBEDATE;
00846     } else if (strncmp(l, "leshort", NLESHORT) == 0) {
00847         m->type = LESHORT;
00848         l += NLESHORT;
00849     } else if (strncmp(l, "lelong", NLELONG) == 0) {
00850         m->type = LELONG;
00851         l += NLELONG;
00852     } else if (strncmp(l, "ledate", NLEDATE) == 0) {
00853         m->type = LEDATE;
00854         l += NLEDATE;
00855     } else {
00856         kdError(7018) << "parse: type " << l << " invalid" << endl;
00857         return -1;
00858     }
00859     /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
00860     if (*l == '&') {
00861         ++l;
00862         m->mask = signextend(m, strtol(l, &l, 0));
00863     } else
00864         m->mask = (unsigned long) ~0L;
00865     EATAB;
00866 
00867     switch (*l) {
00868         case '>':
00869         case '<':
00870             /* Old-style anding: "0 byte &0x80 dynamically linked" */
00871         case '&':
00872         case '^':
00873         case '=':
00874             m->reln = *l;
00875             ++l;
00876             break;
00877         case '!':
00878             if (m->type != STRING) {
00879                 m->reln = *l;
00880                 ++l;
00881                 break;
00882             }
00883             /* FALL THROUGH */
00884         default:
00885             if (*l == 'x' && isascii((unsigned char) l[1]) &&
00886                 isspace((unsigned char) l[1])) {
00887                 m->reln = *l;
00888                 ++l;
00889                 goto GetDesc;   /* Bill The Cat */
00890             }
00891             m->reln = '=';
00892             break;
00893     }
00894     EATAB;
00895 
00896     if (getvalue(m, &l))
00897         return -1;
00898     /*
00899      * now get last part - the description
00900      */
00901       GetDesc:
00902     EATAB;
00903     if (l[0] == '\b') {
00904         ++l;
00905         m->nospflag = 1;
00906     } else if ((l[0] == '\\') && (l[1] == 'b')) {
00907         ++l;
00908         ++l;
00909         m->nospflag = 1;
00910     } else
00911         m->nospflag = 0;
00912         // Copy description - until EOL or '#' (for comments)
00913         while (*l != '\0' && *l != '#' && i < MAXDESC-1)
00914             m->desc[i++] = *l++;
00915         m->desc[i] = '\0';
00916         // Remove trailing spaces
00917         while (--i>0 && isspace( m->desc[i] ))
00918             m->desc[i] = '\0';
00919 
00920         // old code
00921     //while ((m->desc[i++] = *l++) != '\0' && i < MAXDESC) /* NULLBODY */ ;
00922 
00923 #ifdef DEBUG_APPRENTICE
00924     kdDebug(7018) << "parse: line=" << lineno << " m=" << m << " next=" << m->next << " cont=" << m->cont_level << " desc=" << (m->desc ? m->desc : "NULL") << endl;
00925 #endif
00926     return 0;
00927 }
00928 
00929 /*
00930  * Read a numeric value from a pointer, into the value union of a magic
00931  * pointer, according to the magic type.  Update the string pointer to point
00932  * just after the number read.  Return 0 for success, non-zero for failure.
00933  */
00934 static int
00935 getvalue(struct magic *m, char **p)
00936 {
00937     int slen;
00938 
00939     if (m->type == STRING) {
00940         *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
00941         m->vallen = slen;
00942     } else if (m->reln != 'x')
00943         m->value.l = signextend(m, strtol(*p, p, 0));
00944     return 0;
00945 }
00946 
00947 /*
00948  * Convert a string containing C character escapes.  Stop at an unescaped
00949  * space or tab. Copy the converted version to "p", returning its length in
00950  * *slen. Return updated scan pointer as function result.
00951  */
00952 static char *
00953 getstr(register char *s, register char *p, int plen, int *slen)
00954 {
00955     char *origs = s,
00956     *origp = p;
00957     char *pmax = p + plen - 1;
00958     register int c;
00959     register int val;
00960 
00961     while ((c = *s++) != '\0') {
00962         if (isspace((unsigned char) c))
00963             break;
00964         if (p >= pmax) {
00965             kdError(7018) << "String too long: " << origs << endl;
00966             break;
00967         }
00968         if (c == '\\') {
00969             switch (c = *s++) {
00970 
00971                 case '\0':
00972                     goto out;
00973 
00974                 default:
00975                     *p++ = (char) c;
00976                     break;
00977 
00978                 case 'n':
00979                     *p++ = '\n';
00980                     break;
00981 
00982                 case 'r':
00983                     *p++ = '\r';
00984                     break;
00985 
00986                 case 'b':
00987                     *p++ = '\b';
00988                     break;
00989 
00990                 case 't':
00991                     *p++ = '\t';
00992                     break;
00993 
00994                 case 'f':
00995                     *p++ = '\f';
00996                     break;
00997 
00998                 case 'v':
00999                     *p++ = '\v';
01000                     break;
01001 
01002                     /* \ and up to 3 octal digits */
01003                 case '0':
01004                 case '1':
01005                 case '2':
01006                 case '3':
01007                 case '4':
01008                 case '5':
01009                 case '6':
01010                 case '7':
01011                     val = c - '0';
01012                     c = *s++;   /* try for 2 */
01013                     if (c >= '0' && c <= '7') {
01014                         val = (val << 3) | (c - '0');
01015                         c = *s++;   /* try for 3 */
01016                         if (c >= '0' && c <= '7')
01017                             val = (val << 3) | (c - '0');
01018                         else
01019                             --s;
01020                     } else
01021                         --s;
01022                     *p++ = (char) val;
01023                     break;
01024 
01025                     /* \x and up to 3 hex digits */
01026                 case 'x':
01027                     val = 'x';  /* Default if no digits */
01028                     c = hextoint(*s++); /* Get next char */
01029                     if (c >= 0) {
01030                         val = c;
01031                         c = hextoint(*s++);
01032                         if (c >= 0) {
01033                             val = (val << 4) + c;
01034                             c = hextoint(*s++);
01035                             if (c >= 0) {
01036                                 val = (val << 4) + c;
01037                             } else
01038                                 --s;
01039                         } else
01040                             --s;
01041                     } else
01042                         --s;
01043                     *p++ = (char) val;
01044                     break;
01045             }
01046         } else
01047             *p++ = (char) c;
01048     }
01049       out:
01050     *p = '\0';
01051     *slen = p - origp;
01052     return s;
01053 }
01054 
01055 
01056 /* Single hex char to int; -1 if not a hex char. */
01057 static int
01058 hextoint(int c)
01059 {
01060     if (!isascii((unsigned char) c))
01061         return -1;
01062     if (isdigit((unsigned char) c))
01063         return c - '0';
01064     if ((c >= 'a') && (c <= 'f'))
01065         return c + 10 - 'a';
01066     if ((c >= 'A') && (c <= 'F'))
01067         return c + 10 - 'A';
01068     return -1;
01069 }
01070 
01071 /*
01072  * Convert the byte order of the data we are looking at
01073  */
01074 static int
01075 mconvert(union VALUETYPE *p, struct magic *m)
01076 {
01077     switch (m->type) {
01078         case BYTE:
01079             return 1;
01080         case STRING:
01081             /* Null terminate */
01082             p->s[sizeof(p->s) - 1] = '\0';
01083             return 1;
01084 #ifndef WORDS_BIGENDIAN
01085         case SHORT:
01086 #endif
01087         case BESHORT:
01088             p->h = (short) ((p->hs[0] << 8) | (p->hs[1]));
01089             return 1;
01090 #ifndef WORDS_BIGENDIAN
01091         case LONG:
01092         case DATE:
01093 #endif
01094         case BELONG:
01095         case BEDATE:
01096             p->l = (long)
01097                 ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3]));
01098             return 1;
01099 #ifdef WORDS_BIGENDIAN
01100         case SHORT:
01101 #endif
01102         case LESHORT:
01103             p->h = (short) ((p->hs[1] << 8) | (p->hs[0]));
01104             return 1;
01105 #ifdef WORDS_BIGENDIAN
01106         case LONG:
01107         case DATE:
01108 #endif
01109         case LELONG:
01110         case LEDATE:
01111             p->l = (long)
01112                 ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0]));
01113             return 1;
01114         default:
01115             kdError(7018) << "mconvert: invalid type " << m->type << endl;
01116             return 0;
01117     }
01118 }
01119 
01120 
01121 static int
01122 mget(union VALUETYPE *p, unsigned char *s, struct magic *m,
01123      int nbytes)
01124 {
01125     long offset = m->offset;
01126 // The file length might be < sizeof(union VALUETYPE) (David)
01127 // -> pad with zeros (the 'file' command does it this way)
01128 // Thanks to Stan Covington <stan@calderasystems.com> for detailed report
01129     if (offset + (int)sizeof(union VALUETYPE) > nbytes)
01130     {
01131       int have = nbytes - offset;
01132       memset(p, 0, sizeof(union VALUETYPE));
01133       if (have > 0)
01134         memcpy(p, s + offset, have);
01135     } else
01136       memcpy(p, s + offset, sizeof(union VALUETYPE));
01137 
01138     if (!mconvert(p, m))
01139         return 0;
01140 
01141     if (m->flag & INDIR) {
01142 
01143         switch (m->in.type) {
01144             case BYTE:
01145                 offset = p->b + m->in.offset;
01146                 break;
01147             case SHORT:
01148                 offset = p->h + m->in.offset;
01149                 break;
01150             case LONG:
01151                 offset = p->l + m->in.offset;
01152                 break;
01153         }
01154 
01155         if (offset + (int)sizeof(union VALUETYPE) > nbytes)
01156              return 0;
01157 
01158         memcpy(p, s + offset, sizeof(union VALUETYPE));
01159 
01160         if (!mconvert(p, m))
01161             return 0;
01162     }
01163     return 1;
01164 }
01165 
01166 static int
01167 mcheck(union VALUETYPE *p, struct magic *m)
01168 {
01169     register unsigned long l = m->value.l;
01170     register unsigned long v;
01171     int matched;
01172 
01173     if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) {
01174         kdError(7018) << "BOINK" << endl;
01175         return 1;
01176     }
01177     switch (m->type) {
01178         case BYTE:
01179             v = p->b;
01180             break;
01181 
01182         case SHORT:
01183         case BESHORT:
01184         case LESHORT:
01185             v = p->h;
01186             break;
01187 
01188         case LONG:
01189         case BELONG:
01190         case LELONG:
01191         case DATE:
01192         case BEDATE:
01193         case LEDATE:
01194             v = p->l;
01195             break;
01196 
01197         case STRING:
01198             l = 0;
01199             /*
01200              * What we want here is: v = strncmp(m->value.s, p->s,
01201              * m->vallen); but ignoring any nulls.  bcmp doesn't give
01202              * -/+/0 and isn't universally available anyway.
01203              */
01204             v = 0;
01205             {
01206                 register unsigned char *a = (unsigned char *) m->value.s;
01207                 register unsigned char *b = (unsigned char *) p->s;
01208                 register int len = m->vallen;
01209                 Q_ASSERT(len);
01210 
01211                 while (--len >= 0)
01212                     if ((v = *b++ - *a++) != 0)
01213                         break;
01214             }
01215             break;
01216         default:
01217             kdError(7018) << "mcheck: invalid type " << m->type << endl;
01218             return 0;   /* NOTREACHED */
01219     }
01220 #if 0
01221     debug("Before signextend %08x", v);
01222 #endif
01223     v = signextend(m, v) & m->mask;
01224 #if 0
01225     debug("After signextend %08x", v);
01226 #endif
01227 
01228     switch (m->reln) {
01229         case 'x':
01230             matched = 1;
01231             break;
01232 
01233         case '!':
01234             matched = v != l;
01235             break;
01236 
01237         case '=':
01238             matched = v == l;
01239             break;
01240 
01241         case '>':
01242             if (m->flag & UNSIGNED)
01243                 matched = v > l;
01244             else
01245                 matched = (long) v > (long) l;
01246             break;
01247 
01248         case '<':
01249             if (m->flag & UNSIGNED)
01250                 matched = v < l;
01251             else
01252                 matched = (long) v < (long) l;
01253             break;
01254 
01255         case '&':
01256             matched = (v & l) == l;
01257             break;
01258 
01259         case '^':
01260             matched = (v & l) != l;
01261             break;
01262 
01263         default:
01264             matched = 0;
01265             kdError(7018) << "mcheck: can't happen: invalid relation " << m->reln << "." << endl;
01266             break;  /* NOTREACHED */
01267     }
01268 
01269     return matched;
01270 }
01271 
01272 #if 0
01273 
01274 /* states for the state-machine algorithm in finishResult() */
01275 typedef enum {
01276     rsl_leading_space, rsl_type, rsl_subtype, rsl_separator, rsl_encoding
01277 } rsl_states;
01278 
01279 /* process resultBuf and set the MIME info in magicResult */
01280 int
01281 KMimeMagic::finishResult()
01282 {
01283     int cur_pos,            /* current position within result */
01284      type_pos,              /* content type starting point: position */
01285      type_len,              /* content type length */
01286      encoding_pos,          /* content encoding starting point: position */
01287      encoding_len;          /* content encoding length */
01288 
01289     int state;
01290     /* start searching for the type and encoding */
01291     state = rsl_leading_space;
01292     type_pos = type_len = 0;
01293     encoding_pos = encoding_len = 0;
01294         //kdDebug(7018) << "KMimeMagic::finishResult " << resultBuf << endl;
01295     /* loop through the characters in the result */
01296     for (cur_pos = 0; cur_pos < (int)resultBuf.length(); cur_pos++) {
01297         if (resultBuf[cur_pos].isSpace()) {
01298             /* process whitespace actions for each state */
01299             if (state == rsl_leading_space) {
01300                 /* eat whitespace in this state */
01301                 continue;
01302             } else if (state == rsl_type) {
01303                 /* whitespace: type has no slash! */
01304                 return DECLINED;
01305             } else if (state == rsl_subtype) {
01306                 /* whitespace: end of MIME type */
01307                 state++;
01308                 continue;
01309             } else if (state == rsl_separator) {
01310                 /* eat whitespace in this state */
01311                 continue;
01312             } else if (state == rsl_encoding) {
01313                 /* whitespace: end of MIME encoding */
01314                 /* we're done */
01315                 break;
01316             } else {
01317                 /* should not be possible */
01318                 /* abandon malfunctioning module */
01319                 kdError(7018) << "KMimeMagic::finishResult: bad state " << state << " (ws)" << endl;
01320                 return DECLINED;
01321             }
01322             /* NOTREACHED */
01323         } else if (state == rsl_type &&
01324                resultBuf.at(cur_pos) == '/') {
01325             /* copy the char and go to rsl_subtype state */
01326             type_len++;
01327             state++;
01328         } else {
01329             /* process non-space actions for each state */
01330             if (state == rsl_leading_space) {
01331                 /* non-space: begin MIME type */
01332                 state++;
01333                 type_pos = cur_pos;
01334                 type_len = 1;
01335                 continue;
01336             } else if (state == rsl_type ||
01337                    state == rsl_subtype) {
01338                 /* non-space: adds to type */
01339                 type_len++;
01340                 continue;
01341             } else if (state == rsl_separator) {
01342                 /* non-space: begin MIME encoding */
01343                 state++;
01344                 encoding_pos = cur_pos;
01345                 encoding_len = 1;
01346                 continue;
01347             } else if (state == rsl_encoding) {
01348                 /* non-space: adds to encoding */
01349                 encoding_len++;
01350                 continue;
01351             } else {
01352                 /* should not be possible */
01353                 /* abandon malfunctioning module */
01354                 kdError(7018) << " KMimeMagic::finishResult: bad state " << state << " (ns)" << endl;
01355                 return DECLINED;
01356             }
01357             /* NOTREACHED */
01358         }
01359         /* NOTREACHED */
01360     }
01361 
01362     /* if we ended prior to state rsl_subtype, we had incomplete info */
01363     if (state != rsl_subtype && state != rsl_separator &&
01364         state != rsl_encoding) {
01365         /* defer to other modules */
01366         return DECLINED;
01367     }
01368     /* save the info in the request record */
01369     if (state == rsl_subtype || state == rsl_encoding ||
01370         state == rsl_encoding || state == rsl_separator) {
01371         magicResult->setMimeType(resultBuf.mid(type_pos, type_len).ascii());
01372     }
01373     if (state == rsl_encoding)
01374         magicResult->setEncoding(resultBuf.mid(encoding_pos,
01375                                encoding_len).ascii());
01376     /* detect memory allocation errors */
01377     if (!magicResult->mimeType() ||
01378         (state == rsl_encoding && !magicResult->encoding())) {
01379         return -1;
01380     }
01381     /* success! */
01382     return OK;
01383 }
01384 #endif
01385 
01386 /*
01387  * magic_process - process input file fn. Opens the file and reads a
01388  * fixed-size buffer to begin processing the contents.
01389  */
01390 void
01391 KMimeMagic::process(const QString & fn)
01392 {
01393     int fd = 0;
01394     unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
01395     struct stat sb;
01396     int nbytes = 0;         /* number of bytes read from a datafile */
01397         QCString fileName = QFile::encodeName( fn );
01398 
01399     /*
01400      * first try judging the file based on its filesystem status
01401      */
01402     if (fsmagic(fileName, &sb) != 0) {
01403         //resultBuf += "\n";
01404         return;
01405     }
01406     if ((fd = open(fileName, O_RDONLY)) < 0) {
01407         /* We can't open it, but we were able to stat it. */
01408         /*
01409          * if (sb.st_mode & 0002) addResult("writable, ");
01410          * if (sb.st_mode & 0111) addResult("executable, ");
01411          */
01412         //kdDebug(7018) << "can't read `" << fn << "' (" << strerror(errno) << ")." << endl;
01413         resultBuf = MIME_BINARY_UNREADABLE;
01414         return;
01415     }
01416     /*
01417      * try looking at the first HOWMANY bytes
01418      */
01419     if ((nbytes = read(fd, (char *) buf, HOWMANY)) == -1) {
01420         kdError(7018) << "" << fn << " read failed (" << strerror(errno) << ")." << endl;
01421         resultBuf = MIME_BINARY_UNREADABLE;
01422         return;
01423         /* NOTREACHED */
01424     }
01425     if (nbytes == 0) {
01426         resultBuf = MIME_BINARY_ZEROSIZE;
01427     } else {
01428         buf[nbytes++] = '\0';   /* null-terminate it */
01429         tryit(buf, nbytes);
01430     }
01431 
01432         if ( conf->utimeConf && conf->utimeConf->restoreAccessTime( fn ) )
01433         {
01434             /*
01435              * Try to restore access, modification times if read it.
01436              * This changes the "change" time (ctime), but we can't do anything
01437              * about that.
01438              */
01439             struct utimbuf utbuf;
01440             utbuf.actime = sb.st_atime;
01441             utbuf.modtime = sb.st_mtime;
01442             (void) utime(fileName, &utbuf);
01443         }
01444     (void) close(fd);
01445 }
01446 
01447 
01448 void
01449 KMimeMagic::tryit(unsigned char *buf, int nb)
01450 {
01451     /* try tests in /etc/magic (or surrogate magic file) */
01452     if (match(buf, nb))
01453         return;
01454 
01455     /* try known keywords, check for ascii-ness too. */
01456     if (ascmagic(buf, nb) == 1)
01457         return;
01458 
01459         /* see if it's plain text */
01460         if (textmagic(buf, nb))
01461                 return;
01462 
01463     /* abandon hope, all ye who remain here */
01464     resultBuf = MIME_BINARY_UNKNOWN;
01465     accuracy = 0;
01466 }
01467 
01468 int
01469 KMimeMagic::fsmagic(const char *fn, struct stat *sb)
01470 {
01471     int ret = 0;
01472 
01473     /*
01474      * Fstat is cheaper but fails for files you don't have read perms on.
01475      * On 4.2BSD and similar systems, use lstat() to identify symlinks.
01476      */
01477     ret = lstat(fn, sb);  /* don't merge into if; see "ret =" above */
01478 
01479     if (ret) {
01480         return 1;
01481     }
01482     /*
01483      * if (sb->st_mode & S_ISUID) resultBuf += "setuid ";
01484      * if (sb->st_mode & S_ISGID) resultBuf += "setgid ";
01485      * if (sb->st_mode & S_ISVTX) resultBuf += "sticky ";
01486      */
01487 
01488     switch (sb->st_mode & S_IFMT) {
01489         case S_IFDIR:
01490             resultBuf = MIME_INODE_DIR;
01491             return 1;
01492         case S_IFCHR:
01493             resultBuf = MIME_INODE_CDEV;
01494             return 1;
01495         case S_IFBLK:
01496             resultBuf = MIME_INODE_BDEV;
01497             return 1;
01498             /* TODO add code to handle V7 MUX and Blit MUX files */
01499 #ifdef    S_IFIFO
01500         case S_IFIFO:
01501             resultBuf = MIME_INODE_FIFO;;
01502             return 1;
01503 #endif
01504 #ifdef    S_IFLNK
01505         case S_IFLNK:
01506             {
01507                 char buf[BUFSIZ + BUFSIZ + 4];
01508                 register int nch;
01509                 struct stat tstatbuf;
01510 
01511                 if ((nch = readlink(fn, buf, BUFSIZ - 1)) <= 0) {
01512                     resultBuf = MIME_INODE_LINK;
01513                     //resultBuf += "\nunreadable";
01514                     return 1;
01515                 }
01516                 buf[nch] = '\0'; /* readlink(2) forgets this */
01517                 /* If broken symlink, say so and quit early. */
01518                 if (*buf == '/') {
01519                     if (stat(buf, &tstatbuf) < 0) {
01520                         resultBuf = MIME_INODE_LINK;
01521                         //resultBuf += "\nbroken";
01522                         return 1;
01523                     }
01524                 } else {
01525                     char *tmp;
01526                     char buf2[BUFSIZ + BUFSIZ + 4];
01527 
01528                     strncpy(buf2, fn, BUFSIZ);
01529                     buf2[BUFSIZ] = 0;
01530 
01531                     if ((tmp = strrchr(buf2, '/')) == NULL) {
01532                         tmp = buf; /* in current dir */
01533                     } else {
01534                         /* dir part plus (rel.) link */
01535                         *++tmp = '\0';
01536                         strcat(buf2, buf);
01537                         tmp = buf2;
01538                     }
01539                     if (stat(tmp, &tstatbuf) < 0) {
01540                         resultBuf = MIME_INODE_LINK;
01541                         //resultBuf += "\nbroken";
01542                         return 1;
01543                     } else
01544                         strcpy(buf, tmp);
01545                 }
01546                 if (followLinks)
01547                     process( QFile::decodeName( buf ) );
01548                 else
01549                     resultBuf = MIME_INODE_LINK;
01550                 return 1;
01551             }
01552             return 1;
01553 #endif
01554 #ifdef    S_IFSOCK
01555 #ifndef __COHERENT__
01556         case S_IFSOCK:
01557             resultBuf = MIME_INODE_SOCK;
01558             return 1;
01559 #endif
01560 #endif
01561         case S_IFREG:
01562             break;
01563         default:
01564             kdError(7018) << "KMimeMagic::fsmagic: invalid mode 0" << sb->st_mode << "." << endl;
01565             /* NOTREACHED */
01566     }
01567 
01568     /*
01569      * regular file, check next possibility
01570      */
01571     if (sb->st_size == 0) {
01572         resultBuf = MIME_BINARY_ZEROSIZE;
01573         return 1;
01574     }
01575     return 0;
01576 }
01577 
01578 /*
01579  * Go through the whole list, stopping if you find a match.  Process all the
01580  * continuations of that match before returning.
01581  *
01582  * We support multi-level continuations:
01583  *
01584  * At any time when processing a successful top-level match, there is a current
01585  * continuation level; it represents the level of the last successfully
01586  * matched continuation.
01587  *
01588  * Continuations above that level are skipped as, if we see one, it means that
01589  * the continuation that controls them - i.e, the lower-level continuation
01590  * preceding them - failed to match.
01591  *
01592  * Continuations below that level are processed as, if we see one, it means
01593  * we've finished processing or skipping higher-level continuations under the
01594  * control of a successful or unsuccessful lower-level continuation, and are
01595  * now seeing the next lower-level continuation and should process it.  The
01596  * current continuation level reverts to the level of the one we're seeing.
01597  *
01598  * Continuations at the current level are processed as, if we see one, there's
01599  * no lower-level continuation that may have failed.
01600  *
01601  * If a continuation matches, we bump the current continuation level so that
01602  * higher-level continuations are processed.
01603  */
01604 int
01605 KMimeMagic::match(unsigned char *s, int nbytes)
01606 {
01607     int cont_level = 0;
01608     union VALUETYPE p;
01609     struct magic *m;
01610 
01611 #ifdef DEBUG_MIMEMAGIC
01612     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;
01613     for (m = conf->magic; m; m = m->next) {
01614         if (isprint((((unsigned long) m) >> 24) & 255) &&
01615             isprint((((unsigned long) m) >> 16) & 255) &&
01616             isprint((((unsigned long) m) >> 8) & 255) &&
01617             isprint(((unsigned long) m) & 255)) {
01618             kdDebug(7018) << "match: POINTER CLOBBERED! " << endl;
01619             break;
01620         }
01621     }
01622 #endif
01623 
01624     for (m = conf->magic; m; m = m->next) {
01625 #ifdef DEBUG_MIMEMAGIC
01626         kdDebug(7018) << "match: line=" << m->lineno << " desc=" << m->desc << endl;
01627 #endif
01628         memset(&p, 0, sizeof(union VALUETYPE));
01629 
01630         /* check if main entry matches */
01631         if (!mget(&p, s, m, nbytes) ||
01632             !mcheck(&p, m)) {
01633             struct magic *m_cont;
01634 
01635             /*
01636              * main entry didn't match, flush its continuations
01637              */
01638             if (!m->next || (m->next->cont_level == 0)) {
01639                 continue;
01640             }
01641             m_cont = m->next;
01642             while (m_cont && (m_cont->cont_level != 0)) {
01643 #ifdef DEBUG_MIMEMAGIC
01644                 kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m_cont->cont_level << " mc=" << m_cont->lineno << " mc->next=" << m_cont << " " << endl;
01645 #endif
01646                 /*
01647                  * this trick allows us to keep *m in sync
01648                  * when the continue advances the pointer
01649                  */
01650                 m = m_cont;
01651                 m_cont = m_cont->next;
01652             }
01653             continue;
01654         }
01655         /* if we get here, the main entry rule was a match */
01656         /* this will be the last run through the loop */
01657 #ifdef DEBUG_MIMEMAGIC
01658         kdDebug(7018) << "match: rule matched, line=" << m->lineno << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
01659 #endif
01660 
01661         /* remember the match */
01662         resultBuf = m->desc;
01663 
01664         cont_level++;
01665         /*
01666          * while (m && m->next && m->next->cont_level != 0 && ( m =
01667          * m->next ))
01668          */
01669         m = m->next;
01670         while (m && (m->cont_level != 0)) {
01671 #ifdef DEBUG_MIMEMAGIC
01672                     kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m->cont_level << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
01673 #endif
01674                     if (cont_level >= m->cont_level) {
01675                 if (cont_level > m->cont_level) {
01676                     /*
01677                      * We're at the end of the level
01678                      * "cont_level" continuations.
01679                      */
01680                     cont_level = m->cont_level;
01681                 }
01682                 if (mget(&p, s, m, nbytes) &&
01683                     mcheck(&p, m)) {
01684                     /*
01685                      * This continuation matched. Print
01686                      * its message, with a blank before
01687                      * it if the previous item printed
01688                      * and this item isn't empty.
01689                      */
01690 #ifdef DEBUG_MIMEMAGIC
01691                                     kdDebug(7018) << "continuation matched" << endl;
01692 #endif
01693                                     resultBuf = m->desc;
01694                     cont_level++;
01695                 }
01696             }
01697             /* move to next continuation record */
01698             m = m->next;
01699         }
01700                 // KDE-specific: need an actual mimetype for a real match
01701                 // If we only matched a rule with continuations but no mimetype, it's not a match
01702                 if ( !resultBuf.isEmpty() )
01703                 {
01704 #ifdef DEBUG_MIMEMAGIC
01705                     kdDebug(7018) << "match: matched" << endl;
01706 #endif
01707                     return 1;       /* all through */
01708                 }
01709     }
01710 #ifdef DEBUG_MIMEMAGIC
01711     kdDebug(7018) << "match: failed" << endl;
01712 #endif
01713     return 0;               /* no match at all */
01714 }
01715 
01716 /* an optimization over plain strcmp() */
01717 #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
01718 
01719 int
01720 KMimeMagic::ascmagic(unsigned char *buf, int nbytes)
01721 {
01722     int i;
01723     double pct, maxpct, pctsum;
01724     double pcts[NTYPES];
01725     int mostaccurate, tokencount;
01726     int typeset, jonly, conly, jconly, cppcomm, ccomm;
01727     int has_escapes = 0;
01728     unsigned char *s;
01729     char nbuf[HOWMANY + 1]; /* one extra for terminating '\0' */
01730     char *token;
01731     register const struct names *p;
01732     int typecount[NTYPES];
01733 
01734     /* these are easy, do them first */
01735     accuracy = 70;
01736 
01737     /*
01738      * for troff, look for . + letter + letter or .\"; this must be done
01739      * to disambiguate tar archives' ./file and other trash from real
01740      * troff input.
01741      */
01742     if (*buf == '.') {
01743         unsigned char *tp = buf + 1;
01744 
01745         while (isascii(*tp) && isspace(*tp))
01746             ++tp;   /* skip leading whitespace */
01747         if ((isascii(*tp) && (isalnum(*tp) || *tp == '\\') &&
01748              isascii(*(tp + 1)) && (isalnum(*(tp + 1)) || *tp == '"'))) {
01749             resultBuf = MIME_APPL_TROFF;
01750             return 1;
01751         }
01752     }
01753     if ((*buf == 'c' || *buf == 'C') &&
01754         isascii(*(buf + 1)) && isspace(*(buf + 1))) {
01755         /* Fortran */
01756         resultBuf = MIME_TEXT_FORTRAN;
01757         return 1;
01758     }
01759     assert(nbytes-1 < HOWMANY + 1);
01760     /* look for tokens - this is expensive! */
01761     /* make a copy of the buffer here because strtok() will destroy it */
01762     s = (unsigned char *) memcpy(nbuf, buf, nbytes);
01763     s[nbytes-1] = '\0';
01764     has_escapes = (memchr(s, '\033', nbytes) != NULL);
01765 /*
01766  * Fritz:
01767  * Try a little harder on C/C++/Java.
01768  */
01769     memset(&typecount, 0, sizeof(typecount));
01770     typeset = 0;
01771     jonly = 0;
01772     conly = 0;
01773     jconly = 0;
01774     cppcomm = 0;
01775     ccomm = 0;
01776     tokencount = 0;
01777         bool foundClass = false; // mandatory for java
01778     // first collect all possible types and count matches
01779         // we stop at '>' too, because of "<title>blah</title>" on HTML pages
01780     while ((token = strtok((char *) s, " \t\n\r\f,;>")) != NULL) {
01781         s = NULL;       /* make strtok() keep on tokin' */
01782 #ifdef DEBUG_MIMEMAGIC
01783                 kdDebug(7018) << "KMimeMagic::ascmagic token=" << token << endl;
01784 #endif
01785         for (p = names; p->name ; p++) {
01786             if (STREQ(p->name, token)) {
01787 #ifdef DEBUG_MIMEMAGIC
01788                                 kdDebug(7018) << "KMimeMagic::ascmagic token matches ! name=" << p->name << " type=" << p->type << endl;
01789 #endif
01790                     tokencount++;
01791                 typeset |= p->type;
01792                 if (p->type == L_JAVA)
01793                     jonly++;
01794                 if ((p->type & (L_C|L_CPP|L_JAVA))
01795                     == (L_CPP|L_JAVA)) {
01796                     jconly++;
01797                                         if ( !foundClass && STREQ("class", token) )
01798                                             foundClass = true;
01799                                 }
01800                 if ((p->type & (L_C|L_CPP|L_JAVA))
01801                     == (L_C|L_CPP))
01802                     conly++;
01803                 if (STREQ(token, "//"))
01804                     cppcomm++;
01805                 if (STREQ(token, "/*"))
01806                     ccomm++;
01807                 for (i = 0; i < (int)NTYPES; i++)
01808                     if ((1 << i) & p->type)
01809                         typecount[i]++;
01810             }
01811         }
01812     }
01813 
01814     if (typeset & (L_C|L_CPP|L_JAVA)) {
01815         accuracy = 40;
01816             if (!(typeset & ~(L_C|L_CPP|L_JAVA))) {
01817 #ifdef DEBUG_MIMEMAGIC
01818                         kdDebug(7018) << "C/C++/Java: jonly=" << jonly << " conly=" << conly << " jconly=" << jconly << " ccomm=" << ccomm << endl;
01819 #endif
01820             if (jonly && conly)
01821                             // Take the biggest
01822                             if ( jonly > conly )
01823                                 conly = 0;
01824                             else
01825                                 jonly = 0;
01826             if (jonly > 1 && foundClass) {
01827                 // At least two java-only tokens have matched, including "class"
01828                 resultBuf = QString(types[P_JAVA].type);
01829                 return 1;
01830             }
01831             if (jconly > 1) {
01832                 // At least two non-C (only C++ or Java) token have matched.
01833                 if (typecount[P_JAVA] > typecount[P_CPP])
01834                   resultBuf = QString(types[P_JAVA].type);
01835                 else
01836                   resultBuf = QString(types[P_CPP].type);
01837                 return 1;
01838             }
01839             if (conly) {
01840                 // Either C or C++, rely on comments.
01841                 if (cppcomm)
01842                   resultBuf = QString(types[P_CPP].type);
01843                 else
01844                   resultBuf = QString(types[P_C].type);
01845                 return 1;
01846             }
01847             if (ccomm) {
01848                 resultBuf = QString(types[P_C].type);
01849                 return 1;
01850             }
01851           }
01852     }
01853 
01854     /* Neither C, C++ or Java (or all of them without able to distinguish):
01855      * Simply take the token-class with the highest
01856      * matchcount > 0
01857      */
01858     mostaccurate = -1;
01859     maxpct = pctsum = 0.0;
01860     for (i = 0; i < (int)NTYPES; i++) {
01861       if (typecount[i] > 1) { // one word is not enough, we need at least two
01862         pct = (double)typecount[i] / (double)types[i].kwords *
01863             (double)types[i].weight;
01864         pcts[i] = pct;
01865         pctsum += pct;
01866         if (pct > maxpct) {
01867             maxpct = pct;
01868             mostaccurate = i;
01869           }
01870 #ifdef DEBUG_MIMEMAGIC
01871           kdDebug(7018) << "" << types[i].type << " has " << typecount[i] << " hits, " << types[i].kwords << " kw, weight " << types[i].weight << ", " << pct << " -> max = " << maxpct << "\n" << endl;
01872 #endif
01873       }
01874     }
01875     if (mostaccurate >= 0) {
01876             if ( mostaccurate != P_JAVA || foundClass ) // 'class' mandatory for java
01877             {
01878         accuracy = (int)(pcts[mostaccurate] / pctsum * 60);
01879 #ifdef DEBUG_MIMEMAGIC
01880                 kdDebug(7018) << "mostaccurate=" << mostaccurate << " pcts=" << pcts[mostaccurate] << " pctsum=" << pctsum << " accuracy=" << accuracy << endl;
01881 #endif
01882         resultBuf = QString(types[mostaccurate].type);
01883         return 1;
01884             }
01885     }
01886 
01887     switch (is_tar(buf, nbytes)) {
01888         case 1:
01889             /* V7 tar archive */
01890             resultBuf = MIME_APPL_TAR;
01891             accuracy = 90;
01892             return 1;
01893         case 2:
01894             /* POSIX tar archive */
01895             resultBuf = MIME_APPL_TAR;
01896             accuracy = 90;
01897             return 1;
01898     }
01899 
01900     for (i = 0; i < nbytes; i++) {
01901         if (!isascii(*(buf + i)))
01902             return 0;   /* not all ascii */
01903     }
01904 
01905     /* all else fails, but it is ascii... */
01906     accuracy = 90;
01907     if (has_escapes) {
01908         /* text with escape sequences */
01909         /* we leave this open for further differentiation later */
01910         resultBuf = MIME_TEXT_UNKNOWN;
01911     } else {
01912         /* plain text */
01913         resultBuf = MIME_TEXT_PLAIN;
01914     }
01915     return 1;
01916 }
01917 
01918 /* Maximal length of a line we consider "reasonable". */
01919 #define TEXT_MAXLINELEN 300
01920 
01921 // This code is taken from the "file" command, where it is licensed
01922 // in the "beer-ware license" :-)
01923 // Original author: <joerg@FreeBSD.ORG>
01924 // Simplified by David Faure to avoid the static array char[256].
01925 int KMimeMagic::textmagic(unsigned char * buf, int nbytes)
01926 {
01927     int i;
01928     unsigned char *cp;
01929 
01930     nbytes--;
01931 
01932     /* First, look whether there are "unreasonable" characters. */
01933     for (i = 0, cp = buf; i < nbytes; i++, cp++)
01934         if ((*cp < 8) || (*cp>13 && *cp<32 && *cp!=27 ) || (*cp==0x7F))
01935             return 0;
01936 
01937     /* Now, look whether the file consists of lines of
01938      * "reasonable" length. */
01939 
01940     for (i = 0; i < nbytes;) {
01941         cp = (unsigned char *) memchr(buf, '\n', nbytes - i);
01942         if (cp == NULL) {
01943             /* Don't fail if we hit the end of buffer. */
01944             if (i + TEXT_MAXLINELEN >= nbytes)
01945                 break;
01946             else
01947                 return 0;
01948         }
01949         if (cp - buf > TEXT_MAXLINELEN)
01950             return 0;
01951         i += (cp - buf + 1);
01952         buf = cp + 1;
01953     }
01954     resultBuf = MIME_TEXT_PLAIN;
01955     return 1;
01956 }
01957 
01958 
01959 /*
01960  * is_tar() -- figure out whether file is a tar archive.
01961  *
01962  * Stolen (by author of file utility) from the public domain tar program: Public
01963  * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
01964  *
01965  * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7
01966  * 1997/06/24 00:41:02 ikluft Exp ikluft $
01967  *
01968  * Comments changed and some code/comments reformatted for file command by Ian
01969  * Darwin.
01970  */
01971 
01972 #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
01973 
01974 /*
01975  * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for
01976  * old UNIX tar file, 2 for Unix Std (POSIX) tar file.
01977  */
01978 
01979 static int
01980 is_tar(unsigned char *buf, int nbytes)
01981 {
01982     register union record *header = (union record *) buf;
01983     register int i;
01984     register long sum,
01985      recsum;
01986     register char *p;
01987 
01988     if (nbytes < (int)sizeof(union record))
01989          return 0;
01990 
01991     recsum = from_oct(8, header->header.chksum);
01992 
01993     sum = 0;
01994     p = header->charptr;
01995     for (i = sizeof(union record); --i >= 0;) {
01996         /*
01997          * We can't use unsigned char here because of old compilers,
01998          * e.g. V7.
01999          */
02000         sum += 0xFF & *p++;
02001     }
02002 
02003     /* Adjust checksum to count the "chksum" field as blanks. */
02004     for (i = sizeof(header->header.chksum); --i >= 0;)
02005         sum -= 0xFF & header->header.chksum[i];
02006     sum += ' ' * sizeof header->header.chksum;
02007 
02008     if (sum != recsum)
02009         return 0;       /* Not a tar archive */
02010 
02011     if (0 == strcmp(header->header.magic, TMAGIC))
02012         return 2;       /* Unix Standard tar archive */
02013 
02014     return 1;               /* Old fashioned tar archive */
02015 }
02016 
02017 
02018 /*
02019  * Quick and dirty octal conversion.
02020  *
02021  * Result is -1 if the field is invalid (all blank, or nonoctal).
02022  */
02023 static long
02024 from_oct(int digs, char *where)
02025 {
02026     register long value;
02027 
02028     while (isspace(*where)) {   /* Skip spaces */
02029         where++;
02030         if (--digs <= 0)
02031             return -1;  /* All blank field */
02032     }
02033     value = 0;
02034     while (digs > 0 && isodigit(*where)) {  /* Scan til nonoctal */
02035         value = (value << 3) | (*where++ - '0');
02036         --digs;
02037     }
02038 
02039     if (digs > 0 && *where && !isspace(*where))
02040         return -1;      /* Ended on non-space/nul */
02041 
02042     return value;
02043 }
02044 
02045 KMimeMagic::KMimeMagic()
02046 {
02047     // Magic file detection init
02048     QString mimefile = locate( "mime", "magic" );
02049     init( mimefile );
02050     // Add snippets from share/config/magic/*
02051     QStringList snippets = KGlobal::dirs()->findAllResources( "config", "magic/*.magic", true );
02052     for ( QStringList::Iterator it = snippets.begin() ; it != snippets.end() ; ++it )
02053         if ( !mergeConfig( *it ) )
02054             kdWarning() << k_funcinfo << "Failed to parse " << *it << endl;
02055 }
02056 
02057 KMimeMagic::KMimeMagic(const QString & _configfile)
02058 {
02059     init( _configfile );
02060 }
02061 
02062 void KMimeMagic::init( const QString& _configfile )
02063 {
02064     int result;
02065     conf = new config_rec;
02066 
02067     /* set up the magic list (empty) */
02068     conf->magic = conf->last = NULL;
02069     magicResult = NULL;
02070     followLinks = FALSE;
02071 
02072         conf->utimeConf = 0L; // created on demand
02073     /* on the first time through we read the magic file */
02074     result = apprentice(_configfile);
02075     if (result == -1)
02076         return;
02077 #ifdef MIME_MAGIC_DEBUG_TABLE
02078     test_table();
02079 #endif
02080 }
02081 
02082 /*
02083  * The destructor.
02084  * Free the magic-table and other resources.
02085  */
02086 KMimeMagic::~KMimeMagic()
02087 {
02088     if (conf) {
02089         struct magic *p = conf->magic;
02090         struct magic *q;
02091         while (p) {
02092             q = p;
02093             p = p->next;
02094             free(q);
02095         }
02096                 delete conf->utimeConf;
02097         delete conf;
02098     }
02099         delete magicResult;
02100 }
02101 
02102 bool
02103 KMimeMagic::mergeConfig(const QString & _configfile)
02104 {
02105     kdDebug(7018) << k_funcinfo << _configfile << endl;
02106     int result;
02107 
02108     if (_configfile.isEmpty())
02109         return false;
02110     result = apprentice(_configfile);
02111     if (result == -1) {
02112         return false;
02113     }
02114 #ifdef MIME_MAGIC_DEBUG_TABLE
02115     test_table();
02116 #endif
02117     return true;
02118 }
02119 
02120 bool
02121 KMimeMagic::mergeBufConfig(char * _configbuf)
02122 {
02123     int result;
02124 
02125     if (conf) {
02126         result = buff_apprentice(_configbuf);
02127         if (result == -1)
02128             return false;
02129 #ifdef MIME_MAGIC_DEBUG_TABLE
02130         test_table();
02131 #endif
02132         return true;
02133     }
02134     return false;
02135 }
02136 
02137 void
02138 KMimeMagic::setFollowLinks( bool _enable )
02139 {
02140     followLinks = _enable;
02141 }
02142 
02143 KMimeMagicResult *
02144 KMimeMagic::findBufferType(const QByteArray &array)
02145 {
02146     unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
02147 
02148     resultBuf = QString::null;
02149     if ( !magicResult )
02150       magicResult = new KMimeMagicResult();
02151     magicResult->setInvalid();
02152     accuracy = 100;
02153 
02154     int nbytes = array.size();
02155 
02156         if (nbytes > HOWMANY)
02157                 nbytes = HOWMANY;
02158         memcpy(buf, array.data(), nbytes);
02159         if (nbytes == 0) {
02160                 resultBuf = MIME_BINARY_ZEROSIZE;
02161         } else {
02162                 buf[nbytes++] = '\0';   /* null-terminate it */
02163                 tryit(buf, nbytes);
02164         }
02165         /* if we have any results, put them in the request structure */
02166         //finishResult();
02167     magicResult->setMimeType(resultBuf.stripWhiteSpace());
02168     magicResult->setAccuracy(accuracy);
02169         return magicResult;
02170 }
02171 
02172 static void
02173 refineResult(KMimeMagicResult *r, const QString & _filename)
02174 {
02175     QString tmp = r->mimeType();
02176     if (tmp.isEmpty())
02177         return;
02178     if ( tmp == "text/x-c"  ||
02179          tmp == "text/x-c++" )
02180     {
02181         if ( _filename.right(2) == ".h" )
02182             tmp += "hdr";
02183         else
02184             tmp += "src";
02185         r->setMimeType(tmp);
02186     }
02187 }
02188 
02189 KMimeMagicResult *
02190 KMimeMagic::findBufferFileType( const QByteArray &data,
02191                 const QString &fn)
02192 {
02193         KMimeMagicResult * r = findBufferType( data );
02194     refineResult(r, fn);
02195         return r;
02196 }
02197 
02198 /*
02199  * Find the content-type of the given file.
02200  */
02201 KMimeMagicResult* KMimeMagic::findFileType(const QString & fn)
02202 {
02203 #ifdef DEBUG_MIMEMAGIC
02204     kdDebug(7018) << "KMimeMagic::findFileType " << fn << endl;
02205 #endif
02206     resultBuf = QString::null;
02207 
02208         if ( !magicResult )
02209       magicResult = new KMimeMagicResult();
02210     magicResult->setInvalid();
02211     accuracy = 100;
02212 
02213         if ( !conf->utimeConf )
02214             conf->utimeConf = new KMimeMagicUtimeConf();
02215 
02216         /* process it based on the file contents */
02217         process( fn );
02218 
02219         /* if we have any results, put them in the request structure */
02220         //finishResult();
02221     magicResult->setMimeType(resultBuf.stripWhiteSpace());
02222     magicResult->setAccuracy(accuracy);
02223     refineResult(magicResult, fn);
02224         return magicResult;
02225 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Mar 16 18:44:14 2005 by doxygen 1.3.3 written by Dimitri van Heesch, © 1997-2001