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