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.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:13:41 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001