Imported Upstream version 1.7.0 15/170315/1 upstream upstream/1.7.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 19 Feb 2018 04:53:17 +0000 (13:53 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 19 Feb 2018 04:53:32 +0000 (13:53 +0900)
Change-Id: Ibd838d76a8c0d5bc9bcf3afd1250191207abbdc1
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
12 files changed:
CHANGES
Makefile
README
color.c
doc/tree.1
hash.c
html.c
json.c [new file with mode: 0644]
tree.c
tree.h
unix.c
xml.c

diff --git a/CHANGES b/CHANGES
index 10f2e20..1a6baa9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,30 @@
+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.
@@ -142,7 +169,7 @@ Version 1.3
   - 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.
index 7501eee..e91f1f6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 # $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
@@ -20,19 +20,19 @@ prefix = /usr
 
 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
@@ -56,6 +56,7 @@ LDFLAGS=-s
 #CC=cc
 #CFLAGS=-O2 -Wall -fomit-frame-pointer -no-cpp-precomp
 #LDFLAGS=
+#MANDIR=/usr/share/man/man1
 #OBJS+=strverscmp.o
 
 # Uncomment for HP/UX:
@@ -97,7 +98,7 @@ install: tree
        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)
 
diff --git a/README b/README
index ab58d82..ad67207 100644 (file)
--- a/README
+++ b/README
@@ -141,6 +141,43 @@ Ujjwal Kumar
   - 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.
 
diff --git a/color.c b/color.c
index 6b8254b..1da3363 100644 (file)
--- a/color.c
+++ b/color.c
@@ -1,5 +1,5 @@
 /* $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
index 4b80852..96a6606 100644 (file)
@@ -1,5 +1,5 @@
 .\" $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
@@ -98,6 +99,19 @@ alternate patterns.
 .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
@@ -202,10 +216,6 @@ Prints the device number to which the file or directory belongs
 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
@@ -220,17 +230,26 @@ modification time.
 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
@@ -238,7 +257,7 @@ Turn on ANSI line graphics hack when printing the indentation lines.
 .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
@@ -251,13 +270,17 @@ Turn colorization on always, using built-in color defaults if the LS_COLORS
 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
@@ -287,6 +310,10 @@ Outputs a verbose usage listing.
 .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
@@ -333,7 +360,7 @@ The timefmt expansion buffer is limited to a ridiculously large 255 characters.
 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.
 
diff --git a/hash.c b/hash.c
index 62b6964..f367ee1 100644 (file)
--- a/hash.c
+++ b/hash.c
@@ -1,5 +1,5 @@
 /* $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
@@ -25,11 +25,11 @@ struct xtable *gtable[256], *utable[256];
 #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) {
@@ -41,7 +41,8 @@ char *uidtoname(int uid)
   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;
@@ -51,11 +52,11 @@ char *uidtoname(int 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) {
@@ -67,7 +68,8 @@ char *gidtoname(int gid)
   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;
diff --git a/html.c b/html.c
index 1062faf..75e2ed1 100644 (file)
--- a/html.c
+++ b/html.c
@@ -1,5 +1,5 @@
 /* $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
@@ -20,7 +20,7 @@
 
 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;
 
@@ -38,34 +38,34 @@ void url_encode(FILE *fd, char *s);
 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)
@@ -78,7 +78,10 @@ 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);
@@ -101,7 +104,7 @@ off_t html_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
     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);
diff --git a/json.c b/json.c
new file mode 100644 (file)
index 0000000..4b15e0d
--- /dev/null
+++ b/json.c
@@ -0,0 +1,305 @@
+/* $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));
+}
diff --git a/tree.c b/tree.c
index 19cf368..e7d85d2 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -1,5 +1,5 @@
 /* $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;
 
@@ -45,8 +48,8 @@ int mb_cur_max;
 #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
@@ -56,6 +59,17 @@ const u_short ifmt[]={ FILE_ARCHIVED, FILE_DIRECTORY, FILE_SYSTEM, FILE_HIDDEN,
   #endif
 #endif
 
+struct sorts {
+  char *name;
+  int (*cmpfunc)();
+} sorts[] = {
+  {"name", alnumsort},
+  {"version", versort},
+  {"size", fsizesort},
+  {"mtime", mtimesort},
+  {"ctime", ctimesort},
+  {NULL, NULL}
+};
 
 /* Externs */
 /* hash.c */
@@ -70,17 +84,18 @@ extern const struct linedraw *linedraw;
 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));
