Files
2016-05-04 00:26:27 -04:00

360 lines
8.5 KiB
C

#define _XOPEN_SOURCE 500
#include "gatling.h"
#include "mmap.h"
#include "str.h"
#include "byte.h"
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
struct arena {
struct arena* next;
unsigned long n;
void* ptrs[(4096/sizeof(void*))-2];
};
static void ainit(struct arena* a) {
a->n=0; a->next=0;
}
static void* amalloc(struct arena* a,size_t n) {
void* x;
while (a->n==(sizeof(a->ptrs)/sizeof(a->ptrs[0])) && a->next) a=a->next;
if (a->n==(sizeof(a->ptrs)/sizeof(a->ptrs[0]))) {
if (!(a->next=malloc(sizeof(struct arena)))) return 0;
ainit(a->next);
a=a->next;
}
if ((a->ptrs[a->n]=x=malloc(n))) ++a->n;
return x;
}
#if 0
static void afree(struct arena* a,void* x) {
for (; a; a=a->next) {
unsigned int i;
for (i=0; i<a->n; ++i)
if (a->ptrs[i]==x) {
free(x);
a->ptrs[i]=a->ptrs[a->n-1];
--a->n;
}
}
}
#endif
static void free_arena(struct arena* a) {
for (; a; a=a->next) {
unsigned int i;
for (i=0; i<a->n; ++i)
free(a->ptrs[i]);
}
}
struct pool {
struct arena a;
char* dat;
size_t rest;
};
static void pinit(struct pool* p) {
ainit(&p->a);
p->rest=0;
}
static void* pmalloc(struct pool* p,size_t n) {
void* x;
if (n>p->rest) {
if (n>4096) return amalloc(&p->a,n);
if (!(p->dat=amalloc(&p->a,p->rest=16*1024))) return 0;
}
x=p->dat;
p->dat+=n;
p->rest-=n;
return x;
}
static void pfree(struct pool* p) {
free_arena(&p->a);
}
static const char* nextline(const char* x,const char* end) {
for (; x<end; ++x)
if (*x=='\n') return x+1;
return x;
}
static const char* skipws(const char* x,const char* end) {
for (; x<end && (*x==' ' || *x=='\t'); ++x) ;
return x;
}
static const char* skipnonws(const char* x,const char* end) {
for (; x<end && *x!=' ' && *x!='\t' && *x!='\n'; ++x) ;
return x;
}
static char* memdup(struct pool* p,const char* x,const char* end) {
char* y=0;
if (x<end) {
y=pmalloc(p,end-x+1);
if (y) {
memcpy(y,x,end-x);
y[end-x]=0;
}
}
return y;
}
static struct mimeentry { const char* name, *type; }* mimetypes;
static struct pool* mimepool;
static void parse_mime_types(const char* filename) {
size_t maplen;
const char* map=mmap_read(filename,&maplen);
unsigned int allocated=0,used=0;
struct mimeentry* nmt=0;
if (map) {
const char* mimetype;
const char* extension;
const char* end=map+maplen;
const char* x,* l;
struct pool* p=malloc(sizeof(struct pool));
if (!p) goto kaputt;
pinit(p);
for (l=map; l<end; l=nextline(l,end)) {
x=skipws(l,end);
if (x>=end) break; if (*x=='#' || *x=='\n') continue;
mimetype=x;
x=skipnonws(x,end);
if (x>=end) break; if (*x=='#' || *x=='\n') continue;
mimetype=memdup(p,mimetype,x);
x=skipws(x,end);
if (x>=end) break; if (*x=='#' || *x=='\n') continue;
while (x<end) {
extension=x;
x=skipnonws(x,end);
if (x>extension) {
extension=memdup(p,extension,x);
if (!extension) continue;
// printf("%s -> %s\n",extension,mimetype);
if (used+1 > allocated) {
struct mimeentry* tmp;
allocated+=16;
tmp=realloc(nmt,allocated*sizeof(nmt[0]));
if (!tmp) {
free(nmt);
pfree(p);
free(p);
nmt=0;
goto kaputt;
}
nmt=tmp;
}
nmt[used].name=extension;
nmt[used].type=mimetype;
++used;
}
x=skipws(x,end);
if (x>=end || *x=='#' || *x=='\n') break;
}
if (x>=end) break;
}
if (mimepool) { pfree(mimepool); free(mimepool); }
mimepool=p;
kaputt:
mmap_unmap((char*)map,maplen);
}
if (nmt) {
nmt[used].name=nmt[used].type=0;
free(mimetypes);
mimetypes=nmt;
}
}
const char* find_mime_type(const char* extension,const char* filename,time_t now) {
static time_t last;
static struct stat lasts;
unsigned int i;
if (now>last+10) {
struct stat cur;
last=now;
if (stat(filename,&cur)==0 && cur.st_mtime != lasts.st_mtime) {
lasts=cur;
parse_mime_types(filename);
}
}
if (mimetypes)
for (i=0; mimetypes[i].name; ++i)
if (!strcmp(mimetypes[i].name,extension))
return mimetypes[i].type;
return 0;
}
char* magicelfvalue=(char*)0x23;
struct mimeentry mimetab[] = {
{ "html", "text/html" },
{ "ico", "image/x-icon" },
{ "txt", "text/plain" },
{ "css", "text/css" },
{ "dvi", "application/x-dvi" },
{ "ps", "application/postscript" },
{ "pdf", "application/pdf" },
{ "gif", "image/gif" },
{ "png", "image/png" },
{ "jpeg", "image/jpeg" },
{ "bild", "image/jpeg" },
{ "jpg", "image/jpeg" },
{ "svg", "image/svg+xml" },
{ "mpeg", "video/mpeg" },
{ "mpg", "video/mpeg" },
{ "avi", "video/x-msvideo" },
{ "mov", "video/quicktime" },
{ "qt", "video/quicktime" },
{ "mp3", "audio/mpeg" },
#ifndef SUPPORT_MIMEMAGIC
{ "ogg", "audio/ogg" },
{ "opus", "audio/ogg" },
#endif
{ "wav", "audio/x-wav" },
{ "pac", "application/x-ns-proxy-autoconfig" },
{ "sig", "application/pgp-signature" },
{ "torrent", "application/x-bittorrent" },
{ "rss", "application/rss+xml" },
{ "class", "application/octet-stream" },
{ "js", "application/x-javascript" },
{ "tar", "application/x-tar" },
{ "zip", "application/zip" },
{ "rar", "application/x-rar-compressed" },
{ "7z", "application/x-7z-compressed" },
{ "dtd", "text/xml" },
{ "xml", "text/xml" },
{ "xbm", "image/x-xbitmap" },
{ "xpm", "image/x-xpixmap" },
{ "xwd", "image/x-xwindowdump" },
{ "text", "text/plain" },
{ "txt", "text/plain" },
{ "m3u", "audio/x-mpegurl" },
{ "htm", "text/html" },
{ "swf", "application/x-shockwave-flash" },
{ "md5", "text/plain" },
{ "wmv", "video/x-ms-wmv" },
{ "mp4", "video/mp4" },
{ "m4a", "audio/mp4" },
{ "nzb", "application/x-nzb" },
{ "webm", "video/webm" },
#ifndef SUPPORT_MIMEMAGIC
{ "ogv", "video/ogg" },
#endif
{ 0 } };
const char* mimetype(const char* filename,int fd) {
int i,e=str_rchr(filename,'.');
if (filename[e]) {
++e;
if (mimetypesfilename) {
const char* x=find_mime_type(filename+e,mimetypesfilename,now.sec.x-4611686018427387914ULL);
if (x) return x;
}
for (i=0; mimetab[i].name; ++i)
if (str_equal(mimetab[i].name,filename+e))
return mimetab[i].type;
}
#ifdef SUPPORT_MIMEMAGIC
{
char buf[300];
int r;
r=pread(fd,buf,sizeof(buf),0);
if (r>=1 && buf[0]=='<') {
if (r>1 && buf[1]=='?') {
char* c;
if (r>=100)
for (c=buf+1; c<buf+r-5; ++c) {
if (*c=='<') {
if (c<buf+r-8 && byte_equal(c,8,"<rdf:RDF"))
return "application/rss+xml";
else if (c<buf+r-8 && byte_equal(c,5,"<rss "))
return "application/rss+xml";
else if (c<buf+r-8 && byte_equal(c,5,"<svg "))
return "image/svg+xml";
}
}
return "text/xml";
} else if (buf[1]=='!' || isalnum(buf[1]))
return "text/html";
} else if (r>=5 && byte_equal(buf,4,"GIF9"))
return "image/gif";
else if (r>=4 && byte_equal(buf,4,"\x89PNG"))
return "image/png";
else if (r>=10 && byte_equal(buf,2,"\xff\xd8"))
return "image/jpeg";
else if (r>=5 && byte_equal(buf,5,"%PDF-"))
return "application/pdf";
else if (r>=4 && (byte_equal(buf,3,"ID3") || byte_equal(buf,2,"\xff\xfb")))
return "audio/mpeg";
else if (r>=200 && byte_equal(buf,4,"OggS")) {
size_t i;
for (i=0; i<200-6; ++i)
if (buf[i]=='t' && byte_equal(buf+i+1,5,"heora"))
return "video/ogg";
return "audio/ogg";
} else if (r>=4 && byte_equal(buf,4,"RIFF")) {
if (r>=16 && byte_equal(buf+8,3,"AVI"))
return "video/x-msvideo";
else
return "audio/x-wav";
} else if (r==100 && byte_equal(buf+4,4,"moov"))
return "video/quicktime";
else if (r==100 && byte_equal(buf+4,8,"ftypqt ") && byte_equal(buf+0x24,4,"moov"))
return "video/quicktime";
else if (r==100 && byte_equal(buf+4,7,"ftypmp4"))
return "video/mp4";
else if (r>=4 && byte_equal(buf,4,"\x30\x26\xb2\x75"))
return "video/x-ms-asf";
else if (r>=4 && (byte_equal(buf,4,"\177ELF") || byte_equal(buf,2,"#!")))
return magicelfvalue;
}
#else
else
return "text/plain";
#endif
return "application/octet-stream";
}
#ifdef MIME_MAIN
#include <stdio.h>
int main() {
unsigned int i;
parse_mime_types("/etc/mime.types");
for (i=0; mimetypes[i].name; ++i)
printf("%s -> %s\n",mimetypes[i].name,mimetypes[i].type);
printf("\n\n ------\n\n");
parse_mime_types("/etc/mime.types");
for (i=0; mimetypes[i].name; ++i)
printf("%s -> %s\n",mimetypes[i].name,mimetypes[i].type);
free(mimetypes);
pfree(mimepool);
free(mimepool);
return 0;
}
#endif