/* * gnome-magic.c * * Written by: * James Youngman (jay@gnu.org) * * Adatped to the GNOME needs by: * Elliot Lee (sopwith@cuc.edu) */ /* needed for S_ISSOCK with 'gcc -ansi -pedantic' on GNU/Linux */ #ifndef _BSD_SOURCE # define _BSD_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include "gnome-mime.h" #include "gnome-magic.h" /****** misc lame parsing routines *******/ static guchar read_octal_str(char **pos) { guchar retval = 0; if(**pos >= '0' && **pos <= '7') { retval += **pos - '0'; } else return retval; retval *= 8; (*pos)++; if(**pos >= '0' && **pos <= '7') { retval += **pos - '0'; } else return retval/8; (*pos)++; retval *= 8; if(**pos >= '0' && **pos <= '7') { retval += **pos - '0'; } else return retval/8; (*pos)++; return retval; } static char read_hex_str(char **pos) { char retval = 0; if(**pos >= '0' && **pos <= '9') { retval = **pos - '0'; } else if(**pos >= 'a' && **pos <= 'f') { retval = **pos - 'a' + 10; } else if(**pos >= 'A' && **pos <= 'A') { retval = **pos - 'A' + 10; } else g_error("bad hex digit %c", **pos); (*pos)++; retval *= 16; if(**pos >= '0' && **pos <= '9') { retval += **pos - '0'; } else if(**pos >= 'a' && **pos <= 'f') { retval += **pos - 'a' + 10; } else if(**pos >= 'A' && **pos <= 'A') { retval += **pos - 'A' + 10; } else g_error("bad hex digit %c", **pos); (*pos)++; return retval; } static char * read_string_val(char *curpos, char *intobuf, guchar max_len, guchar *into_len) { char *intobufend = intobuf+max_len-1; char c; *into_len = 0; while (*curpos && !isspace(*curpos)) { c = *curpos; curpos++; switch(c) { case '\\': switch(*curpos) { case 'x': /* read hex value */ curpos++; c = read_hex_str(&curpos); break; case '0': case '1': /* read octal value */ c = read_octal_str(&curpos); break; case 'n': c = '\n'; curpos++; break; default: /* everything else is a literal */ c = *curpos; curpos++; break; } break; default: break; /* already setup c/moved curpos */ } if (intobufdata; if(nents) *nents = array->len; g_array_free(array, FALSE); return retval; } static void do_byteswap(guchar *outdata, const guchar *data, gulong datalen) { const guchar *source_ptr = data; guchar *dest_ptr = (guchar *)outdata + datalen - 1; while(dest_ptr >= outdata) *dest_ptr-- = *source_ptr++; } static gboolean gnome_magic_matches_p(FILE *fh, GnomeMagicEntry *ent) { char buf[sizeof(ent->test)]; gboolean retval = TRUE; fseek(fh, (long)ent->offset, SEEK_SET); fread(buf, ent->test_len, 1, fh); #if G_BYTE_ORDER == G_LITTLE_ENDIAN if(ent->type >= T_BESHORT && ent->type <= T_BEDATE) #else /* assume big endian */ if(ent->type >= T_LESHORT && ent->type <= T_LEDATE) #endif { /* endian-convert comparison */ char buf2[sizeof(ent->test)]; do_byteswap(buf2, buf, ent->test_len); retval &= !memcmp(ent->test, buf2, ent->test_len); } else /* direct compare */ retval &= !memcmp(ent->test, buf, ent->test_len); if(ent->mask != 0xFFFFFFFF) { switch(ent->test_len) { case 1: retval &= ((ent->mask & ent->test[0]) == ent->mask); break; case 2: retval &= ((ent->mask & (*(guint16 *)ent->test)) == ent->mask); break; case 4: retval &= ((ent->mask & (*(guint32 *)ent->test)) == ent->mask); break; } } return retval; } static GnomeMagicEntry * gnome_magic_db_load(void) { int fd; char *filename; GnomeMagicEntry *retval; struct stat sbuf; filename = gnome_config_file("mime-magic.dat"); if(!filename) return NULL; fd = open(filename, O_RDONLY); g_free(filename); if(fd < 0) return NULL; fstat(fd, &sbuf); retval = (GnomeMagicEntry*) mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); return retval; } /** * gnome_mime_type_from_magic: * @filename: an existing file name for which we want to guess the mime-type * * This routine uses a magic database as described in magic(5) that maps * files into their mime-type (so our modified magic database contains mime-types * rather than textual descriptions of the files). * * Returns a pointer to an internal copy of the mime-type for @filename. */ const char * gnome_mime_type_from_magic(const gchar *filename) { FILE *fh; static GnomeMagicEntry *ents = NULL; int i; struct stat sbuf; /* we really don't want to start reading from devices :) */ stat(filename, &sbuf); if(!S_ISREG(sbuf.st_mode)) { if(S_ISDIR(sbuf.st_mode)) return "special/directory"; else if(S_ISCHR(sbuf.st_mode)) return "special/device-char"; else if(S_ISBLK(sbuf.st_mode)) return "special/device-block"; else if(S_ISFIFO(sbuf.st_mode)) return "special/fifo"; else if(S_ISSOCK(sbuf.st_mode)) return "special/socket"; else return NULL; } fh = fopen(filename, "r"); if(!fh) return NULL; if(!ents) ents = gnome_magic_db_load(); if(!ents) { char *fn = gnome_config_file("mime-magic"); if(fn) ents = gnome_magic_parse(fn, NULL); g_free(fn); } if(!ents) { fclose(fh); return NULL; } for(i = 0; ents[i].type != T_END; i++) { if(gnome_magic_matches_p(fh, &ents[i])) break; } fclose(fh); return (ents[i].type == T_END)?NULL:ents[i].mimetype; }