@@ -92,7 +107,7 @@ int main(int argc, char **argv)
   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";
   }
 
@@ -107,9 +122,10 @@ int main(int argc, char **argv)
   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':
@@ -154,6 +170,7 @@ int main(int argc, char **argv)
          break;
        case 'i':
          noindent = TRUE;
+         _nl = "";
          break;
        case 'C':
          force_color = TRUE;
@@ -195,18 +212,21 @@ int main(int argc, char **argv)
          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;
@@ -247,7 +267,14 @@ int main(int argc, char **argv)
          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);
@@ -350,6 +377,47 @@ int main(int argc, char **argv)
              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]);
@@ -387,16 +455,22 @@ int main(int argc, char **argv)
   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. */
 
@@ -419,8 +493,9 @@ int main(int argc, char **argv)
   } 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) {
@@ -435,16 +510,16 @@ int main(int argc, char **argv)
        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);
@@ -457,7 +532,8 @@ int main(int argc, char **argv)
          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) {
@@ -465,11 +541,13 @@ int main(int argc, char **argv)
       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)
@@ -477,11 +555,17 @@ int main(int argc, char **argv)
 
   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);
@@ -498,12 +582,14 @@ int main(int argc, char **argv)
     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);
@@ -513,13 +599,16 @@ int main(int argc, char **argv)
 
 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"
@@ -530,6 +619,8 @@ void usage(int 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"
@@ -551,25 +642,28 @@ void usage(int 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);
 }
 
@@ -584,8 +678,8 @@ struct _info **read_dir(char *dir, int *n)
   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);
   }
@@ -689,14 +783,38 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
   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;
@@ -714,7 +832,7 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
     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));
@@ -745,7 +863,9 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
        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--;
@@ -769,57 +889,74 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
 /* 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);
@@ -868,9 +1005,15 @@ char *gnu_getcwd()
     }
 }
 
+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
@@ -918,11 +1061,11 @@ int patmatch(char *buf, char *pat)
          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++;
@@ -940,7 +1083,7 @@ int patmatch(char *buf, char *pat)
       if(*pat)
        pat++;
     default:
-      match = (*buf++ == *pat);
+      match = (cond_lower(*buf++) == cond_lower(*pat));
       break;
     }
     pat++;
@@ -1104,7 +1247,7 @@ int psize(char *buf, off_t size)
     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)
@@ -1114,7 +1257,7 @@ 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 '*';
@@ -1136,8 +1279,8 @@ char *fillinfo(char *buf, struct _info *ent)
   #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));
   
diff --git a/tree.h b/tree.h
index 21e01ad..9be3570 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -1,5 +1,5 @@
 /* $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
@@ -52,6 +52,7 @@
 #endif
 
 #include <locale.h>
+#include <langinfo.h>
 #include <wchar.h>
 #include <wctype.h>
 
@@ -93,7 +94,7 @@ struct _info {
 };
 /* hash.c */
 struct xtable {
-  u_short xid;
+  unsigned int xid;
   char *name;
   struct xtable *nxt;
 };
@@ -121,11 +122,15 @@ struct linedraw {
 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 *);
@@ -161,6 +166,13 @@ void xmlr_listdir(struct _info **dir, char *d, int *dt, int *ft, u_long lev);
 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);
@@ -168,7 +180,8 @@ const char *getcharset(void);
 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);
 
diff --git a/unix.c b/unix.c
index 657269c..ca5f76f 100644 (file)
--- a/unix.c
+++ b/unix.c
@@ -1,5 +1,5 @@
 /* $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
@@ -20,7 +20,7 @@
 
 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)();
@@ -65,7 +65,7 @@ off_t unix_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
     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);
diff --git a/xml.c b/xml.c
index cd8a32f..32f3c0c 100644 (file)
--- a/xml.c
+++ b/xml.c
@@ -1,5 +1,5 @@
 /* $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
@@ -20,7 +20,7 @@
 
 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[];
@@ -66,7 +66,7 @@ off_t xml_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
   int t, n, mt;
 
   if ((Level >= 0) && (lev > Level)) {
-    fputc('\n',outfile);
+    if (!noindent) fputc('\n',outfile);
     return 0;
   }
 
@@ -81,24 +81,24 @@ off_t xml_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
     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);
 
@@ -167,7 +167,7 @@ off_t xml_listdir(char *d, int *dt, int *ft, u_long lev, dev_t dev)
       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;
@@ -298,6 +298,6 @@ void xml_fillinfo(struct _info *ent)
   #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));
 }