/* mkhtmlindex.c gcc -O2 -s -o mkhtmlindex mkhtmlindex.c -Wall -W diet -Os gcc -nostdinc -O2 -s -o mkhtmlindex mkhtmlindex.c -Wall -W large file support: gcc -O2 -DSUPPORT_64BIT_FILESIZE -s -o mkhtmlindex mkhtmlindex.c -Wall -W diet -Os gcc -nostdinc -O2 -DSUPPORT_64BIT_FILESIZE -s -o mkhtmlindex mkhtmlindex.c -Wall -W source: http://riemann.fmi.uni-sofia.bg/programs/ binary: http://riemann.fmi.uni-sofia.bg/utils/ */ #ifdef SUPPORT_64BIT_FILESIZE #define _FILE_OFFSET_BITS 64 typedef unsigned long long humank_t; #else typedef unsigned long humank_t; #endif #include #include #include #include #include #include #include #include #include #include #include /* gcc rename */ #define INC_HEADER ".#header" #define INC_TAILER ".#tailer" #define EXCLUDE ".#exclude" #define IDX_TMP ".#index.html" #define IDX_DENY ".#deny" #ifndef __dietlibc__ #ifndef O_DIRECTORY #define O_DIRECTORY 0200000 /* must be a directory */ #endif #endif typedef struct stralloc { char* s; unsigned long int len; unsigned long int a; } stralloc; unsigned int fmt_ulong(register char *s, register unsigned long u) { register unsigned int len; register unsigned long q; len = 1; q = u; while (q > 9) { ++len; q /= 10; } if (s) { s += len; do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ } return len; } unsigned int fmt_humank(char* dest, humank_t l) { char unit; int i; if (l<1000) return fmt_ulong(dest,l); #ifdef SUPPORT_64BIT_FILESIZE if (l>1024*1024*1024*1024ull) { l=(l+(1024*1024*1024*1024ull/20))/(1024*1024*1024*1024ull/10); unit='T'; } else #endif if (l>1024*1024*1024) { l=(l+(1024*1024*1024/20))/(1024*1024*1024/10); unit='G'; } else if (l>1024*1024) { l=(l+(1024*1024/20))/(1024*1024/10); unit='M'; } else { l=(l+(1024/20))/(1024/10); unit='k'; } i=fmt_ulong(dest,l/10); if (i<2) { if (dest) dest[i]='.'; i++; if (dest) dest[i]=(l%10)+'0'; i++; } if (dest) dest[i]=unit; i++; return i; } struct tm *gmtime_r(const time_t *t, struct tm *tm) { unsigned long day, mon, year, yday=0, tod; tod = (unsigned long)(*t) % 86400; day = (unsigned long)(*t) / 86400; tm->tm_wday = ((day+4) % 7); tm->tm_sec = tod%60; tod /= 60; tm->tm_min = tod%60; tm->tm_hour = tod/60; year = 4*day + 2; year /= 1461; day += 671; day %= 1461; /* day 0 is march 1, 1972 */ if (day < 306) yday = 1; if (day == 1460) { day = 365; yday = 59; } else { day %= 365; yday += (day + 59) % 365; } day *= 10; mon = (day + 5) / 306; day = day + 5 - 306 * mon; day /= 10; if (mon >= 10) mon -= 10; else mon += 2; /* day 0,1,30, mon 0..11, year 1970=0,1,2 */ tm->tm_year = year+70; tm->tm_mon = mon; tm->tm_mday = day+1; tm->tm_yday = yday; return tm; } static int fmt_hour(char *dest, unsigned int h, unsigned int m) { dest[0] = '0' + (h/10); dest[1] = '0' + (h%10); dest[2] = ':'; dest[3] = '0' + (m/10); dest[4] = '0' + (m%10); return 5; } typedef struct buffer { char *x; /* actual buffer space */ unsigned int p; /* current position */ unsigned int n; /* string postition */ unsigned int a; /* allocated buffer size */ int fd; int (*op)(int,char *,unsigned int); } buffer; extern int safe_write(int fd,char *s,unsigned int len); char out_space[4096]; buffer out_buf = { out_space,0,0,sizeof(out_space),1,safe_write }; buffer dir_buf = { out_space,0,0,sizeof(out_space),-1,safe_write }; static int buffer_flush(buffer* b) { register int p; if (!(p=b->p)) return 0; /* buffer already empty */ b->p=0; if (p != b->op(b->fd,b->x,p)) return -1; return 0; } int buffer_put(buffer* b,char* buf,unsigned int len) { if (len>b->a-b->p) { /* doesn't fit */ if (buffer_flush(b)==-1) return -1; if (len>b->a) { if (len != (unsigned int)b->op(b->fd,buf,len)) return -1; return 0; } } memcpy(b->x+b->p, buf, len); b->p+=len; return 0; } int out(char *s) {return buffer_put(&out_buf,s, strlen(s));} int outb(char* s,unsigned int len) {return buffer_put(&out_buf,s,len);} int fd=-1,fd_err=1; static char *months = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char *hex = "0123456789abcdef"; void err(char *s) {write(fd_err,"error: ",7); write(fd_err,s,strlen(s)); _exit(1);} int safe_write(int fd,char *s, unsigned int len) { int r; unsigned int u=len; while (u) { if ((r=write(fd,s,u))==0) break; if (r==-1) {if (errno==EINTR) continue; break;} s += r; u -= r; } if (u) err("write output\n"); return len-u; } void outh(char *s) { for (; *s; s++) switch (*s) { case '\"': out("""); break; case '&': out("&"); break; case '>': out(">"); break; case '<': out("<"); break; default: outb(s,1); } } void outu(char *s) { char c, x[3]; x[0] = '%'; for (; *s; s++) { if ((c=*s)!='"' && c!='%' && c>=' ' && c!='+') { outb(s,1); } else { x[1] = hex[c>>4]; x[2] = hex[c&15]; outb(x,3); } } } int include_fd(int fd) { char buf[1024]; int r; for (;;) { if ((r=read(fd,buf,sizeof buf))==0) break; if (r==-1) {if (errno==EINTR) continue; break;} outb(buf,r); } return r; } struct d { struct d *next; humank_t size; time_t time; char mode[4]; }; int byname(struct d** a,struct d** b) { return strcmp((char*)(*a)+sizeof(struct d), (char*)(*b)+sizeof(struct d)); } int bysize(struct d** a,struct d** b) { if ((*a)->size>(*b)->size) return 1; else if ((*a)->size<(*b)->size) return -1; else return byname(a,b); } int bytime(struct d** a,struct d** b) { int res=(*b)->time-(*a)->time; if (res) return res; return byname(a,b); } int (*compare)(const void *a,const void *b)=(int (*)(const void *,const void *))byname; void fmt_mode(char *x, mode_t mode) { memset(x,'-',4); if (S_ISLNK(mode)) x[0]='l'; else if (S_ISDIR(mode)) x[0]='d'; if ((mode&0444) == 0444) x[1]='r'; if (mode&2) x[2]='w'; if (mode&1) x[3]='x'; } mode_t do_chmod(char *s, mode_t mode) { mode_t m=0644; if (S_ISDIR(mode)) m = 0755; else { char *x = strrchr(s, '.'); if (x && !strcmp(x,".cgi")) m = 0711; } if ((mode & 07777) != m) { chmod(s, m); mode = (mode & ~07777) | m; } return mode; } void print_link(char *s) { char link[512]; int r=readlink(s,link,sizeof(link)-1); outh(" -> "); if (r > 0) { link[r] = 0; outh(link); } } struct dirent* read_dir(buffer *d) { if (d->p >= d->n || (d->p += ((struct dirent*)(d->x+d->p))->d_reclen) >= d->n) { #ifdef __dietlibc__ int res = getdents(d->fd, (struct dirent *)d->x, d->a-1); #else off_t x; int res = getdirentries(d->fd, d->x, d->a-1, &x); #endif if (res<=0) return 0; d->n=res; d->p=0; } return (struct dirent*)(d->x+d->p); } struct d *do_qsort(struct d *root) { unsigned int k; struct d **x, **map, *tmp; if (!root) return root; for (k=0,tmp=root; tmp; tmp=tmp->next) k++; if (!(map = alloca((k+1)*sizeof(struct d*)))) err("alloca"); for (x=map,tmp=root ;; tmp=tmp->next) { *x++ = tmp; if (!tmp) break; } qsort(map,k,sizeof(struct d*),compare); for (x=map; *x;) { tmp = *x++; tmp->next = *x; } return map[0]; } static stralloc mem_block; static void *alloc_a(stralloc *sa, unsigned long len) { len = (len + 3) & (~3); if (!sa->s || sa->len < len) return 0; sa->len -= len; return (void *)sa->s + sa->len; } int main(int argc, char **argv) { struct stat st; int ifd,i=0; struct tm *tm, tm_; time_t now; int flagdir; int flaglink; int flagmode=0; int flagdirent=0; int flagsort = 1; char x[16],*p; char *title=0; char **ee, **exclude=argv; int excl_len=0; struct dirent *de; struct d *tmp, *root_last=0, *root=0; for (optind=1; argv[optind] && argv[optind][0]=='-'; optind++) { switch (argv[optind][1]) { case 'm': flagmode=1; break; case 'o': fd=1; fd_err=2; break; case 't': title=argv[optind]+2; break; case 'e': exclude[excl_len++]=argv[optind]+2; break; case 'd': flagdirent=1; case 's': switch (argv[optind][2]) { case 'n': compare=(int (*)(const void *,const void *))byname; break; case 't': compare=(int (*)(const void *,const void *))bytime; break; case 's': compare=(int (*)(const void *,const void *))bysize; break; case 'r': flagsort=0; break; } break; case 'h': default: goto usage; } } argc -= (optind); if (!argc && !flagdirent) { usage: err("usage: mkhtmlindex [-m] [-o] [-d] [-tTitle] [-sntsr] [-eName] [file(s)]\n" "\t-m chmod 644/711; -o write to stdout; " "-d read files from disk\n" "\t-sx sort by Name Time Size Raw\n" "\t-ex.gz exclude x.gz; " "similary if file '" EXCLUDE "' exists\n" "\tif file '" INC_HEADER "' exists it is included for header\n" ); } exclude[excl_len] = 0; if (!stat(EXCLUDE, &st)) { int fd,r; char *map,*p; if ((fd=open(EXCLUDE, O_RDONLY)) >= 0) { if (!(map = alloca(st.st_size + 1))) err("alloca"); r=read(fd,map,st.st_size); close(fd); if (r>0) { map[r]=0; for (r=excl_len + 4, p=map; *p; ++p) if (*p == '\n') r++; if (!(exclude = alloca(i*sizeof(char *)))) err("alloca"); memcpy(exclude , argv, excl_len*sizeof(char*)); for (ee=exclude+excl_len,*ee=map,p=map; *p; p++) if (*p == '\n') { *p=0; ++ee, *ee = p+1; } ee[1]=0; } } } argv += (optind); if (flagdirent && (dir_buf.fd=open(".", O_RDONLY | O_DIRECTORY))==-1) err("open '.'"); for (i=0;;) { char *name=0; unsigned int len; if (flagdirent) {if ((de = read_dir(&dir_buf))) name = de->d_name;} else name = *argv++; if (name==0) break; if (name[0]=='.') continue; if (fd != 1 && !strcmp(name, "index.html")) continue; if (lstat(name,&st)) continue; for (ee=exclude; *ee; ee++) if (!strcmp(*ee,name)) break; if (*ee) continue; len = strlen(name) +1+ sizeof(struct d); if (!(tmp = alloc_a(&mem_block, len))) { mem_block.a = len + 2000; if (!(mem_block.s = alloca(mem_block.a))) err("alloca"); mem_block.len = mem_block.a; tmp = alloc_a(&mem_block,len); /* already allocated */ } tmp->next = 0; if (!root) root = tmp; else root_last->next = tmp; root_last = tmp; strcpy((char*)tmp + sizeof(struct d), name); tmp->time = st.st_mtime; tmp->size = st.st_size; if (flagmode && (S_ISLNK(st.st_mode) == 0)) st.st_mode = do_chmod(name, st.st_mode); fmt_mode(tmp->mode, st.st_mode); if (tmp->mode[0] == 'd') tmp->size=0; } if (flagdirent) close(dir_buf.fd); if (flagsort) root = do_qsort(root); if (fd == -1) { fd = open(IDX_TMP, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd == -1) err(IDX_TMP "\n"); out_buf.fd=fd; } ifd=open(INC_HEADER,O_RDONLY); if (title || ifd==-1) { if (title && !title[0]) title = 0; out("\nIndex"); if (title) {out(" of "); out(title);} out("\n"); out(""); out("\n

