+Version 1.7.0
+ - Allow user/group names up to 32 characters before clipping.
+ - Made -i compress XML and JSON output as much as possible by eliminating
+ extraneous whitespace.
+ - Added --caseinsensitive (renamed --ignore-case ala grep) flag so patterns
+ match without regard to case, courtesy of Jason A Donenfeld.
+ - Added --matchdirs option courtesy of Brian Mattern & Jason A. Donenfeld
+ <Jason@zx2c4.com>.
+ - Fixed possible buffer overflow on large uid/gids w/o user names/group
+ names (Alexandre Wendling <alexandrerw@celepar.pr.gov.br>)
+ - Added JSON support courtesy of Florian Sesser <fs@it-agenten.com>.
+ - Fixed formatting error with HTML output when -L 1 specified. (Sascha Zorn
+ <sascha.zorn@gmail.com>)
+ - Added file size sorting (Philipp M?ller <philippausmuensing@googlemail.com>)
+ - Added '--sort[=]<name>' option, ala ls.
+ - Fixed OS X makefile problems (Ryan Hollis <theryanhollis@gmail.com>)
+ - Fixed possible memory overflow in read_dir (path/lbuf not equal in size
+ to pathsize/lbufsize.) (Han Hui <hanhui03@163.com>)
+ - Fix S_ISDOOR/S_IFDOOR spelling mistake for Solaris. (Tim Mooney
+ <Tim.Mooney@ndsu.edu>)
+ - Make tree more reliably detect UTF-8 locales. (Mantas Mikulnas
+ <grawity@gmail.com> and others.)
+ - Return non-zero exit status on option errors, print usage to stdout when
+ not an error, add the posix '--' option terminator, Change -S description
+ to mean CP437 (console) output codes, not ASCII. (Ivan Shmakov
+ <oneingray@gmail.com>)
+
Version 1.6.0
- Re-org of code into multiple files, split HTML and Unix listdir() into
separate functions, various code cleanups and optimizations.
- Added -S to show ASCII tree lines (Gerald Scheidl)
- Made tree more portable (Guido Socher and others)
- Following options curtsey of Francesc Rocher:
+ Following options courtesy of Francesc Rocher:
- Added -o <filename> to redirect the output.
- Added -H <baseHRef> to print the tree in HTML format.
- Added -L to set the maximum level of directories to print.
# $Copyright: $
-# Copyright (c) 1996 - 2011 by Steve Baker
+# Copyright (c) 1996 - 2014 by Steve Baker
# All Rights reserved
#
# This program is free software; you can redistribute it and/or modify
CC=gcc
-VERSION=1.6.0
+VERSION=1.7.0
TREE_DEST=tree
BINDIR=${prefix}/bin
MAN=tree.1
MANDIR=${prefix}/man/man1
-OBJS=tree.o unix.o html.o xml.o hash.o color.o
+OBJS=tree.o unix.o html.o xml.o json.o hash.o color.o
# Uncomment options below for your particular OS:
# Linux defaults:
-#CFLAGS=-ggdb -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-CFLAGS=-O4 -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
-LDFLAGS=-s
+CFLAGS=-ggdb -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+#CFLAGS=-O4 -Wall -DLINUX -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+#LDFLAGS=-s
# Uncomment for FreeBSD:
#CFLAGS=-O2 -Wall -fomit-frame-pointer
#CC=cc
#CFLAGS=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp
#LDFLAGS=
+#MANDIR=/usr/share/man/man1
#OBJS+=strverscmp.o
# Uncomment for HP/UX:
install -d $(BINDIR)
install -d $(MANDIR)
if [ -e $(TREE_DEST) ]; then \
- install -s $(TREE_DEST) $(BINDIR)/$(TREE_DEST); \
+ install $(TREE_DEST) $(BINDIR)/$(TREE_DEST); \
fi
install doc/$(MAN) $(MANDIR)/$(MAN)
- Suggested that tree backslash spaces like ls does for script use. Made
output more like ls.
+Ivan Shmakov
+ - Pointed out multiple CLI defenciencies (via Debian)
+
+Mantas Mikulnas
+ - Provided patch to make tree more reliably detect the UTF-8 locale.
+
+Tim Mooney
+ - Noticed S_ISDOOR/S_IFDOOR spelling mistake for under Solaris.
+
+Han Hui
+ - Pointed out possible memory overflow in read_dir (path/lbuf not equal in size
+ to pathsize/lbufsize.)
+
+Ryan Hollis
+ - Pointed out problems with the Makefile w/ respect to OSX.
+
+Philipp M?ller
+ - Provided patch for filesize sorting.
+
+Sascha Zorn
+ - Pointed out that the HTML output was broken when -L 1 option was used.
+
+Alexandre Wendling
+ - Pointed out that modern systems may use 32 bit uid/gids which could lead
+ to a potential buffer overflow in the uid/gid to name mapping functions.
+
+Florian Sesser
+ - Provided patch to add JSON support.
+
+Brian Mattern & Jason A. Donenfeld
+ - Provided patch to add --matchdirs functionality.
+
+Jason A. Donenfeld
+ - Added --caseinsentive, renamed --ignore-case option.
+ - Bugged me a lot.
+
+
And many others whom I've failed to keep track of. I should have started
this list years ago.
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
.\" $Copyright: $
-.\" Copyright (c) 1996 - 2011 by Steve Baker
+.\" Copyright (c) 1996 - 2012 by Steve Baker
.\" All Rights reserved
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
.\"
...
-.TH TREE 1 "\*(V)" "Tree 1.6.0"
+.TH TREE 1 "" "Tree 1.7.0"
.SH NAME
tree \- list contents of directories in a tree-like format.
.SH SYNOPSIS
-\fBtree\fP [\fB-acdfghilnpqrstuvxACDFQNSUX\fP] [\fB-L\fP \fIlevel\fP [\fB-R\fP]] [\fB-H\fP \fIbaseHREF\fP] [\fB-T\fP \fItitle\fP] [\fB-o\fP \fIfilename\fP] [\fB--nolinks\fP] [\fB-P\fP \fIpattern\fP] [\fB-I\fP \fIpattern\fP] [\fB--inodes\fP] [\fB--device\fP] [\fB--noreport\fP] [\fB--dirsfirst\fP] [\fB--version\fP] [\fB--help\fP] [\fB--filelimit\fP \fI#\fP] [\fB--si\fP] [\fB--prune\fP] [\fB--du\fP] [\fB--timefmt\fP \fIformat\fP] [\fIdirectory\fP ...]
+\fBtree\fP [\fB-acdfghilnpqrstuvxACDFQNSUX\fP] [\fB-L\fP \fIlevel\fP [\fB-R\fP]] [\fB-H\fP \fIbaseHREF\fP] [\fB-T\fP \fItitle\fP] [\fB-o\fP \fIfilename\fP] [\fB--nolinks\fP] [\fB-P\fP \fIpattern\fP] [\fB-I\fP \fIpattern\fP] [\fB--inodes\fP] [\fB--device\fP] [\fB--noreport\fP] [\fB--dirsfirst\fP] [\fB--version\fP] [\fB--help\fP] [\fB--filelimit\fP \fI#\fP] [\fB--si\fP] [\fB--prune\fP] [\fB--du\fP] [\fB--timefmt\fP \fIformat\fP] [\fB--matchdirs\fP] [\fB--\fP] [\fIdirectory\fP ...]
+
.br
.SH DESCRIPTION
\fITree\fP is a recursive directory listing program that produces a depth
.B -I \fIpattern\fP
Do not list those files that match the wild-card \fIpattern\fP.
.PP
+ .TP
+.B --ignore-case
+If a match pattern is specified by the \fB-P\fP or \fB-I\fP option, this will
+cause the pattern to match without regards to the case of each letter.
+.PP
+.TP
+.B --matchdirs
+If a match pattern is specified by the \fB-P\fP option, this will cause the
+pattern to be applied to directory names (in addition to filenames). In the
+event of a match on the directory name, matching is disabled for the directory's
+contents. If the \fB--prune\fP option is used, empty folders that match the
+pattern will not be pruned.
+.PP
.TP
.B --prune
Makes tree prune empty directories from the output, useful when used in
Sort the output by version.
.PP
.TP
-.B -r
-Sort the output in reverse alphabetic order.
-.PP
-.TP
.B -t
Sort the output by last modification time instead of alphabetically.
.PP
Do not sort. Lists files in directory order. Disables \fB--dirsfirst\fP.
.PP
.TP
+.B -r
+Sort the output in reverse order. This is a meta-sort that alter the above sorts.
+This option is disabled when \fB-U\fP is used.
+.PP
+.TP
.B --dirsfirst
List directories before files. This is a meta-sort that alters the above sorts.
This option is disabled when \fB-U\fP is used.
.PP
-
+.TP
+.B --sort[=]<name>
+Sort the output by name (as per ls): name (default), ctime (\fP-c\fP), mtime
+(\fP-t\fB), size or version (\fP-v\fB).
.SH GRAPHICS OPTIONS
.TP
.B -i
Makes tree not print the indentation lines, useful when used in conjunction
-with the \fB-f\fP option.
+with the \fB-f\fP option. Also removes as much whitespace as possible when used
+with the \fB-J\fP or \fB-x\fP options.
.PP
.TP
.B -A
.PP
.TP
.B -S
-Turn on ASCII line graphics (useful when using Linux console mode fonts). This
+Turn on CP437 line graphics (useful when using Linux console mode fonts). This
option is now equivalent to `--charset=IBM437' and may eventually be depreciated.
.PP
.TP
environment variable is not set. Useful to colorize output to a pipe.
.PP
-.SH XML/HTML OPTIONS
+.SH XML/JSON/HTML OPTIONS
.TP
.B -X
Turn on XML output. Outputs the directory tree as an XML formatted file.
.PP
.TP
+.B -J
+Turn on JSON output. Outputs the directory tree as an JSON formatted array.
+.PP
+.TP
.B -H \fIbaseHREF\fP
Turn on HTML output, including HTTP references. Useful for ftp sites.
\fIbaseHREF\fP gives the base ftp location when using HTML output. That is, the
.B --version
Outputs the version of tree.
.PP
+.TP
+.B --
+Option processing terminator. No further options will be processed after this.
+.PP
.br
.SH FILES
Output of time strings longer than this will be undefined, but are guaranteed
to not exceed 255 characters.
-XML trees are not colored, which is a bit of a shame.
+XML/JSON trees are not colored, which is a bit of a shame.
Probably more.
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
#define inohash(x) ((x)&255)
struct inotable *itable[256];
-char *uidtoname(int uid)
+char *uidtoname(uid_t uid)
{
struct xtable *o, *p, *t;
struct passwd *ent;
- char ubuf[6];
+ char ubuf[32];
int uent = HASH(uid);
for(o = p = utable[uent]; p ; p=p->nxt) {
t = xmalloc(sizeof(struct xtable));
if ((ent = getpwuid(uid)) != NULL) t->name = scopy(ent->pw_name);
else {
- sprintf(ubuf,"%d",uid);
+ snprintf(ubuf,30,"%d",uid);
+ ubuf[31] = 0;
t->name = scopy(ubuf);
}
t->xid = uid;
return t->name;
}
-char *gidtoname(int gid)
+char *gidtoname(gid_t gid)
{
struct xtable *o, *p, *t;
struct group *ent;
- char gbuf[6];
+ char gbuf[32];
int gent = HASH(gid);
for(o = p = gtable[gent]; p ; p=p->nxt) {
t = xmalloc(sizeof(struct xtable));
if ((ent = getgrgid(gid)) != NULL) t->name = scopy(ent->gr_name);
else {
- sprintf(gbuf,"%d",gid);
+ snprintf(gbuf,30,"%d",gid);
+ gbuf[31] = 0;
t->name = scopy(gbuf);
}
t->xid = gid;
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
extern bool Dflag, inodeflag, devflag, Rflag;
-extern bool noindent, force_color, xdev, nolinks, flimit, nosort;
+extern bool noindent, force_color, xdev, nolinks, flimit;
//extern char *title,
extern char *host, *sp;
void emit_html_header(const char *charset, char *title, char *version)
{
fprintf(outfile,
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
- "<html>\n"
- "<head>\n"
- " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n"
- " <meta name=\"Author\" content=\"Made by 'tree'\">\n"
- " <meta name=\"GENERATOR\" content=\"%s\">\n"
- " <title>%s</title>\n"
- " <style type=\"text/css\">\n <!-- \n"
- " BODY { font-family : ariel, monospace, sans-serif; }\n"
- " P { font-weight: normal; font-family : ariel, monospace, sans-serif; color: black; background-color: transparent;}\n"
- " B { font-weight: normal; color: black; background-color: transparent;}\n"
- " A:visited { font-weight : normal; text-decoration : none; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
- " A:link { font-weight : normal; text-decoration : none; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
- " A:hover { color : #000000; font-weight : normal; text-decoration : underline; background-color : yellow; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
- " A:active { color : #000000; font-weight: normal; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
- " .VERSION { font-size: small; font-family : arial, sans-serif; }\n"
- " .NORM { color: black; background-color: transparent;}\n"
- " .FIFO { color: purple; background-color: transparent;}\n"
- " .CHAR { color: yellow; background-color: transparent;}\n"
- " .DIR { color: blue; background-color: transparent;}\n"
- " .BLOCK { color: yellow; background-color: transparent;}\n"
- " .LINK { color: aqua; background-color: transparent;}\n"
- " .SOCK { color: fuchsia;background-color: transparent;}\n"
- " .EXEC { color: green; background-color: transparent;}\n"
- " -->\n </style>\n"
- "</head>\n"
- "<body>\n"
- "\t<h1>%s</h1><p>\n\t",charset ? charset : "iso-8859-1", version, title, title);
+ "<!DOCTYPE html>\n"
+ "<html>\n"
+ "<head>\n"
+ " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n"
+ " <meta name=\"Author\" content=\"Made by 'tree'\">\n"
+ " <meta name=\"GENERATOR\" content=\"%s\">\n"
+ " <title>%s</title>\n"
+ " <style type=\"text/css\">\n <!-- \n"
+ " BODY { font-family : ariel, monospace, sans-serif; }\n"
+ " P { font-weight: normal; font-family : ariel, monospace, sans-serif; color: black; background-color: transparent;}\n"
+ " B { font-weight: normal; color: black; background-color: transparent;}\n"
+ " A:visited { font-weight : normal; text-decoration : none; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
+ " A:link { font-weight : normal; text-decoration : none; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
+ " A:hover { color : #000000; font-weight : normal; text-decoration : underline; background-color : yellow; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
+ " A:active { color : #000000; font-weight: normal; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }\n"
+ " .VERSION { font-size: small; font-family : arial, sans-serif; }\n"
+ " .NORM { color: black; background-color: transparent;}\n"
+ " .FIFO { color: purple; background-color: transparent;}\n"
+ " .CHAR { color: yellow; background-color: transparent;}\n"
+ " .DIR { color: blue; background-color: transparent;}\n"
+ " .BLOCK { color: yellow; background-color: transparent;}\n"
+ " .LINK { color: aqua; background-color: transparent;}\n"
+ " .SOCK { color: fuchsia;background-color: transparent;}\n"
+ " .EXEC { color: green; background-color: transparent;}\n"
+ " -->\n </style>\n"
+ "</head>\n"
+ "<body>\n"
+ "\t<h1>%s</h1><p>\n\t",charset ? charset : "iso-8859-1", version, title, title);
}
off_t html_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
int i,n,c;
char hclr[20], *hdir, *hcmd;
- if ((Level >= 0) && (lev > Level)) return 0;
+ if ((Level >= 0) && (lev > Level)) {
+ fprintf(outfile, "<br>\n");
+ return 0;
+ }
if (xdev && lev == 0) {
stat(d,&sb);
return 0;
}
- if (!nosort) qsort(dir,n,sizeof(struct _info *),cmpfunc);
+ if (cmpfunc) qsort(dir,n,sizeof(struct _info *),cmpfunc);
if (lev >= maxdirs-1) {
dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
memset(dirs+(maxdirs-1024), 0, sizeof(int) * 1024);
--- /dev/null
+/* $Copyright: $
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
+ * All Rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "tree.h"
+
+extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
+extern bool Dflag, inodeflag, devflag, Rflag, cflag;
+extern bool noindent, force_color, xdev, nolinks, flimit;
+
+extern const int ifmt[];
+extern const char fmt[], *ftype[];
+
+extern void (*listdir)(char *, int *, int *, u_long, dev_t);
+extern int (*cmpfunc)();
+extern FILE *outfile;
+extern int Level, *dirs, maxdirs;
+
+extern char *endcode;
+
+/* JSON code courtesy of Florian Sesser <fs@it-agenten.com>
+[
+ {"type": "directory", "name": "name", "mode": "0777", "user": "user", "group": "group", "inode": ###, "dev": ####, "time": "00:00 00-00-0000", "contents": [
+ {"type": "link", "name": "name", "target": "name", "contents": [... if link is followed, otherwise this is empty.]}
+ {"type": "file", "name": "name", "mode": "0777", "size": ###, "group": "group", "inode": ###, "dev": ###, "time": "00:00 00-00-0000"}
+ {"type": "socket", "name": "", "error": "some error" ...}
+ {"type": "block", "name": "" ...},
+ {"type": "char", "name": "" ...},
+ {"type": "fifo", "name": "" ...},
+ {"type": "door", "name": "" ...},
+ {"type": "port", "name": "" ...}
+ ]},
+ {"type": "report", "size": ###, "files": ###, "directories": ###}
+]
+*/
+
+off_t json_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
+{
+ char *path;
+ bool nlf = FALSE;
+ long pathsize = 0;
+ struct _info **dir, **sav;
+ struct stat sb;
+ int t, n, mt;
+
+ if ((Level >= 0) && (lev > Level)) {
+ if (!noindent) fputc('\n',outfile);
+ return 0;
+ }
+
+ if (xdev && lev == 0) {
+ stat(d,&sb);
+ dev = sb.st_dev;
+ }
+
+ sav = dir = read_dir(d,&n);
+ if (!dir && n) {
+ fprintf(outfile,"{\"error\": \"opening dir\"}%s",noindent?"":"\n");
+ return 0;
+ }
+ if (!n) {
+ if (!noindent) fputc('\n', outfile);
+ free_dir(sav);
+ return 0;
+ }
+ if (flimit > 0 && n > flimit) {
+ fprintf(outfile,", \"error\": \"%d entries exceeds filelimit, not opening dir\"\n",n);
+ free_dir(sav);
+ return 0;
+ }
+
+ if (cmpfunc) qsort(dir,n,sizeof(struct _info *),cmpfunc);
+ if (lev >= maxdirs-1) {
+ dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
+ memset(dirs+(maxdirs-1024), 0, sizeof(int) * 1024);
+ }
+ dirs[lev] = 1;
+ if (!*(dir+1)) dirs[lev] = 2;
+ if (!noindent) fprintf(outfile,"\n");
+
+ path = malloc(pathsize=4096);
+
+ while(*dir) {
+ if (!noindent) json_indent(lev);
+
+ if ((*dir)->lnk) mt = (*dir)->mode & S_IFMT;
+ else mt = (*dir)->mode & S_IFMT;
+ for(t=0;ifmt[t];t++)
+ if (ifmt[t] == mt) break;
+ fprintf(outfile,"{\"type\":\"%s\"", ftype[t]);
+
+ if (fflag) {
+ if (sizeof(char) * (strlen(d)+strlen((*dir)->name)+2) > pathsize)
+ path=xrealloc(path,pathsize=(sizeof(char) * (strlen(d)+strlen((*dir)->name)+1024)));
+ if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
+ else sprintf(path,"%s/%s",d,(*dir)->name);
+ } else {
+ if (sizeof(char) * (strlen((*dir)->name)+1) > pathsize)
+ path=xrealloc(path,pathsize=(sizeof(char) * (strlen((*dir)->name)+1024)));
+ sprintf(path,"%s",(*dir)->name);
+ }
+
+ fprintf(outfile, ",\"name\":\"");
+ html_encode(outfile,path);
+ fputc('"',outfile);
+
+ if ((*dir)->lnk) {
+ fprintf(outfile, ",\"target\":\"");
+ html_encode(outfile,(*dir)->lnk);
+ fputc('"',outfile);
+ }
+ json_fillinfo(*dir);
+ if (!(*dir)->isdir && !(*dir)->lnk) {
+ fputc('}',outfile);
+ if (*(dir+1)) fputc(',',outfile);
+ } else
+ fprintf(outfile, ",\"contents\":[");
+
+ if ((*dir)->isdir) {
+ if ((*dir)->lnk) {
+ if (lflag && !(xdev && dev != (*dir)->dev)) {
+ if (findino((*dir)->inode,(*dir)->dev)) {
+ fprintf(outfile,"{\"error\":\"recursive, not followed\"}");
+ } else {
+ saveino((*dir)->inode, (*dir)->dev);
+ if (*(*dir)->lnk == '/')
+ listdir((*dir)->lnk,dt,ft,lev+1,dev);
+ else {
+ if (strlen(d)+strlen((*dir)->lnk)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
+ if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->lnk);
+ else sprintf(path,"%s/%s",d,(*dir)->lnk);
+ listdir(path,dt,ft,lev+1,dev);
+ }
+ nlf = TRUE;
+ }
+ }
+ } else if (!(xdev && dev != (*dir)->dev)) {
+ if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
+ if (fflag && !strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
+ else sprintf(path,"%s/%s",d,(*dir)->name);
+ saveino((*dir)->inode, (*dir)->dev);
+ listdir(path,dt,ft,lev+1,dev);
+ nlf = TRUE;
+ }
+ *dt += 1;
+ } else *ft += 1;
+ if (*(dir+1) && !*(dir+2)) dirs[lev] = 2;
+ if (nlf) {
+ nlf = FALSE;
+ if (!noindent) json_indent(lev);
+ }
+ if ((*dir)->isdir || (*dir)->lnk) {
+ fprintf(outfile, "]}");
+ if (*(dir+1)) fputc(',',outfile);
+ }
+ if (!noindent) fputc('\n', outfile);
+ dir++;
+ }
+ dirs[lev] = 0;
+ free(path);
+ free_dir(sav);
+ return 0;
+}
+
+off_t json_rlistdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
+{
+ struct _info **dir;
+ off_t size = 0;
+ char *err;
+
+ dir = getfulltree(d, lev, dev, &size, &err);
+
+ memset(dirs, 0, sizeof(int) * maxdirs);
+
+ jsonr_listdir(dir, d, dt, ft, lev);
+
+ return size;
+}
+
+void jsonr_listdir(struct _info **dir, char *d, int *dt, int *ft, u_long lev)
+{
+ char *path;
+ long pathsize = 0;
+ struct _info **sav = dir;
+ bool nlf = FALSE;
+ int mt, t;
+
+ if (dir == NULL) return;
+
+ dirs[lev] = 1;
+ if (!*(dir+1)) dirs[lev] = 2;
+ if (!noindent) fprintf(outfile,"\n");
+
+ path = malloc(pathsize=4096);
+
+ while(*dir) {
+ if (!noindent) json_indent(lev);
+
+ if ((*dir)->lnk) mt = (*dir)->mode & S_IFMT;
+ else mt = (*dir)->mode & S_IFMT;
+ for(t=0;ifmt[t];t++)
+ if (ifmt[t] == mt) break;
+ fprintf(outfile,"{\"type\":\"%s\"", ftype[t]);
+
+ if (fflag) {
+ if (sizeof(char) * (strlen(d)+strlen((*dir)->name)+2) > pathsize)
+ path=xrealloc(path,pathsize=(sizeof(char) * (strlen(d)+strlen((*dir)->name)+1024)));
+ if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
+ else sprintf(path,"%s/%s",d,(*dir)->name);
+ } else {
+ if (sizeof(char) * (strlen((*dir)->name)+1) > pathsize)
+ path=xrealloc(path,pathsize=(sizeof(char) * (strlen((*dir)->name)+1024)));
+ sprintf(path,"%s",(*dir)->name);
+ }
+
+ fprintf(outfile, ",\"name\":\"");
+ html_encode(outfile,path);
+ fputc('"',outfile);
+
+ if ((*dir)->lnk) {
+ fprintf(outfile, ",\"target\":\"");
+ html_encode(outfile,(*dir)->lnk);
+ fputc('"',outfile);
+ }
+
+ json_fillinfo(*dir);
+ if (mt != S_IFDIR && mt != S_IFLNK && (*dir)->err == NULL) fprintf(outfile,"},");
+ else fprintf(outfile, ",\"contents\":[");
+
+ if ((*dir)->err) {
+ fprintf(outfile,",\"error\":\"%s\"", (*dir)->err);
+ free((*dir)->err);
+ (*dir)->err = NULL;
+ }
+ if ((*dir)->child) {
+ if (fflag) {
+ if (strlen(d)+strlen((*dir)->name)+2 > pathsize) path=xrealloc(path,pathsize=(strlen(d)+strlen((*dir)->name)+1024));
+ if (!strcmp(d,"/")) sprintf(path,"%s%s",d,(*dir)->name);
+ else sprintf(path,"%s/%s",d,(*dir)->name);
+ }
+ jsonr_listdir((*dir)->child, fflag? path : NULL, dt, ft, lev+1);
+ nlf = TRUE;
+ *dt += 1;
+ } else {
+ if ((*dir)->isdir) *dt += 1;
+ else *ft += 1;
+ }
+
+ if (*(dir+1) && !*(dir+2)) dirs[lev] = 2;
+ if (nlf) {
+ nlf = FALSE;
+ if (!noindent) json_indent(lev);
+ }
+ if (mt == S_IFDIR || mt == S_IFLNK || (*dir)->err != NULL) fprintf(outfile,"]},%s",noindent?"":"\n");
+ else {
+ if (!noindent) putc('\n',outfile);
+ }
+ dir++;
+ }
+ dirs[lev] = 0;
+ free(path);
+ free_dir(sav);
+}
+
+void json_indent(int maxlevel)
+{
+ int i;
+
+ fprintf(outfile, " ");
+ for(i=0; i<maxlevel; i++)
+ fprintf(outfile, " ");
+}
+
+void json_fillinfo(struct _info *ent)
+{
+ #ifdef __USE_FILE_OFFSET64
+ if (inodeflag) fprintf(outfile,",\"inode\":%lld",(long long)ent->inode);
+ #else
+ if (inodeflag) fprintf(outfile,",\"inode\":%ld",(long int)ent->inode);
+ #endif
+ if (devflag) fprintf(outfile, ",\"dev\":%d", (int)ent->dev);
+ #ifdef __EMX__
+ if (pflag) fprintf(outfile, ",\"mode\":\"%04o\",\"prot\":\"%s\"",ent->attr, prot(ent->attr));
+ #else
+ if (pflag) fprintf(outfile, ",\"mode\":\"%04o\",\"prot\":\"%s\"", ent->mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID|S_ISVTX), prot(ent->mode));
+ #endif
+ if (uflag) fprintf(outfile, ",\"user\":\"%s\"", uidtoname(ent->uid));
+ if (gflag) fprintf(outfile, ",\"group\":\"%s\"", gidtoname(ent->gid));
+ if (sflag) fprintf(outfile, ",\"size\":%lld", (long long int)ent->size);
+ if (Dflag) fprintf(outfile, ",\"time\":\"%s\"", do_date(cflag? ent->ctime : ent->mtime));
+}
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
#include "tree.h"
-static char *version ="$Version: $ tree v1.6.0 (c) 1996 - 2011 by Steve Baker, Thomas Moore, Francesc Rocher, Kyosuke Tokoro $";
-static char *hversion="\t\t tree v1.6.0 %s 1996 - 2011 by Steve Baker and Thomas Moore <br>\n"
+static char *version ="$Version: $ tree v1.7.0 (c) 1996 - 2014 by Steve Baker, Thomas Moore, Francesc Rocher, Florian Sesser, Kyosuke Tokoro $";
+static char *hversion="\t\t tree v1.7.0 %s 1996 - 2014 by Steve Baker and Thomas Moore <br>\n"
"\t\t HTML output hacked and copyleft %s 1998 by Francesc Rocher <br>\n"
+ "\t\t JSON output hacked and copyleft %s 2014 by Florian Sesser <br>\n"
"\t\t Charsets / OS/2 support %s 2001 by Kyosuke Tokoro\n";
/* Globals */
bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
bool qflag, Nflag, Qflag, Dflag, inodeflag, devflag, hflag, Rflag;
-bool Hflag, siflag, cflag, Xflag, duflag, pruneflag;
-bool noindent, force_color, nocolor, xdev, noreport, nolinks, flimit, dirsfirst, nosort;
-char *pattern = NULL, *ipattern = NULL, *host = NULL, *title = "Directory Tree", *sp = " ";
+bool Hflag, siflag, cflag, Xflag, Jflag, duflag, pruneflag;
+bool noindent, force_color, nocolor, xdev, noreport, nolinks, flimit, dirsfirst;
+bool ignorecase, matchdirs;
+bool reverse;
+char *pattern = NULL, *ipattern = NULL, *host = NULL, *title = "Directory Tree", *sp = " ", *_nl = "\n";
char *timefmt = NULL;
const char *charset = NULL;
#ifdef __EMX__
const u_short ifmt[]={ FILE_ARCHIVED, FILE_DIRECTORY, FILE_SYSTEM, FILE_HIDDEN, FILE_READONLY, 0};
#else
- #ifdef S_ISPORT
- const u_int ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, S_ISDOOR, S_ISPORT, 0};
+ #ifdef S_IFPORT
+ const u_int ifmt[] = {S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFSOCK, S_IFIFO, S_IFDOOR, S_IFPORT, 0};
const char fmt[] = "-dlcbspDP?";
const char *ftype[] = {"file", "directory", "link", "char", "block", "socket", "fifo", "door", "port", "unknown", NULL};
#else
#endif
#endif
+struct sorts {
+ char *name;
+ int (*cmpfunc)();
+} sorts[] = {
+ {"name", alnumsort},
+ {"version", versort},
+ {"size", fsizesort},
+ {"mtime", mtimesort},
+ {"ctime", ctimesort},
+ {NULL, NULL}
+};
/* Externs */
/* hash.c */
int main(int argc, char **argv)
{
char **dirname = NULL;
- int i,j=0,n,p,q,dtotal,ftotal,colored = FALSE;
+ int i,j=0,k,n,optf,p,q,dtotal,ftotal,colored = FALSE;
struct stat st;
- char sizebuf[64];
+ char sizebuf[64], *stmp;
off_t size = 0;
mode_t mt;
+ bool needfulltree;
q = p = dtotal = ftotal = 0;
aflag = dflag = fflag = lflag = pflag = sflag = Fflag = uflag = gflag = FALSE;
Dflag = qflag = Nflag = Qflag = Rflag = hflag = Hflag = siflag = cflag = FALSE;
- noindent = force_color = nocolor = xdev = noreport = nolinks = FALSE;
- dirsfirst = nosort = inodeflag = devflag = Xflag = FALSE;
+ noindent = force_color = nocolor = xdev = noreport = nolinks = reverse = FALSE;
+ ignorecase = matchdirs = dirsfirst = inodeflag = devflag = Xflag = Jflag = FALSE;
duflag = pruneflag = FALSE;
flimit = 0;
dirs = xmalloc(sizeof(int) * (maxdirs=4096));
setlocale(LC_COLLATE, "");
charset = getcharset();
- if (charset == NULL && patmatch(setlocale(LC_CTYPE,NULL), "*[Uu][Tt][Ff]-8") == 1) {
+ if (charset == NULL && strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
charset = "UTF-8";
}
memset(gtable,0,sizeof(gtable));
memset(itable,0,sizeof(itable));
+ optf = TRUE;
for(n=i=1;i<argc;i=n) {
n++;
- if (argv[i][0] == '-' && argv[i][1]) {
+ if (optf && argv[i][0] == '-' && argv[i][1]) {
for(j=1;argv[i][j];j++) {
switch(argv[i][j]) {
case 'N':
break;
case 'i':
noindent = TRUE;
+ _nl = "";
break;
case 'C':
force_color = TRUE;
cflag = TRUE;
break;
case 'r':
- cmpfunc = reversealnumsort;
+ reverse = TRUE;
break;
case 'v':
cmpfunc = versort;
break;
case 'U':
- nosort = TRUE;
+ cmpfunc = NULL;
break;
case 'X':
Hflag = FALSE;
Xflag = TRUE;
break;
+ case 'J':
+ Jflag = TRUE;
+ break;
case 'H':
Hflag = TRUE;
Xflag = FALSE;
break;
case '-':
if (j == 1) {
- if (!strcmp("--help",argv[i])) usage(2);
+ if (!strcmp("--", argv[i])) {
+ optf = FALSE;
+ break;
+ }
+ if (!strcmp("--help",argv[i])) {
+ usage(2);
+ exit(0);
+ }
if (!strcmp("--version",argv[i])) {
char *v = version+12;
printf("%.*s\n",(int)strlen(v)-1,v);
Dflag = TRUE;
break;
}
+ if (!strncmp("--ignore-case",argv[i],13)) {
+ j = strlen(argv[i])-1;
+ ignorecase = TRUE;
+ break;
+ }
+ if (!strncmp("--matchdirs",argv[i],11)) {
+ j = strlen(argv[i])-1;
+ matchdirs = TRUE;
+ break;
+ }
+ if (!strncmp("--sort",argv[i],6)) {
+ j = 6;
+ if (*(argv[i]+j) == '=') {
+ if (*(argv[i]+(++j))) {
+ stmp = argv[i]+j;
+ j = strlen(argv[i])-1;
+ } else {
+ fprintf(stderr,"tree: missing argument to --sort=\n");
+ exit(1);
+ }
+ } else if (argv[n] != NULL) {
+ stmp = argv[n++];
+ } else {
+ fprintf(stderr,"tree: missing argument to --sort\n");
+ exit(1);
+ }
+ cmpfunc = (void *)1;
+ for(k=0;sorts[k].name;k++) {
+ if (strcasecmp(sorts[k].name,stmp) == 0) {
+ cmpfunc = sorts[k].cmpfunc;
+ break;
+ }
+ }
+ if (cmpfunc == (void *)1) {
+ fprintf(stderr,"tree: sort type '%s' not valid, should be one of: ", stmp);
+ for(k=0; sorts[k].name; k++)
+ printf("%s%c", sorts[k].name, sorts[k+1].name? ',': '\n');
+ exit(1);
+ }
+ break;
+ }
}
default:
fprintf(stderr,"tree: Invalid argument -`%c'.\n",argv[i][j]);
parse_dir_colors();
initlinedraw(0);
+ needfulltree = duflag || pruneflag || matchdirs;
+
/* Set our listdir function and sanity check options. */
if (Hflag) {
- listdir = (duflag || pruneflag)? html_rlistdir : html_listdir;
+ listdir = needfulltree ? html_rlistdir : html_listdir;
Xflag = FALSE;
} else if (Xflag) {
- listdir = (duflag || pruneflag)? xml_rlistdir : xml_listdir;
+ listdir = needfulltree ? xml_rlistdir : xml_listdir;
colorize = FALSE;
colored = FALSE; /* Do people want colored XML output? */
+ } else if (Jflag) {
+ listdir = needfulltree ? json_rlistdir : json_listdir;
+ colorize = FALSE;
+ colored = FALSE; /* Do people want colored JSON output? */
} else {
- listdir = (duflag || pruneflag)? unix_rlistdir : unix_listdir;
+ listdir = needfulltree ? unix_rlistdir : unix_listdir;
}
if (dflag) pruneflag = FALSE; /* You'll just get nothing otherwise. */
} else if (Xflag) {
fprintf(outfile,"<?xml version=\"1.0\"");
if (charset) fprintf(outfile," encoding=\"%s\"",charset);
- fprintf(outfile,"?>\n<tree>\n");
- }
+ fprintf(outfile,"?>%s<tree>%s",_nl,_nl);
+ } else if (Jflag)
+ fputc('[',outfile);
if (dirname) {
for(colored=i=0;dirname[i];i++,colored=0) {
if (colorize) colored = color(st.st_mode,dirname[i],n<0,FALSE);
size += st.st_size;
}
- if (Xflag) {
+ if (Xflag || Jflag) {
mt = st.st_mode & S_IFMT;
for(j=0;ifmt[j];j++)
if (ifmt[j] == mt) break;
- fprintf(outfile," <%s", ftype[j]);
- if (mt == S_IFDIR) {
- fprintf(outfile, " name=\"%s\"", dirname[i]);
+ if (Xflag)
+ fprintf(outfile,"%s<%s name=\"%s\">", noindent?"":" ", ftype[j], dirname[i]);
+ else if (Jflag) {
+ if (i) fprintf(outfile, ",");
+ fprintf(outfile,"%s{\"type\":\"%s\",\"name\":\"%s\",\"contents\":[", noindent?"":"\n ", ftype[j], dirname[i]);
}
- fputc('>',outfile);
- if (mt != S_IFDIR) fprintf(outfile,"%s", dirname[i]);
} else if (!Hflag) printit(dirname[i]);
if (colored) fprintf(outfile,"%s",endcode);
if (!Hflag) size += listdir(dirname[i],&dtotal,&ftotal,0,0);
chdir(curdir);
}
}
- if (Xflag) fprintf(outfile," </%s>\n",ftype[j]);
+ if (Xflag) fprintf(outfile,"%s</%s>\n",noindent?"":" ", ftype[j]);
+ if (Jflag) fprintf(outfile,"%s]}",noindent?"":" ");
}
} else {
if ((n = lstat(".",&st)) >= 0) {
if (colorize) colored = color(st.st_mode,".",n<0,FALSE);
size = st.st_size;
}
- if (Xflag) fprintf(outfile," <directory name=\".\">");
+ if (Xflag) fprintf(outfile,"%s<directory name=\".\">",noindent?"":" ");
+ else if (Jflag) fprintf(outfile, "{\"type\":\"directory\",\"name\": \".\",\"contents\":[");
else if (!Hflag) fprintf(outfile,".");
if (colored) fprintf(outfile,"%s",endcode);
size += listdir(".",&dtotal,&ftotal,0,0);
- if (Xflag) fprintf(outfile," </directory>\n");
+ if (Xflag) fprintf(outfile,"%s</directory>%s",noindent?"":" ", _nl);
+ if (Jflag) fprintf(outfile,"%s]}",noindent?"":" ");
}
if (Hflag)
if (!noreport) {
if (Xflag) {
- fprintf(outfile," <report>\n");
- if (duflag) fprintf(outfile," <size>%lld</size>\n", size);
- fprintf(outfile," <directories>%d</directories>\n", dtotal);
- if (!dflag) fprintf(outfile," <files>%d</files>\n", ftotal);
- fprintf(outfile," </report>\n");
+ fprintf(outfile,"%s<report>%s",noindent?"":" ", _nl);
+ if (duflag) fprintf(outfile,"%s<size>%lld</size>%s", noindent?"":" ", (long long int)size, _nl);
+ fprintf(outfile,"%s<directories>%d</directories>%s", noindent?"":" ", dtotal, _nl);
+ if (!dflag) fprintf(outfile,"%s<files>%d</files>%s", noindent?"":" ", ftotal, _nl);
+ fprintf(outfile,"%s</report>%s",noindent?"":" ", _nl);
+ } else if (Jflag) {
+ fprintf(outfile, ",%s{\"type\":\"report\"",noindent?"":"\n ");
+ if (duflag) fprintf(outfile,",\"size\":%lld", (long long int)size);
+ fprintf(outfile,",\"directories\":%d", dtotal);
+ if (!dflag) fprintf(outfile,",\"files\":%d", ftotal);
+ fprintf(outfile, "}");
} else {
if (duflag) {
psize(sizebuf, size);
fprintf(outfile,"\t<br><br>\n\t</p>\n");
fprintf(outfile,"\t<hr>\n");
fprintf(outfile,"\t<p class=\"VERSION\">\n");
- fprintf(outfile,hversion,linedraw->copy, linedraw->copy, linedraw->copy);
+ fprintf(outfile,hversion,linedraw->copy, linedraw->copy, linedraw->copy, linedraw->copy);
fprintf(outfile,"\t</p>\n");
fprintf(outfile,"</body>\n");
fprintf(outfile,"</html>\n");
} else if (Xflag) {
fprintf(outfile,"</tree>\n");
+ } else if (Jflag) {
+ fprintf(outfile, "%s]\n",_nl);
}
if (outfilename != NULL) fclose(outfile);
void usage(int n)
{
- fprintf(stderr,
- "usage: tree [-acdfghilnpqrstuvxACDFQNSUX] [-H baseHREF] [-T title ] [-L level [-R]]\n"
- "\t[-P pattern] [-I pattern] [-o filename] [--version] [--help] [--inodes]\n"
- "\t[--device] [--noreport] [--nolinks] [--dirsfirst] [--charset charset]\n"
- "\t[--filelimit[=]#] [--si] [--timefmt[=]<f>] [<directory list>]\n");
- if (n < 2) exit(0);
- fprintf(stderr,
+ /* 123456789!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */
+ /* \t9!123456789!123456789!123456789!123456789!123456789!123456789!123456789! */
+ fprintf(n < 2? stderr: stdout,
+ "usage: tree [-acdfghilnpqrstuvxACDFJQNSUX] [-H baseHREF] [-T title ]\n"
+ "\t[-L level [-R]] [-P pattern] [-I pattern] [-o filename] [--version]\n"
+ "\t[--help] [--inodes] [--device] [--noreport] [--nolinks] [--dirsfirst]\n"
+ "\t[--charset charset] [--filelimit[=]#] [--si] [--timefmt[=]<f>]\n"
+ "\t[--sort[=]<name>] [--matchdirs] [--ignore-case] [--] [<directory list>]\n");
+ if (n < 2) return;
+ fprintf(stdout,
" ------- Listing options -------\n"
" -a All files are listed.\n"
" -d List directories only.\n"
" -R Rerun tree when max dir level reached.\n"
" -P pattern List only those files that match the pattern given.\n"
" -I pattern Do not list files that match the given pattern.\n"
+ " --ignore-case Ignore case when pattern matching.\n"
+ " --matchdirs Include directory names in -P pattern matching.\n"
" --noreport Turn off file/directory count at end of tree listing.\n"
" --charset X Use charset X for terminal/HTML and indentation line output.\n"
" --filelimit # Do not descend dirs with more than # files in them.\n"
" --device Print device ID number to which each file belongs.\n"
" ------- Sorting options -------\n"
" -v Sort files alphanumerically by version.\n"
- " -r Sort files in reverse alphanumeric order.\n"
" -t Sort files by last modification time.\n"
" -c Sort files by last status change time.\n"
" -U Leave files unsorted.\n"
+ " -r Reverse the order of the sort.\n"
" --dirsfirst List directories before files (-U disables).\n"
+ " --sort X Select sort: name,version,size,mtime,ctime.\n"
" ------- Graphics options ------\n"
" -i Don't print indentation lines.\n"
" -A Print ANSI lines graphic indentation lines.\n"
- " -S Print with ASCII graphics indentation lines.\n"
+ " -S Print with CP437 (console) graphics indentation lines.\n"
" -n Turn colorization off always (-C overrides).\n"
" -C Turn colorization on always.\n"
- " ------- XML/HTML options -------\n"
+ " ------- XML/HTML/JSON options -------\n"
" -X Prints out an XML representation of the tree.\n"
+ " -J Prints out an JSON representation of the tree.\n"
" -H baseHREF Prints out HTML format with baseHREF as top directory.\n"
" -T string Replace the default HTML title and H1 header with string.\n"
" --nolinks Turn off hyperlinks in HTML output.\n"
" ---- Miscellaneous options ----\n"
" --version Print version and exit.\n"
- " --help Print usage and this help message and exit.\n");
+ " --help Print usage and this help message and exit.\n"
+ " -- Options processing terminator.\n");
exit(0);
}
DIR *d;
int ne, p = 0, len, rs;
- pathsize = lbufsize = strlen(dir)+4096;
if (path == NULL) {
+ pathsize = lbufsize = strlen(dir)+4096;
path=xmalloc(pathsize);
lbuf=xmalloc(lbufsize);
}
struct _info **dir, **sav, **p, *sp;
struct stat sb;
int n;
-
+ u_long lev_tmp;
+ char *tmp_pattern = NULL, *start_rel_path;
+
*err = NULL;
if (Level >= 0 && lev > Level) return NULL;
if (xdev && lev == 0) {
stat(d,&sb);
dev = sb.st_dev;
}
+ // if the directory name matches, turn off pattern matching for contents
+ if (matchdirs && pattern) {
+ lev_tmp = lev;
+ start_rel_path = d + strlen(d);
+ for (start_rel_path = d + strlen(d); start_rel_path != d; --start_rel_path) {
+ if (*start_rel_path == '/')
+ --lev_tmp;
+ if (lev_tmp <= 0) {
+ if (*start_rel_path)
+ ++start_rel_path;
+ break;
+ }
+ }
+ if (*start_rel_path && patmatch(start_rel_path,pattern) == 1) {
+ tmp_pattern = pattern;
+ pattern = NULL;
+ }
+ }
sav = dir = read_dir(d,&n);
+ if (tmp_pattern) {
+ pattern = tmp_pattern;
+ tmp_pattern = NULL;
+ }
if (dir == NULL) {
*err = scopy("error opening dir");
return NULL;
free(path);
return NULL;
}
- if (!nosort) qsort(dir,n,sizeof(struct _info *),cmpfunc);
+ if (cmpfunc) qsort(dir,n,sizeof(struct _info *),cmpfunc);
if (lev >= maxdirs-1) {
dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
saveino((*dir)->inode, (*dir)->dev);
(*dir)->child = getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err));
}
- if (pruneflag && (*dir)->child == NULL) {
+ // prune empty folders, unless they match the requested pattern
+ if (pruneflag && (*dir)->child == NULL &&
+ !(matchdirs && pattern && patmatch((*dir)->name,pattern) == 1)) {
sp = *dir;
for(p=dir;*p;p++) *p = *(p+1);
n--;
/* Sorting functions */
int alnumsort(struct _info **a, struct _info **b)
{
- if (dirsfirst) {
- if ((*a)->isdir == (*b)->isdir) return strcoll((*a)->name,(*b)->name);
- else return (*a)->isdir ? -1 : 1;
+ int v;
+
+ if (dirsfirst && ((*a)->isdir != (*b)->isdir)) {
+ return (*a)->isdir ? -1 : 1;
}
- return strcoll((*a)->name,(*b)->name);
+ v = strcoll((*a)->name,(*b)->name);
+ return reverse? -v : v;
}
int versort(struct _info **a, struct _info **b)
{
- if (dirsfirst) {
- if ((*a)->isdir == (*b)->isdir) return strverscmp((*a)->name,(*b)->name);
- else return (*a)->isdir ? -1 : 1;
+ int v;
+
+ if (dirsfirst && ((*a)->isdir != (*b)->isdir)) {
+ return (*a)->isdir ? -1 : 1;
}
- return strverscmp((*a)->name,(*b)->name);
+ v = strverscmp((*a)->name,(*b)->name);
+ return reverse? -v : v;
}
-int reversealnumsort(struct _info **a, struct _info **b)
+int mtimesort(struct _info **a, struct _info **b)
{
- if (dirsfirst) {
- if ((*a)->isdir == (*b)->isdir) return strcoll((*b)->name,(*a)->name);
- else return (*a)->isdir ? -1 : 1;
+ int v;
+
+ if (dirsfirst && ((*a)->isdir != (*b)->isdir)) {
+ return (*a)->isdir ? -1 : 1;
+ }
+ if ((*a)->mtime == (*b)->mtime) {
+ v = strcoll((*a)->name,(*b)->name);
+ return reverse? -v : v;
}
- return strcoll((*b)->name,(*a)->name);
+ v = (*a)->mtime == (*b)->mtime? 0 : ((*a)->mtime < (*b)->mtime ? -1 : 1);
+ return reverse? -v : v;
}
-int mtimesort(struct _info **a, struct _info **b)
+int ctimesort(struct _info **a, struct _info **b)
{
- if (dirsfirst) {
- if ((*a)->isdir == (*b)->isdir) {
- if ((*a)->mtime == (*b)->mtime) return strcoll((*a)->name,(*b)->name);
- return (*a)->mtime < (*b)->mtime;
- }
- else return (*a)->isdir ? -1 : 1;
+ int v;
+
+ if (dirsfirst && ((*a)->isdir != (*b)->isdir)) {
+ return (*a)->isdir ? -1 : 1;
+ }
+ if ((*a)->ctime == (*b)->ctime) {
+ v = strcoll((*a)->name,(*b)->name);
+ return reverse? -v : v;
}
- if ((*a)->mtime == (*b)->mtime) return strcoll((*a)->name,(*b)->name);
- return (*a)->mtime < (*b)->mtime;
+ v = (*a)->ctime == (*b)->ctime? 0 : ((*a)->ctime < (*b)->ctime? -1 : 1);
+ return reverse? -v : v;
}
-int ctimesort(struct _info **a, struct _info **b)
+int sizecmp(off_t a, off_t b)
{
- if (dirsfirst) {
- if ((*a)->isdir == (*b)->isdir) {
- if ((*a)->ctime == (*b)->ctime) return strcoll((*a)->name,(*b)->name);
- return (*a)->ctime < (*b)->ctime;
- }
- else return (*a)->isdir ? -1 : 1;
+ return (a == b)? 0 : ((a < b)? 1 : -1);
+}
+
+int fsizesort(struct _info **a, struct _info **b)
+{
+ int v;
+
+ if (dirsfirst && ((*a)->isdir != (*b)->isdir)) {
+ return (*a)->isdir ? -1 : 1;
}
- if ((*a)->ctime == (*b)->ctime) return strcoll((*a)->name,(*b)->name);
- return (*a)->ctime < (*b)->ctime;
+ v = sizecmp((*a)->size, (*b)->size);
+ if (v == 0) v = strcoll((*a)->name,(*b)->name);
+ return reverse? -v : v;
}
+
void *xmalloc (size_t size)
{
register void *value = malloc (size);
}
}
+static inline char cond_lower(char c)
+{
+ return ignorecase ? tolower(c) : c;
+}
+
/*
* Patmatch() code courtesy of Thomas Moore (dark@mama.indstate.edu)
* '|' support added by David MacMahon (davidm@astron.Berkeley.EDU)
+ * Case insensitive support added by Jason A. Donenfeld (Jason@zx2c4.com)
* returns:
* 1 on a match
* 0 on a mismatch
pat += 2;
if(*pat == '\\' && *pat)
pat++;
- if(*buf >= m && *buf <= *pat)
+ if(cond_lower(*buf) >= cond_lower(m) && cond_lower(*buf) <= cond_lower(*pat))
match = n;
if(!*pat)
pat--;
- } else if(*buf == *pat) match = n;
+ } else if(cond_lower(*buf) == cond_lower(*pat)) match = n;
pat++;
}
buf++;
if(*pat)
pat++;
default:
- match = (*buf++ == *pat);
+ match = (cond_lower(*buf++) == cond_lower(*pat));
break;
}
pat++;
for (idx=size<usize?0:1; size >= (usize*usize); idx++,size/=usize);
if (!idx) return sprintf(buf, " %4d", (int)size);
else return sprintf(buf, ((size/usize) >= 10)? " %3.0f%c" : " %3.1f%c" , (float)size/(float)usize,unit[idx]);
- } else return sprintf(buf, sizeof(off_t) == sizeof(long long)? " %11lld" : " %9ld", size);
+ } else return sprintf(buf, sizeof(off_t) == sizeof(long long)? " %11lld" : " %9ld", (long long int)size);
}
char Ftype(mode_t mode)
else if (m == S_IFSOCK) return '=';
else if (m == S_IFIFO) return '|';
else if (m == S_IFLNK) return '@'; /* Here, but never actually used though. */
-#ifdef S_ISDOOR
+#ifdef S_IFDOOR
else if (m == S_ISDOOR) return '>';
#endif
else if ((m == S_IFREG) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return '*';
#else
if (pflag) n += sprintf(buf+n, " %s", prot(ent->mode));
#endif
- if (uflag) n += sprintf(buf+n, " %-8.8s", uidtoname(ent->uid));
- if (gflag) n += sprintf(buf+n, " %-8.8s", gidtoname(ent->gid));
+ if (uflag) n += sprintf(buf+n, " %-8.32s", uidtoname(ent->uid));
+ if (gflag) n += sprintf(buf+n, " %-8.32s", gidtoname(ent->gid));
if (sflag) n += psize(buf+n,ent->size);
if (Dflag) n += sprintf(buf+n, " %s", do_date(cflag? ent->ctime : ent->mtime));
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
#endif
#include <locale.h>
+#include <langinfo.h>
#include <wchar.h>
#include <wctype.h>
};
/* hash.c */
struct xtable {
- u_short xid;
+ unsigned int xid;
char *name;
struct xtable *nxt;
};
void usage(int);
struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **err);
struct _info **read_dir(char *, int *);
+
int alnumsort(struct _info **, struct _info **);
int versort(struct _info **a, struct _info **b);
int reversealnumsort(struct _info **, struct _info **);
int mtimesort(struct _info **, struct _info **);
int ctimesort(struct _info **, struct _info **);
+int sizecmp(off_t a, off_t b);
+int fsizesort(struct _info **a, struct _info **b);
+
void *xmalloc(size_t), *xrealloc(void *, size_t);
char *gnu_getcwd();
int patmatch(char *, char *);
void xml_indent(int maxlevel);
void xml_fillinfo(struct _info *ent);
+/* json.c */
+off_t json_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev);
+off_t json_rlistdir(char *d, int *dt, int *ft, u_long lev, dev_t dev);
+void jsonr_listdir(struct _info **dir, char *d, int *dt, int *ft, u_long lev);
+void json_indent(int maxlevel);
+void json_fillinfo(struct _info *ent);
+
/* color.c */
void parse_dir_colors();
int color(u_short mode, char *name, bool orphan, bool islink);
void initlinedraw(int);
/* hash.c */
-char *gidtoname(int), *uidtoname(int);
+char *uidtoname(uid_t uid);
+char *gidtoname(gid_t gid);
int findino(ino_t, dev_t);
void saveino(ino_t, dev_t);
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
extern bool Dflag, inodeflag, devflag, Rflag, duflag, pruneflag;
-extern bool noindent, force_color, xdev, nolinks, flimit, nosort;
+extern bool noindent, force_color, xdev, nolinks, flimit;
extern void (*listdir)(char *, int *, int *, u_long, dev_t);
extern int (*cmpfunc)();
return 0;
}
- if (!nosort) qsort(dir,n,sizeof(struct _info *),cmpfunc);
+ if (cmpfunc) qsort(dir,n,sizeof(struct _info *), cmpfunc);
if (lev >= maxdirs-1) {
dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
memset(dirs+(maxdirs-1024), 0, sizeof(int) * 1024);
/* $Copyright: $
- * Copyright (c) 1996 - 2011 by Steve Baker (ice@mama.indstate.edu)
+ * Copyright (c) 1996 - 2014 by Steve Baker (ice@mama.indstate.edu)
* All Rights reserved
*
* This program is free software; you can redistribute it and/or modify
extern bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
extern bool Dflag, inodeflag, devflag, Rflag, cflag;
-extern bool noindent, force_color, xdev, nolinks, flimit, nosort;
+extern bool noindent, force_color, xdev, nolinks, flimit;
extern const int ifmt[];
extern const char fmt[], *ftype[];
int t, n, mt;
if ((Level >= 0) && (lev > Level)) {
- fputc('\n',outfile);
+ if (!noindent) fputc('\n',outfile);
return 0;
}
return 0;
}
if (!n) {
- fputc('\n', outfile);
+ if (!noindent) fputc('\n', outfile);
free_dir(sav);
return 0;
}
if (flimit > 0 && n > flimit) {
- fprintf(outfile,"<error>%d entries exceeds filelimit, not opening dir</error>\n",n);
+ fprintf(outfile,"<error>%d entries exceeds filelimit, not opening dir</error>%s",n,noindent?"":"\n");
free_dir(sav);
return 0;
}
- if (!nosort) qsort(dir,n,sizeof(struct _info *),cmpfunc);
+ if (cmpfunc) qsort(dir,n,sizeof(struct _info *), cmpfunc);
if (lev >= maxdirs-1) {
dirs = xrealloc(dirs,sizeof(int) * (maxdirs += 1024));
memset(dirs+(maxdirs-1024), 0, sizeof(int) * 1024);
}
dirs[lev] = 1;
if (!*(dir+1)) dirs[lev] = 2;
- fprintf(outfile,"\n");
+ if (!noindent) fprintf(outfile,"\n");
path = malloc(pathsize=4096);
nlf = FALSE;
if (!noindent) xml_indent(lev);
}
- fprintf(outfile,"</%s>\n",ftype[t]);
+ fprintf(outfile,"</%s>%s",ftype[t],noindent?"":"\n");
dir++;
}
dirs[lev] = 0;
#endif
if (uflag) fprintf(outfile, " user=\"%s\"", uidtoname(ent->uid));
if (gflag) fprintf(outfile, " group=\"%s\"", gidtoname(ent->gid));
- if (sflag) fprintf(outfile, " size=\"%lld\"", ent->size);
+ if (sflag) fprintf(outfile, " size=\"%lld\"", (long long int)(ent->size));
if (Dflag) fprintf(outfile, " time=\"%s\"", do_date(cflag? ent->ctime : ent->mtime));
}