Index"); if (title) {out(" of "); out(title);} out("

\n\n"); out("Parent directory\n"); } else { if (include_fd(ifd)==-1) err("read " INC_HEADER); close(ifd); } out("
mode   size  last-change   name
\n"); for (now=time(0),tmp=root; tmp; tmp=tmp->next) { flaglink= (tmp->mode[0] == 'l'); flagdir = (tmp->mode[0] == 'd'); outb(tmp->mode,4); memset(x,' ',sizeof x); if (!flaglink && !flagdir) { i = fmt_humank(0, tmp->size); fmt_humank(x+7-i, tmp->size); } outb(x,9); tm = gmtime_r((time_t*)&tmp->time, &tm_); memset(x,' ',sizeof x); p = x+1; outb(months +3*tm->tm_mon, 3); if (tm->tm_mday<10) p++; p += fmt_ulong(p, tm->tm_mday) + 1; if (now - tmp->time >= 330*86400) p += 1 + fmt_ulong(p+1, 1900+tm->tm_year); else p += fmt_hour(p, tm->tm_hour, tm->tm_min); outb(x,11); p = (char*)tmp + sizeof(struct d); out(""); outh(p); if (flagdir) out("/"); out(""); if (flaglink) print_link(p); out("\n"); } ifd = open(INC_TAILER,O_RDONLY); if (ifd != -1) { if (include_fd(ifd)==-1) err("read " INC_TAILER); close(ifd); } else { char date[32]; out("\nindex genereted on "); tm = gmtime_r(&now, &tm_); outb(months +3*tm->tm_mon,3); p=date+1; memset(date,' ',32); if (tm->tm_mday <10) ++p; p += 1+fmt_ulong(p, tm->tm_mday); p += 1+fmt_ulong(p, 1900+tm->tm_year); p += fmt_hour(p, tm->tm_hour, tm->tm_min); outb(date,p-date+1); out("GMT by mkhtmlindex"); out("
\n
\n"); } buffer_flush(&out_buf); if (close(fd)) err("close\n"); if (fd!=1) { if (chmod(IDX_TMP,0644)) err("chmod\n"); if (open(IDX_DENY, O_RDONLY)==-1) if (rename(IDX_TMP, "index.html")) err("rename\n"); } return(0); }